microCMS

チュートリアル

このチュートリアルではmicroCMSとiOS SDKを使って、iOSのニュースアプリを作ります。
その過程で、

  • リストの取得と表示
  • 繰り返しコンテンツの取得と表示

について紹介します。

SDKのセットアップがまだ完了してない方は「Getting Started」を参考にセットアップしてください。

Getting Started
https://document.microcms.io/tutorial/ios/ios-getting-started

まず、今回作成する画面はこちらです。


2つの記事がリストに並んでいます。それぞれの項目をタップすると詳細画面に遷移します。


詳細画面で読めるニュースの本文は、「見出し」「テキスト」「画像」から構成されています。

プロジェクトの作成(その1)


  • リスト形式でプロジェクトを作成
  • サンプルのコンテンツを入稿

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 TextContentImageContentの要素が入ります。
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(articleservice.articles[index])) {
                        ArticleListRow(article: service.articles[index])
                    }
                }
            }.navigationTitle("すべての記事")
        }
        .onAppear {
            service.request()
        }
    }
}


以上で詳細画面を作成できました。実際にアプリを実行して確認しましょう。



このような画面が表示されていれば成功です。見出し、本文、画像が繰り返し表示されていますね。
microCMSで入稿したデータを変更し、アプリに反映されることを確認しましょう。