SwiftUI アプリから Firebase エミュレータを使ってみる


これは何?

SwiftUI から Firebase Local Emulator Suite に接続するチュートリアル

目次

  • Firebase Local Emulator Suite とは
  • Emulator の設定
  • Emulator の起動
  • SwiftUI プロジェクトで Emulator に接続する

Firebase Local Emulator Suite とは

公式ドキュメントを見てもらうのが良いですが簡単に説明すると
Firebase Local Emulator Suite は Firestore, Cloud Functions などのエミュレータで構成されていて、本番環境のデータに影響を与えることなくアプリを開発することができるようになるツール。
エミュレータ同士の連携(Firestore の書き込みがあったときに Cloud Functions を起動するなど)もサポートしている。

Emulator の設定

toolsのインストールとプロジェクトの初期化を行う

run npm install -g firebase-tools
firebase init
# Emulators: Set up local emulators for Firebase products を選択する
# Realtime Database, Firestore, Functions も使う場合は一緒に選択しておく

コマンドを実行したディレクトリにFirebaseのプロジェクトが生成される

Emulator の起動

firebase emulators:start

Javaがインストールされていない場合は brew などでインストールすれば解決します。

SwiftUI プロジェクトで Emulator に接続する

以下2手順を行えばEmulatorに接続することができる

  1. Xcode から SwiftUI のプロジェクトを作成し、Firebase console からダウンロードした、GoogleService-Info.plist をプロジェクトに追加する
    ※ これがないとアプリの起動時にクラッシュする

  2. 作成したプロジェクトのAppに以下設定を追加する

import SwiftUI

@main
struct FirebaseEmulatorExampleApp: App {
    @UIApplicationDelegateAdaptor (AppDelegate.self) var appDelegate

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        FirebaseApp.configure()

        Auth.auth().useEmulator(withHost:"localhost", port:9099)

        let settings = Firestore.firestore().settings
        settings.host = "localhost:8080"
        settings.isPersistenceEnabled = false
        settings.isSSLEnabled = false
        Firestore.firestore().settings = settings

        Functions.functions().useFunctionsEmulator(origin: "http://localhost:5001")

        return true
    }
}

作成したプロジェクトでは、いつも通り Firestore の呼び出しをすればエミュレータへのデータの読み書きが行われる。

func read() {
    let db = Firestore.firestore()

    db.collection("contents").addSnapshotListener { (snap, error) in
        if let error = error {
            fatalError(error.localizedDescription)
        }
        if let snap = snap {
            for i in snap.documentChanges {
                if i.type == .added {
                    let id = i.document.documentID
                    let value = i.document.get("value") as! String
                    self.values.append(Content(id: id, value: value))
                }
            }
        }
    }
}

func write(content value: String) {
    let data = ["value": value]
    let db = Firestore.firestore()
    db.collection("contents").addDocument(data: data) { error in
        if let error = error {
            fatalError(error.localizedDescription)
        }
    }
}