チュートリアル
このチュートリアルではmicroCMSとiOS SDKを使って、iOSのニュースアプリを作ります。
その過程で、
- リストの取得と表示
- 繰り返しコンテンツの取得と表示
について紹介します。
SDKのセットアップがまだ完了してない方は「Getting Started」を参考にセットアップしてください。
- Getting Started
- https://document.microcms.io/tutorial/ios/ios-getting-started
まず、今回作成する画面はこちらです。
2つの記事がリストに並んでいます。それぞれの項目をタップすると詳細画面に遷移します。
詳細画面で読めるニュースの本文は、「見出し」「テキスト」「画像」から構成されています。
iOSアプリの作成
リスト画面を作る
まずは、ニュースを表現する Article
クラスを作ります。
struct Article {
let id: String
let title: String
let contents: [ArticleContent]
let imageUrl: URL
let publishedAt: Date
init(id: String, title: String, body: [ArticleContent], imageUrl: URL, publishedAt: Date) {
self.id = id
self.title = title
self.contents = body
self.imageUrl = imageUrl
self.publishedAt = publishedAt
}
init(object: [String: Any]) {
self.id = object["id"] as! String
self.title = object["title"] as! String
let imageObject = object["image"] as! [String: Any]
self.imageUrl = URL(string: imageObject["url"] as! String)!
let formatter = ISO8601DateFormatter()
formatter.formatOptions.insert(.withFractionalSeconds)
let publishedAt = formatter.date(from: object["publishedAt"] as! String)
self.publishedAt = publishedAt!
}
}
ニュース記事が持っているプロパティの定義と、JSONオブジェクトから初期化するイニシャライザとを持っています。
次はニュース一覧の取得です。MicrocmsService.swift
という名前で、以下の内容を記述します。
import Foundation
import MicrocmsSDK
class MicrocsmService: ObservableObject {
@Published var articles: [Article] = []
func request() {
let client = MicrocmsClient(
serviceDomain: "SERVICE_DOMAIN",
apiKey: "YOUR_API_KEY")
client.get(endpoint: "trends") { result in
switch result {
case .success(let object):
if let object = object as? [String: Any],
let contents = object["contents"] as? [[String: Any]] {
self.articles = contents.map { Article(object: $0) }
}
case .failure(let error):
print("[ERROR] \(error)")
}
}
}
}
request()
を実行するとニュースの一覧がmicroCMSから取得され、その内容がArticle
の型で保持されます。
このMicrocmsService
を使って、ニュース一覧画面は以下のように作れます。
import SwiftUI
struct ContentView: View {
@ObservedObject var service = MicrocsmService()
var body: some View {
NavigationView {
List {
ForEach(0..<service.articles.count, id: \.self) { index in
ArticleListRow(article: service.articles[index])
}
}.navigationTitle("すべての記事")
}
.onAppear {
service.request()
}
}
}
ArticleListRow.swift
import SwiftUI
struct ArticleListRow: View {
var article: Article
var dateFormatter: DateFormatter = {
let d = DateFormatter()
d.dateFormat = "yyyy/MM/dd"
return d
}()
var body: some View {
HStack {
NetworkImage(url: article.imageUrl)
.frame(width: 136, height: 96)
.cornerRadius(4)
Spacer(minLength: 8)
VStack(alignment: HorizontalAlignment.leading) {
Text(article.title)
.font(.headline)
.padding(.bottom, 4)
Text(dateFormatter.string(from: article.publishedAt))
.font(.caption)
}
Spacer()
}
.padding(4)
}
}
画面が表示されたタイミングでニュース記事を取得し、その内容をリスト画面に表示しています。
ここまで書けたら一度アプリを実行してみましょう。以下のような画面が表示されるはずです。
プロジェクトの作成(その2)
ニュース一覧画面ができたので、次はニュース詳細画面を作ります。
ニュース詳細画面ではmicroCMSの「繰り返しコンテンツ」を利用します。
- 繰り返しフィールドを用いたコンテンツ入稿
- 設定をexportしてimportするだけで簡単に真似できるようにする
iOSアプリ作成
詳細画面を作る
まずは、先ほど作ったArticle
のモデルを拡張していきます。
詳細画面の繰り返しコンテンツでは、「見出し」「本文」「画像」の要素が繰り返し登場します。
そこで、ArticleContent
というprotocolを用意し、それに準拠する形で要素を定義します。
import Foundation
protocol ArticleContent {}
struct HeadingContent: ArticleContent {
let content: String
}
struct TextContent: ArticleContent {
let content: String
}
struct ImageContent: ArticleContent {
let imageUrl: URL
}
Articleに contents
というプロパティを追加します。この配列にはHeadingContent
TextContent
ImageContent
の要素が入ります。fieldId
の情報をもとに情報をパースします。
struct Article {
let id: String
let title: String
let imageUrl: URL
let publishedAt: Date
let contents: [ArticleContent]
init(object: [String: Any]) {
self.id = object["id"] as! String
self.title = object["title"] as! String
let imageObject = object["image"] as! [String: Any]
self.imageUrl = URL(string: imageObject["url"] as! String)!
let formatter = ISO8601DateFormatter()
formatter.formatOptions.insert(.withFractionalSeconds)
let publishedAt = formatter.date(from: object["publishedAt"] as! String)
self.publishedAt = publishedAt!
// 繰り返しコンテンツをパース
let rawContents = object["body"] as! [Dictionary<String, Any>]
let contents = rawContents.map { content -> ArticleContent in
let fieldId = content["fieldId"] as! String
switch fieldId {
case "text":
return TextContent(content: content["content"] as! String)
case "heading":
return HeadingContent(content: content["content"] as! String)
case "image":
let imageObject = content["image"] as! [String: Any]
let imageUrlString = imageObject["url"] as! String
return ImageContent(imageUrl: URL(string: imageUrlString)!)
default:
return TextContent(content: "")
}
}
self.contents = contents
}
}
ここまででモデルの拡張ができました。
次は詳細画面への遷移の部分を作っていきましょう。
まずは詳細画面の作成です。ArticleDetail
という名前で以下を記述します。
import SwiftUI
struct ArticleDetail: View {
let article: Article
var body: some View {
ScrollView(.vertical) {
VStack(alignment: .leading) {
ForEach(0..<article.contents.count, id: \.self) { num in
let content = article.contents[num]
if let heading = content as? HeadingContent {
Text(heading.content)
.font(.title3)
.bold()
} else if let text = content as? TextContent {
Text(text.content)
.font(.body)
} else if let image = content as? ImageContent {
NetworkImage(url: image.imageUrl)
.frame(height: 200)
.cornerRadius(8)
.padding(.bottom, 20)
}
}
.padding(8)
}
.padding(12)
.navigationBarTitle(article.title, displayMode: .inline)
}
}
}
先ほどパースした contents
の中身を見ながら要素を配置しています。
それでは、リスト画面から詳細画面への遷移を実装しましょう。NavigationLink
を用いて簡単に実装できます。
struct ContentView: View {
@ObservedObject var service = MicrocsmService()
var body: some View {
NavigationView {
List {
ForEach(0..<service.articles.count, id: \.self) { index in
// ここを追加
NavigationLink(destination: ArticleDetail(article: service.articles[index])) {
ArticleListRow(article: service.articles[index])
}
}
}.navigationTitle("すべての記事")
}
.onAppear {
service.request()
}
}
}
以上で詳細画面を作成できました。実際にアプリを実行して確認しましょう。
このような画面が表示されていれば成功です。見出し、本文、画像が繰り返し表示されていますね。
microCMSで入稿したデータを変更し、アプリに反映されることを確認しましょう。