iOSアプリ開発でFirebaseのGoogleService-Info.plistを環境別に自動的に切り替える

Firebaseを使っていると`GoogleService-Info.plist`なるファイルをプロジェクト内に配置するよう指示されますよね。
FirebaseのNotificationやAnalytics、CrashReportなどは開発版とリリースされているものとは混ぜたくないので、環境別に自動的に`GoogleService-Info.plist`を切り替えさせたかったのですが、それがなかなかに面倒だったのでメモっておきます。

# 前提：Configurationsの分類
私が現在作成しているアプリでは1つのプロジェクト内に複数の環境（Configuration）を存在させています。
以下のような感じにConfigurationsを分類し、Bundle IDを環境別に変化させることでそれぞれ別アプリとして認識させています。

| Configuration名 | Bundle ID Suffix | 用途 | 運用方針 |
|:-----------------|:------------------|:------------------|:------------------|
| Staging Debug　| .Staging.Debug | 普段の開発ビルド用 | ステージングサーバへアクセスしつつデバッグ情報などを表示させる |
| Staging Release | .Staging | 社内配布やテスト用 | ステージングサーバへアクセスしつつデバッグ情報などは表示させない |
| Production Debug　| .Debug | 本番環境でデバッグしたい時に | 本番サーバへアクセスしつつデバッグ情報などを表示させる  |
| Production Release　| なし | 本番リリース用 | 本番サーバへアクセスしつつデバッグ情報などは表示させない |

これに合わせてSchemeも`Staging`と`Production`の2つ作成しており、Runした場合はそれぞれの環境のDebug版が実行され、Archiveした場合は必ずRelease版がビルドされるようにしています。
あとはアプリ名、アイコンもそれぞれの環境で変更することで事故が起こらないようにしています。

# 手順

## 1. Firebase上のプロジェクトにアプリをConfiguration分作成する

開発版とリリース版など環境ごとにFirebaseの管理を分けたいのでその分だけFirebaseのプロジェクトにアプリを作成します。今回の場合だと最終的に以下のような感じになると思います

![スクリーンショット 2017-04-20 18.22.02.png](https://qiita-image-store.s3.amazonaws.com/0/129872/cbb7585d-cffb-0ab3-e781-ffb1441b61b8.png)

細かい手順は[公式リファレンス](https://firebase.google.com/docs/ios/setup)を見ていただくのが早いです。

## 2. `GoogleService-Info.plist`を名前を変えて全部プロジェクトに配置する

次は`GoogleService-Info.plist`をダウンロードしてプロジェクトに配置します
細かい手順は[公式リファレ（ｒｙ](https://firebase.google.com/docs/ios/setup)

![スクリーンショット 2017-04-20 18.29.20.png](https://qiita-image-store.s3.amazonaws.com/0/129872/c4ba428a-49dd-9614-f478-a1d9d529b185.png)

ですがどのplistをダウンロードしても同じ名前で落ちてくるためそれぞれ環境別に区別がつくように名前を変更します
Xcodeに追加すると以下のような感じになっていると思います（名前は一例です）

![スクリーンショット 2017-04-20 18.36.52（3）.png](https://qiita-image-store.s3.amazonaws.com/0/129872/4ff65d6e-4057-52f7-cc1b-9370f7a4a30c.png)

ちなみにですが、実はXcodeのプロジェクトに追加しておく必要は無いです。
後述のRunScriptで参照できる場所にあればいいのでXcode上で見えていなくても問題ないのですが、私は気分的に追加しつつipaには含まれないようにtargetからは外しています。

## 3. Run Scriptで無理やり環境別の`GoogleService-Info.plist`を適用する
デフォルトではFirebaseライブラリはルート階層にある`GoogleService-Info.plist`という名前のファイルを参照しに行くため、それぞれのビルド環境ごとのplistファイルをコピーします。

PROJECTファイル -> TARGETS -> Build Phases -> + -> New Run Script Phaseをタップして新しいスクリプトを作成して以下のスクリプトを貼り付けて下さい
※パスやらConfiguration名やらが含まれているため、各々の環境に合わせて適宜読み替えて下さい

```bash:Copy_GoogleService-Info.plist
if [ "${CONFIGURATION}" = "Staging_Debug" ]; then
    cp "${PROJECT_DIR}/${PROJECT_NAME}/Configurations/GoogleService-Info-Staging-Debug.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"
    echo "Staging Debug GoogleService-Info copied."
elif [ "${CONFIGURATION}" = "Staging_Release" ]; then
    cp "${PROJECT_DIR}/${PROJECT_NAME}/Configurations/GoogleService-Info-Staging-Release.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"
    echo "Staging Release GoogleService-Info copied."
elif [ "${CONFIGURATION}" = "Production_Debug" ]; then
    cp "${PROJECT_DIR}/${PROJECT_NAME}/Configurations/GoogleService-Info-Production-Debug.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"
    echo "Production Debug GoogleService-Info copied."
elif [ "${CONFIGURATION}" = "Production_Release" ]; then
    cp "${PROJECT_DIR}/${PROJECT_NAME}/Configurations/GoogleService-Info-Production-Release.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"
    echo "Production Release GoogleService-Info copied."
fi
```

## 4. Crash Reportのシンボルファイル自動アップロードに対応する
※Crash Reportが不要であればこの手順は不要です。
Crash Reportを使っている場合は[公式リファレンスの手順](https://firebase.google.com/docs/crash/ios#upload_symbol_files)にある通り人間が読める状態にするためシンボルファイルのアップロードスクリプトを追加する必要があります。
（手動でシンボルファイルをアップロードしてもいいですがかったるくてやってらんないですｗ）

このスクリプト内にはGoogleAppIdを記述するよう指示があるためここも環境別に分岐出来るようスクリプトを修正します。
GoogleAppIdは`GoogleService-Info.plist`内に記述されているので各環境のplistから引っ張ってきて下さい。

※パスやらConfiguration名やらが含まれているため、各々の環境に合わせて適宜読み替えて下さい（2度目）
特に最後のjsonファイル名は各々異なるようなので変更はお忘れなく。

```bash:Upload_Symbol_File_to_Firebase
# Replace this with the GOOGLE_APP_ID from your GoogleService-Info.plist file
if [ "${CONFIGURATION}" = "Staging_Debug" ]; then
    GOOGLE_APP_ID=1:xxxxxxxxxxxx:ios:xxxxxxxxxxxx
    echo "Configure Staging Debug"
elif [ "${CONFIGURATION}" = "Staging_Release" ]; then
    GOOGLE_APP_ID=1:xxxxxxxxxxxx:ios:xxxxxxxxxxxx
    echo "Configure Staging Release"
elif [ "${CONFIGURATION}" = "Production_Debug" ]; then
    GOOGLE_APP_ID=1:xxxxxxxxxxxx:ios:xxxxxxxxxxxx
    echo "Configure Production Debug"
elif [ "${CONFIGURATION}" = "Production_Release" ]; then
    GOOGLE_APP_ID=1:xxxxxxxxxxxx:ios:xxxxxxxxxxxx
    echo "Configure Production Release"
fi

# Replace the /Path/To/ServiceAccount.json with the path to the key you just downloaded
"${PODS_ROOT}"/FirebaseCrash/upload-sym "${PROJECT_DIR}/${PROJECT_NAME}/Configurations/bigfive-15e55-firebase-crashreporting-8v6br-f8d6143138.json"
```

2つのスクリプトを追加するとBuild Phasesにはこんな感じになっているはずです

![スクリーンショット 2017-04-20 18.36.52（3）.png](https://qiita-image-store.s3.amazonaws.com/0/129872/55308f5b-58bc-5c02-cb54-bb72edf943b6.png)

これで作業は完了です。あとはビルドが通ればおｋです

## ポエム：FIROptions()じゃダメな理由

ここまでやってといてなんですが、実はFirebaseライブラリには使用する`GoogleService-Info.plist`を指定できるオプションがあります。
なので以下のようにコード側で環境別にファイル名を明示することで簡単に切り替えることが出来ます。

```swift:AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    let firbaseOptions = FIROptions(contentsOfFile: Bundle.main.path(forResource: "GoogleService-Info-Staging-Debug", ofType: "plist"))!
    FIRApp.configure(with: firbaseOptions)
    return true
}

```

しかしながら、この方法で問題になるのはCrash Reportのシンボルファイル自動アップロードスクリプトです。
このスクリプトの本体はFirebaseのライブラリに含まれているのですが、このスクリプト内に`GoogleService-Info.plist`という名前がハードコーディングされているため別の名前にしているとうまくアップロード出来ません。
こっちのほうが正統派っぽいやり方なので、最初この方法でやろうとしたら出来なくてストレスで寿命がマッハになりかけたので皆様もお気をつけ下さい。

# ちなみにAndroidでは？
Androidアプリ開発においてはProductFlaverやBuildTypeによって使用するソースフォルダを分けることができるので、上記のiOSのConfigurationと同じようなFlaverを作り、そのフォルダごとに`GoogleService-Info.plist`をそのまま配置すればいいので楽ちんです

# 参考
- [scheme毎にGoogleService-Info.plistを切り替える](http://qiita.com/noboru_i/items/5d23daae651e48fb3feb)