【Flutter×Firebase】Firebaseのプロジェクトを本番と開発で分けるまでの道

## やりたいこと

個人開発をしているアプリで、「開発」「本番」でFirebaseのプロジェクトを分け、エミュレータからは「開発」に、実機からは「本番」のDBに接続したい。
元々は、以下のようにFirestore Databaseのコレクション名を、「コレクション名_dev」「コレクション名_prod」で分け、本番用にビルドする時だけ、「コレクション名_prod」になるように書き換えていました。

~~~dart
  ///環境によってコメントアウトを切り替え
  static String devOrProd = 'dev';
  //static String devOrProd = 'prod';

  static String userCollectionName = 'user_' + devOrProd;
~~~
しかし、時折「dev」に戻し忘れたまま開発してしまうこともあり、ストアに公開する準備として、間違えないように、自動で接続先が切り替わるようにしたいと思いました。（今まではストアにはリリースせずに、身内だけで使っていました。）

## 私の場合は、こうしたらできた　(お急ぎの方はこちらをどうぞ)
①以下のリンクに記載いただいている設定を実施（本当にわかりやすくて感謝です。）

https://qiita.com/ampersand-dev/items/39f7f6286058435a6bed

②上記の「flutterのiosの設定(xcode上)」で、「Choose Firebase Environment」として作成したBuild Phaseの「if」以降を以下のように書き換える。
（元のスクリプトでは、「${BUILT_PRODUCTS_DIR}」の下の　"GoogleService-Info-dev.plist"　を書き換えていたのですが、うまくいかなかったので、配置した "GoogleService-Info-dev.plist" と同階層の"GoogleService-Info-dev.plist" を書き換えてみたところ、うまくいきました！！）

~~~
if [ "${CONFIGURATION}" = "Debug" ] ; then
cp "$SRCROOT/Runner/GoogleService-Info-dev.plist" "$SRCROOT/Runner/GoogleService-Info.plist"
echo "Development GoogleService-Info copied."
elif [ "${CONFIGURATION}" = "Release" ] ; then
cp "$SRCROOT/Runner/GoogleService-Info-release.plist" "$SRCROOT/Runner/GoogleService-Info.plist"
echo "Release GoogleService-Info copied."
fi
~~~



## 苦労の道のり
環境まわりの設定に弱く苦労しましたので、上記に至るまでの道のりを記しておきます。

### ・上記手順で環境を分けて、iPhone実機で開いてみたところ、画面が真っ白に・・・

しかし、エミュレータでは起こらないので、ログがわからず、まずFirebaseのCrashlyticsを導入することにしました。

### ・FirebaseCrashlyticsを導入に苦戦

こちらのページを参考にさせていただき、しかしMacの環境変数の仕組みに慣れていなかったりで、苦戦を強いられました・・・。

https://zenn.dev/snova301/books/6df29a230d681f/viewer/213e5d

dSymファイルが自動でアップロードされていなかったので、以下ページを参考にパスを探し、dSYMフォルダをzipにして画面から手動でアップロードを行いました。

https://qiita.com/d2cid-mmori/items/c700a44425b89ace2643

しかし、導入できてしまうと本当に便利なサービスだと思いました。dSymファイルのアップロードだけどうにかうまくやる方法を検討したいです。CI/CDツールでビルド時にアップロードする設定ができればよいのですが、後ほど試してみようと思います。

### ・ログを見られるように
ようやくログを確認することができるようになり、以下のエラーが発生していたことがわかりました。

~~~
Unhandled Exception: [core/duplicate-app] A Firebase App named "[DEFAULT]" already exists
~~~

調べると、以下の２つのファイルで、「Project ID」の不整合がある場合などに発生するとのこと。
・lib/firebase_options.dart
・ios/Runner/GoogleService-Info.plist

最初に実施した手順をよく読み返すと、XCodeに設定したスクリプトの中で、「GoogleService-Info.plist」を一度削除してから、「GoogleService-Info-release.plist」をコピーし、「GoogleService-Info.plist」にリネームしています。
もしかすると、GoogleService-Info.plistがうまく置き換わっていないのではと想像し、一度「GoogleService-Info.plist」を削除した状態でリリースビルドをしてみると、やはりエラーになってしまいました。

### ・ビルドスクリプトを変更してみる

上記「私の場合は、こうしたらできた　(お急ぎの方はこちらをどうぞ)」の②に記載したように、
「\\${BUILT_PRODUCTS_DIR}/\\${PRODUCT_NAME}.app」配下にコピーしていたのを、
「\\$SRCROOT/Runner」配下にコピーするように修正してみました。
（元のGoogleService-Info.plistは、「\\$SRCROOT/Runner」配下にあったため。）

### 開けたーーー！

上記の設定で、再度ビルド、iPhone実機で開いてみると、開けました！！！
よかった！！！
環境周りは実務で触ることもなく、大変苦戦しましたが、なんとか解決して本当によかった。


### ・ついにアプリを開けるようになり、意気揚々とテストアプリを共有したところ、会員登録時にまたエラーが発生

私の場合、FirestoreDBで、indexをつけているCollectionがあったので、prod環境を作成したときにもindexをつける必要がありましたが、失念してしまっていました。prod環境にもindexを追加し、無事解決です。


## 後で少し思ったこと

もしかすると最初のコメントアウトの切り替えを、「kReleaseMode」によって、自動で切り替えるようにするだけではよかったのかもしれないとも思いました。しかし、iPhone, Androidでどの設定ファイルが使われているかや、XCodeのビルドフェーズなどについて理解が深まったので、よかったことにしようと思います・・・。

## 参考
https://qiita.com/ampersand-dev/items/39f7f6286058435a6bed

https://zenn.dev/snova301/books/6df29a230d681f/viewer/213e5d

https://qiita.com/d2cid-mmori/items/c700a44425b89ace2643

https://dev.classmethod.jp/articles/flutter-undefeated-exception-coreduplicate-app-a-firebase-app-named-when-running-an-ios-app-default-what-to-do-when-an-exists-exists-error-occurs/