Swift / Swift UIWordPress

Swift UI + WP APIでWordPressの記事データを取得・表示させてみる

前回: https://wp-kyoto.net/try-calling-wp-api-from-swift-ui/ WordPressの記事データを取得・一覧表示し、タップすると本文が見れるようにするところまで挑戦しま […]

広告ここから
広告ここまで

前回: https://wp-kyoto.net/try-calling-wp-api-from-swift-ui/

WordPressの記事データを取得・一覧表示し、タップすると本文が見れるようにするところまで挑戦しました。

完成形

 Step1: WordPressの投稿を取得する

HTTPリクエストで記事を取得します。

1-1: データ構造を定義する

データ取得の前にまず、structを定義しましょう。

import Foundation

struct WPPostRendered: Codable {
    var rendered: String
}

struct WPPost: Codable, Identifiable {
    let id: Int
    let title: WPPostRendered
    let content: WPPostRendered
    let date: String
}

全てマッピングする必要はないので、使うものだけ定義しました。

1-2: HTTPリクエスト用のクラスを作る

その後、ObservableObjectを作成し、WP APIへのリクエストとレスポンスの処理を行います。

class WPPosts: ObservableObject {
    @Published var posts: [WPPost] = []
    
    func listPosts() async {
        guard let request_url = URL(
            string: "https://api.example.com/wp-json/wp/v2/posts"
        ) else {
            return
        }
        
        do {
            let (data, _) = try await URLSession.shared.data(from: request_url)
            let items = try JSONDecoder().decode([WPPost].self, from:data)
            DispatchQueue.main.async {
                self.posts.removeAll()
            }

            for item in items {
                let post = WPPost(
                    id: item.id,
                    title: item.title,
                    content: item.content,
                    date: item.date
                )
                DispatchQueue.main.async {
                    self.posts.append(post)
                }
            }
        } catch {
            print("Error \(error)")
            return
        }
        
    }
}

1-3: 日付の変換処理を追加する

このままでは投稿日dateがデフォルトのフォーマットで表示されてしまいます。

一旦Date型に変換してから、フォーマットをかけましょう。

func formatWPDate(date: String, format: String) -> String {
    let formatter: DateFormatter = DateFormatter()
    formatter.calendar = Calendar(identifier: .gregorian)
    formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
    if let _date = formatter.date(from: date) {
        formatter.dateFormat = format
        print(_date)
        print(formatter.string(from: _date))
        return formatter.string(from: _date)
    } else {
        return ""
    }
}

クラスのfor in文で、作成した関数を呼び出します。

                let post = WPPost(
                    id: item.id,
                    title: item.title,
                    content: item.content,
                    date: formatWPDate(date: item.date, format: "yyyy/MM/dd HH:mm")
                )

1-4: 取得処理をViewから呼び出す

呼び出す準備ができたので、onAppearで実行します。

実行結果を受け取るためにも、クラスはStateObjectアノテーションをつけておきましょう。

import SwiftUI

struct HomeView: View {
    @StateObject var wpPosts = WPPosts()
    
    var body: some View {
        NavigationView {
            VStack {
                Text("Hello")
            }.onAppear {
                Task {
                    await wpPosts.listPosts()
                }
            }
        }.navigationTitle("Posts")
    }
}

Step2: 取得したデータを一覧表示する

ここまでで記事データへwpPosts.postsからアクセスできるようになりました。

続いて取得した記事を一覧表示させましょう。

2-1: List用のViewを用意する

記事の中身を表示するViewを作成しましょう。

import SwiftUI

struct ListPostView: View {
    var post: WPPost
    var body: some View {
        VStack (alignment: .leading) {
            Text(post.title.rendered)
                .font(.title2)
            Text(post.date)
                .font(.footnote)
            Spacer()
        }.padding()
    }
}

Previewも用意する場合は、このように書きます。

struct ListPostView_Previews: PreviewProvider {
    static var previews: some View {
        List{
            ListPostView(
                post: WPPost(
                    id: 1,
                    title: WPPostRendered(rendered: "Title"),
                    content: WPPostRendered(rendered: "<h1>content<h1>"),
                    date: "2022/01/01"
                )
            )
        }
    }
}

2-2: ListをViewに追加する

Listの中身を用意できたので、List要素を配置します。

import SwiftUI

struct HomeView: View {
    @StateObject var wpPosts = WPPosts()
    
    var body: some View {
        VStack {
            List(wpPosts.posts) {post in
                ListPostView(post: post)
            }
        }.onAppear {
            Task {
                await wpPosts.listPosts()
            }
        }
    }
}

2-3: 詳細ページの雛形だけ作る

遷移できるようにしたいので、遷移先のViewを用意だけします。

import SwiftUI

struct PostDetailView: View {
    var post: WPPost
    
    var body: some View {
        VStack {
            Text("Detail")
        }
    }
}

引数で欲しいものは確定しているので、それだけ定義しておきましょう。

2-4: ナビゲーションを設定する

遷移先のViewが用意できたので、ListのViewを更新します。

import SwiftUI

struct HomeView: View {
    @StateObject var wpPosts = WPPosts()
    
    var body: some View {
        NavigationView {
            VStack {
                List(wpPosts.posts) {post in
                    NavigationLink {
                        PostDetailView(post: post)
                    } label: {
                        ListPostView(post: post)
                    }
                }
            }.onAppear {
                Task {
                    await wpPosts.listPosts()
                }
            }
        }.navigationTitle("Posts")
    }
}

NavigationViewNavigationLinkを追加しました。

3: 記事本文を表示する

あとは記事詳細ページで本文を表示させるだけです。

3-1: RichTextパッケージを追加する

WP APIのレスポンスには、文字列化されたHTMLが入っています。

HTMLを描画する必要がありますので、ライブラリをいれました。

X Codeで[File > Add Packages...]を開き、https://github.com/NuPlay/RichText.gitを指定します。

追加したら、HTMLを描画する用のViewを追加します。

import SwiftUI
import RichText

struct HTMLStringView: View {
    let htmlContent: String
    var body: some View {
            RichText(html: htmlContent)
                .lineHeight(170)
                .colorScheme(ColorScheme.auto)
                .imageRadius(12)
                .fontType(FontType.system)
                .foregroundColor(light: Color.primary, dark: Color.primary)
                .linkColor(light: Color.blue, dark: Color.blue)
                .colorPreference(forceColor: .onlyLinks)
                .customCSS("")
                .placeholder {
                    Text("loading")
                }
    }
}

3-2: 記事詳細ページのViewを作る

最後にStep 2でファイルだけ作った詳細ページのViewを実装します。

import SwiftUI

struct PostDetailView: View {
    var post: WPPost
    var body: some View {
        ScrollView {
            VStack (alignment: .leading) {
                Text(post.title.rendered)
                    .font(.largeTitle)
                Text(post.date)
                    .font(.footnote)
                    .foregroundColor(.secondary)
                Spacer()
                Divider()
                HTMLStringView(htmlContent: post.content.rendered)
            }.padding()
        }
    }
}

ScrollViewをいれないと、シミュレータではスクロールできませんでした。Web(HTML)に慣れていると忘れがちかもなので、要注意です。

感想

Reactのように要素を関数(ライク?)で並べていくスタイルは、個人的に馴染みやすかったです。

ただ、View側でデータ処理(日付のフォーマットなど)をやろうとしてハマり、データ取得側に移したりと、Reactのノリそのままでやるのは事故りそうな気配があります。

パッケージの追加方法も試せたので、また時間ができればなにか作ってみたいと思います。

参考記事

ブックマークや限定記事(予定)など

WP Kyotoサポーター募集中

WordPressやフロントエンドアプリのホスティング、Algolia・AWSなどのサービス利用料を支援する「WP Kyotoサポーター」を募集しています。
月額または年額の有料プランを契約すると、ブックマーク機能などのサポーター限定機能がご利用いただけます。

14日間のトライアルも用意しておりますので、「このサイトよく見るな」という方はぜひご検討ください。

広告ここから
広告ここまで

Related Category posts