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

## これは何？

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

## 目次

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

## Firebase Local Emulator Suite とは

https://firebase.google.com/docs/emulator-suite?hl=ja

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

## Emulator の設定

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

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

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

![Screen Shot 2021-06-24 at 9.23.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33499/75cba50b-49e8-3672-f2af-f1c0e788e234.png)

## Emulator の起動

```bash
firebase emulators:start
```

![Screen Shot 2021-06-24 at 9.23.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33499/aada9f6a-bd9c-feb4-b3a4-f92a1522844e.png)


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

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

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

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

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

```swift
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 の呼び出しをすればエミュレータへのデータの読み書きが行われる。

```swift
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)
        }
    }
}
```


![Screen Shot 2021-06-24 at 9.07.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33499/397d9b44-afa8-ae26-4de1-9594d1c39e09.png)