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")
}
}
NavigationView
とNavigationLink
を追加しました。
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のノリそのままでやるのは事故りそうな気配があります。
パッケージの追加方法も試せたので、また時間ができればなにか作ってみたいと思います。