iOSで開発/本番環境を分けてFirebaseを運用するTips

iOS Advent Calendar 2019の4日目の[@shtnkgm](https://twitter.com/shtnkgm)です！

本記事ではiOSで開発/本番環境を分けてFirebaseを利用するTipsを紹介します。
環境を分けて利用したいユースケースは以下のようなものが挙げられます。

 - **開発/本番環境によって運用者を分けたい**
   例）開発環境と本番環境を利用するチームが異なる

 - **開発環境を本番環境と区別することで本番環境に影響なく開発をしやすくしたい**
   例）テスト時に開発環境へのPush通知を間違って本番環境に送らないようにしたい

 - **本番環境の計測数値に開発時のものが含まれないようにしたい**
   例）AnalyticsやPerformance Monitoring, Crashlyticsの分析精度を高めたい

# 実現方法

## Firebaseで開発/リリース環境を分ける

まずはFirebase側の環境を分ける必要があります。Firebase側の環境を分けるには、以下のいずれかの方法で実現できます。

 - Firebaseプロジェクトを環境毎に複数作成する
 - 同プロジェクト内にアプリを環境毎に複数作成する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/4468eed0-8469-68fb-df86-31f46fb75299.png)

プロジェクト単位で分割した場合はユーザー権限設定も個別に行えますが、アナリティクスは同時にまとめて見られなくなるなどの違いがあるので、運用方法を想定して決めると良いかと思います。

## GoogleService-Info.plistを環境毎に分ける

通常の設定ではセットアップ時にFirebase Consoleから`GoogleService-Info.plist`という構成ファイルをダウンロードし、それがデフォルトで読み込まれます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/01a65ef7-2ec6-4513-59ce-5b21b3467b0c.png" width="400px">

環境毎に構成ファイルは異なるため、構成ファイルを切り替える必要があり、以下の2種類の方法があります。

 - 環境毎のGoogleService-Info.plistを別々のディレクトリに配置し、Target MembershipによってTargetで切り替える
 - 環境毎のGoogleService-Info.plistを別々のファイル名にする

Targetが環境によって別れていれば前者でも問題ありませんが、後者のファイル名を区別する方法が分かりやすいためオススメです。

**別々のファイル名にする例）**

 - GoogleService-Info-dev.plist（開発環境）
 - GoogleService-Info-qa.plist（QA環境）
 - GoogleService-Info-release.plist（本番環境）

**注意**

ファイル名で区別する場合は、デフォルトの`GoogleService-Info.plist`というファイル名は利用しないほうが良さそうです。
公式ドキュメントの[Firebase / アナリティクスのレポートの信頼性を確保する](https://firebase.google.com/docs/projects/multiprojects?hl=ja#reliable-analytics)に**アナリティクスが失われる可能性がある**と記述があります。

## 環境毎に構成ファイルを指定する

環境毎にTargetが分かれている場合は、Build Settingsの`Preprocessor Macros`のマクロで以下のように構成ファイル名を分岐させると良いです。

```swift
let configFileName: String
#if DEBUG
configFileName = "GoogleService-Info-dev"
#elseif QA
configFileName = "GoogleService-Info-qa"
#else
configFileName = "GoogleService-Info-release"
#endif
```

構成ファイルは以下のように`configure(options:)`メソッドで指定できます。

```swift
guard let filePath = Bundle.main.path(forResource: "Firebase/\(configFileName)", ofType: "plist"),
    let options = FirebaseOptions(contentsOfFile: filePath) else {
        fatalError("Firebase plist file is not found.")
}
FirebaseApp.configure(options: options)
```

## Crashlyticsを利用する場合

クラッシュ分析用にFirebase Crashlyticsを利用している場合、Fabricの初期設定とシンボルファイル（dSYM）をアップロードする必要があります。

### Fabricの初期設定

Fabricの初期設定で以下のRun Scriptを実行する際、GoogleService-Info.plistの名前を変更している場合はコンパイル時に`error: Could not get GOOGLE_APP_ID in Google Services file from build environment`というエラーが発生します。

```bash
"${PODS_ROOT}/Fabric/run"
```

これを避けるためには、各構成ファイルをGoogleService-Info.plistにリネームしたものをビルドディレクトリにコピーします。

```bash
# Crashlytics用にGoogleService-Info.plistをビルドディレクトリにコピーする必要があるた
PATH_TO_GOOGLE_PLISTS="${PROJECT_DIR}/Firebase"

case "${CONFIGURATION}" in
"Debug" )
cp -r "$PATH_TO_GOOGLE_PLISTS/GoogleService-Info-dev.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;;

"QA" )
cp -r "$PATH_TO_GOOGLE_PLISTS/GoogleService-Info-qa.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;;

"Release" )
cp -r "$PATH_TO_GOOGLE_PLISTS/GoogleService-Info-release.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;;

*)
;;
esac
```

上記のRun ScriptをFabricのRun Scriptより先に実行するように上に配置します。

### dSYMファイルのアップロード
dSYMファイルのアップロード処理はFastlaneで自動化すると便利です。

Fastlaneでは[upload_symbols_to_crashlytics](https://docs.fastlane.tools/actions/upload_symbols_to_crashlytics/)というアクションが利用できます。
以下のように、アクションのパラメータに構成ファイルのパスやdSYMファイルの保存先を指定します。

```ruby
upload_symbols_to_crashlytics(
  plistName = configuration == "Release" ? "GoogleService-Info-release.plist" : "GoogleService-Info-qa.plist"
  gsp_path: "./YourApp/Path/#{plistName}",
  dsym_path: ENV['DSYM_PATH']
)
```

 - `gsp_path`には環境毎の構成ファイルのパスを指定します。
 - `dsym_path`にはCI環境でビルドした際のdSYMファイルの保存パスを指定します。利用しているCIサービスによっては環境変数として与えられていることもあります。

# おわりに

本記事ではiOSで開発/本番環境を分けてFirebaseを運用するTipsについて紹介しました。

Firebaseの環境を複数に分けると、設定を環境毎に柔軟に行える反面、同じ設定をFirebase Consoleで重複して行う必要があるので、手間を減らすいい方法がないかなと思っています。
本記事と関連する内容で何かアドバイスやご意見があればコメントに記載いただけると嬉しいです。

以上iOS Advent Calendar 2019の4日目の記事でした。

# 参考
 - [Firebase / Firebase を iOS プロジェクトに追加する](https://firebase.google.com/docs/ios/setup?hl=ja)
 - [Firebase / 複数のプロジェクトを構成する](https://firebase.google.com/docs/projects/multiprojects?hl=ja)
 - [Firebase / アナリティクスのレポートの信頼性を確保する](https://firebase.google.com/docs/projects/multiprojects?hl=ja#reliable-analytics)
 - [fastlane docs / upload_symbols_to_crashlytics](https://docs.fastlane.tools/actions/upload_symbols_to_crashlytics/)