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のノリそのままでやるのは事故りそうな気配があります。

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

    参考記事

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark