microCMSブログ

microCMSの最新情報や活用事例などをお届けします。

チュートリアル

【第1回】SwiftUIとmicroCMSでつくるiOSメディアアプリ - セットアップと一覧画面の表示

【第1回】SwiftUIとmicroCMSでつくるiOSメディアアプリ - セットアップと一覧画面の表示
松田 承一 松田 承一
Image
Image

こんにちは!ウォンタ株式会社の松田です。
今回からSwiftUIとmicroCMSを使ったメディアアプリの開発方法について全5回の記事でご紹介していきます。

microCMSは平たく言うとノーコードで入稿画面とREST APIが即座に作れるサービスです。
こうしたサービスのことを「ヘッドレスCMS」と呼び、ウェブ・ネイティブアプリ・サイネージなど様々なフロントエンドと組み合わせたサービス提供に役立つことなどから、特に海外ではかなりの流行を見せている領域です。

microCMSは数多くのヘッドレスCMSの中でも、日本産であることやGitHubのPull Requestライクなレビュー機能、柔軟な権限管理を備えてることなどが特徴の製品です。

サービスについて詳しくはmicroCMSトップページなどもご覧ください。

全5回の内容

今回の記事より、SwiftUIとmicroCMSを組み合わせたメディアアプリ開発について全部で5つの記事に分けて進めていきます。
全5回の内容はこちらです。

  1. microCMSのセットアップとSwiftUIを使った記事一覧表示 ← 本記事です
  2. 繰り返しフィールドを活用した詳細ページの開発(作成中...)
  3. offset/limitパラメータによる一覧表示のページング(作成中...)
  4. 検索機能の導入(作成中...)
  5. コンテンツ参照を使ったカテゴリ機能の追加(作成中...)


今回作成するアプリと全体構成

今回作成するアプリは記事一覧画面、記事の詳細画面から成るとてもシンプルなメディアアプリです。
シリーズ記事の前半では非常にシンプルに各画面を作成し、後半には検索やページング、カテゴリなど比較的高度な活用方法をご紹介していきます。

Image


図:開発するメディアアプリの画面遷移概要

全体構成としては記事の入稿とデータ提供のためのバックエンドとしてmicroCMSを使います。
microCMSはREST APIで入稿したデータを出力するため、iOSはこの出力を読み取って画面表示を行うだけのとてもシンプルな構成となります。
実際の利用時にはmicroCMSに入稿さえすればアプリにも反映されるのでとても楽に全体管理ができますね!

Image


図:全体構成図

チュートリアル

microCMSの初期設定を行う

それではまずはmicroCMSを使って記事の入稿画面とAPIを用意していきましょう。
microCMSの初期設定については公式ドキュメントのアカウントの作成サービスの作成を参照して作業を進めてください。

この記事ではAPIの作成以降より具体的な手順を記載していきます。
まずは「コンテンツ(API)」となっている箇所よりAPIの作成を開始します。

Image


APIの作成時には名称とAPIエンドポイントを決めていきます。
今回は名称は「記事」、APIエンドポイントは「articles」としました。

次にAPIの型を決めていきます。記事のように複数のデータが存在する場合にはリストを選びます。
例えば「トップページ文言」などデータが一つしかないような場合にはオブジェクトを選択してください。

Image


次にスキーマを設定していきます。
これは作成するAPIがどんなフィールドを持っているかを定義していく画面で、今回の「記事」であれば記事のタイトルや本文がフィールドの実体となります。

今回は簡単のため、APIのエクスポート機能によりエクスポートしたjsonファイルを利用してAPIを作成しましょう。
まずは下記のjsonファイルをダウンロードしてください。

api-articles-20210307004502.json

次にスキーマの入力画面で「インポート」を選択します。
こちらでダウンロードしたjsonファイルを選択してください。

Image


するとスキーマが3つ、設定されたことがわかります。
下記の画像の状態になれば問題はありませんので「作成」を押してAPIの作成を完了させます。

Image


今回はインポートによりAPIを作成しましたが、スキーマはAPIの作成後でも自由に編集が可能です。(API設定→スキーマ)
お試しで好きに変えていただいても結構ですので、気になる方は様々なスキーマを試してみてください。

テスト記事の入稿

上記手順で記事APIが作成されたので、記事を入稿していきましょう。
記事の作成は画面右上のボタンより可能です。

Image


入稿画面では設定したスキーマに沿った画面が自動で出来上がっていますので、直感的にコンテンツ入稿が行えます。
今回は適当に2~3個、記事を入稿しておきましょう。

Image


Image

GET APIによる記事一覧の取得

ここまででmicroCMSの準備はできました。
iOSアプリに組み込んでいく前に、まずはmicroCMSからどのようにデータ取得ができるかを確認しましょう。

先にも述べたようにmicroCMSでは入稿画面と合わせてREST API(デフォルトではGET APIのみ)も自動的に作成されています。
GET APIの呼び出しは管理画面から確認できます。

Image


この部分をクリックするとAPIの動作確認を行うための画面が表示されます。
XXXX.microcms.io サブドメイン部分やAPIキーはご自身のサービスかの内容となっているのでご確認ください。

Image


こちらの画像内の「取得」を押すとブラウザよりGET APIを叩きその結果を確認できます。

Image


この機能を使うことでAPIのレスポンスを簡易的に確認可能です。
特に開発時にはとても便利な機能となっていますのでお役立てください。
APIについて詳しくはドキュメントをご参照ください。

SwiftUIで記事一覧を表示

それではいよいよiOSアプリを開発していきましょう。
今回は以下のように記事の一覧を表示する部分までを作成していきます。

Image

プロジェクトの作成

まずはSwiftUIを使う形でプロジェクトを作成します。
この時点では特に変わった点はありませんので、通常通りAppプロジェクトを作成してください。

Image


Interfaceには「SwiftUI」を選択します。

Image

モデル層の準備をする(データ構造、HTTP通信)

プロジェクトを作成できたらまずは表示データの準備を行うモデル層を作成します。
まずは今回はModel.swiftというファイルを作り、その中に全て実装しています。

まずはデータそのものを表すArticleです。
今回は以下のように定義しました。

struct Article: Identifiable, Equatable {
    var id: String
    var publishedAt: Date
    var title: String
    var imageUrl: URL
    var contents: [Dictionary<String, Any>]

      static func == (lhs: Article, rhs: Article) -> Bool {
        lhs.id == rhs.id
    }
}


続いてmicroCMSへのリクエストを行うクラスです。
MicroCMSRequesterと名付けて以下のように記載します。
今回は外部とのデータの受け渡しにObservableObject@Publishedを採用します。
また簡単のため通信部分はOSSライブラリなどは使わず、URLSessionを使った形で実装を進めます。

通信部分では先ほど管理画面でも確認したように、ヘッダにX-API-KEYを付与することを忘れないようにしましょう。

class MicroCMSRequester: ObservableObject {
    @Published var articles = [Article]()
    private let iso8601DateFormatter = ISO8601DateFormatter()
    init() {
        iso8601DateFormatter.formatOptions.insert(.withFractionalSeconds)
    }
 
    func load() {
        var request = URLRequest(url: URL(string: "https://ios-test.microcms.io/api/v1/articles")!)
        request.setValue("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX", forHTTPHeaderField: "X-MICROCMS-API-KEY")
        URLSession.shared.dataTask(with: request) { (data, response, error) in
            guard let data = data else { return }
            do {
                let object = try JSONSerialization.jsonObject(with: data, options: []) as! Dictionary<String, Any>
                let contents = object["contents"] as! Array<Dictionary<String, Any>>
                let articles = contents.map { (content) -> Article in
                    let id = content["id"] as! String
                    let title = content["title"] as! String
                    let mainVisual = content["main_visual"] as! Dictionary<String, Any>
                    let imageUrl = mainVisual["url"] as! String
                    let contents = content["contents"] as! [Dictionary<String, Any>]
                    let publishedAt = self.iso8601DateFormatter.date(from: content["publishedAt"] as! String)
                    return Article(id: id, publishedAt: publishedAt!, title: title, imageUrl: URL(string: imageUrl)!, contents: contents)
                }
                DispatchQueue.main.async {
                    self.articles = articles
                }
            } catch let e {
                print(e)
            }
        }.resume()
    }
}


画像表示用のViewを作成する

次に画像の取得と表示です。microCMSでは画像はURL形式で取得できるため、表示にあたってはその画像URLに対してHTTPリクエストを送る必要があります。
今回はUrlImage.swiftに以下のように実装しました。

ImageRequesterが担うHTTPリクエスト自体はmicroCMSへのリクエストと非常に近い実装です。
こちらのクラスで取得した画像データを@Published経由でUrlImage側に渡すことで表示が行われます。

//UrlImage.swift

import SwiftUI

struct UrlImage: View {
    @ObservedObject private var requester = ImageRequester()
    
    var url: URL    
    var body: some View {
        Image(uiImage: requester.image)
            .resizable()
            .onAppear {
                requester.load(url: url)
            }
    }
}

private class ImageRequester: ObservableObject {
    @Published var image: UIImage = UIImage()
    
    init() {}
    
    func load(url: URL) {
        URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
            guard let imageData = data,
                  let networkImage = UIImage(data: imageData) else {
                return
            }
            DispatchQueue.main.async {
                self.image = networkImage
            }
        }).resume()
    }
}


表示部分を実装する

データの準備を行うモデル層や画像表示のためのViewができたため次に表示周りを実装していきます。
まずは一覧の中の一行(Row)を表すクラスを作成します。
今回はArticleListRowという名前で以下のように実装しました。

先ほど作成したモデル層の中で、画像の取得を行うクラスを利用しています。
画面表示の際(onAppear)に画像取得リクエストを投げています。

//ArticleListRow.swift

import SwiftUI

struct ArticleListRow: View {
    var article: Article

    var body: some View {
        HStack {
            UrlImage(url: article.imageUrl)
                .frame(width: 50, height: 50)
            VStack(alignment: HorizontalAlignment.leading) {
                Text(article.title)
                Text(article.publishedAt.description)
            }
            Spacer()
        }.onAppear {
            imageRequester.load(url: article.imageUrl)
        }
    }
}

struct ArticleListRow_Previews: PreviewProvider {
    static var previews: some View {
        ArticleListRow(article: Article(id: "hoge", publishedAt: Date(), title: "おいしいリンゴが採れました", imageUrl: URL(string: "https://images.microcms-assets.io/assets/fe5cec22561f46a29228d1d04b1afb4f/22c0d2b6cee8492ba58b76fac93b5cd3/apple.jpg")!, contents: []))
    }
}


なお、PreviewProvider部分で適当なダミーデータを渡しておくことで表示の確認もXCode内で行いつつ作業を進められます。

Image


行部分のViewができたので、次に一覧部分であるArticleListも実装します。
ここまでの準備ができていればとてもシンプルな内容です。

//ArticleList.swift

import SwiftUI

struct ArticleList: View {
    
    @ObservedObject var microCMS = MicroCMSRequester()
    
    var body: some View {
        List(microCMS.articles) {
            ArticleListRow(article: $0)
        }.onAppear {
            microCMS.load()
        }
    }
}

struct ArticleList_Previews: PreviewProvider {
    static var previews: some View {
        ArticleList()
    }
}


これで実行すると以下のような一覧画面が表示されます。
microCMSでコンテンツを増減させて再度アプリを立ち上げると内容が変わることも確認できるでしょう。

Image


さて、第一回の記事はこの辺で終了とさせていただきます。
最初の準備さえ整えばHTTPリクエストで画面に表示するだけなので、仕組みとしては簡単なことがおわかりいただければ嬉しいです。

次回は一覧をタップしたら表示される記事の詳細画面に進んでいきます!

まずは、無料で
試してみましょう。

microCMSは無料ではじめられます。
ご不明な点はお気軽にお問い合わせください。