Firebase (FS, CF) + Expo で ステージング環境をどう作るか

現在 ReactNative with Expo と Firebase (CloudFunction, FireStore) を利用して IOS アプリを作成しています。 今まで単一環境(本番環境)で行ってきたのですが、プロダクトβリリースを間近に備えているため、テストのため環境を作る必要が出てきました。 よくある組み合わせである　FireBase + Expoでのテスト環境の作り方でまとまった記事がなかったので、当記事ではそれらを紹介したいと思います。今回は、今まで使っていたものを本番環境とし、新たにステージング環境を作るという目的で紹介します。

## 前提
- Expoを使ってネイティブアプリの開発をしている、 `expo start` や `expo publish` などのコマンドを理解している
- Firebaseを使っている 

## 用意する物
1. ２つのFirebaseプロジェクト

## 概要図
<img width="565" alt="スクリーンショット 2563-05-19 15.29.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275270/80f33523-c6f2-118f-6987-bd55f7f2ccfc.png">
※ FireStoreルール や CloudFunctionについては上記概念図には入っていないため以下で説明する

## 流れ
1.　(省略可能)ステージング用Firebaseと本番用Firebaseを整える
これはFireStoreやCloudStorageのデータを初期段階で両方統一させたい人だけやってください。[プロジェクト間でデータを移動する方法]('https://firebase.google.com/docs/firestore/manage-data/move-data?hl=ja')

2.　ステージングFirebaseプロジェクトの環境変数(APIキーなど)を本番環境での管理と同じように記述する(恐らく、 `.env` に書いている人が多いと思います)

3.　(👉今回のキモ)環境変数を読み取る部分(firebase.initializeApp関数部分)でプログラムの実行のされ方を基に場合分けをする
Expoには[リリースチャンネル]('https://docs.expo.io/distribution/release-channels/') と呼ばれる仕組みがあります。これを利用して `expo start` の時はステージング環境を利用、 `expo build:ios --release-channel prod-v1` の時は本番環境を利用という場合わけを行います。以下が firebase の initialize をしている箇所のコードです。

```firebase/config.ts
import Constants from 'expo-constants';
import {
  API_KEY,
  AUTH_DOMAIN,
  DATABASE_URL,
  PROJECT_ID,
  STORAGE_BUCKET,
  MESSAGE_SENDER_ID,
  MEASUREMENT_ID,
  APP_ID,
  DEV_API_KEY,
  DEV_AUTH_DOMAIN,
  DEV_DATABASE_URL,
  DEV_PROJECT_ID,
  DEV_STORAGE_BUCKET,
  DEV_MESSAGE_SENDER_ID,
  DEV_MEASUREMENT_ID,
  DEV_APP_ID,
} from 'react-native-dotenv';
import 'firebase/firestore';

const firebaseConfig = {
  apiKey: API_KEY,
  authDomain: AUTH_DOMAIN,
  databaseURL: DATABASE_URL,
  projectId: PROJECT_ID,
  storageBucket: STORAGE_BUCKET,
  messagingSenderId: MESSAGE_SENDER_ID,
  MEASUREMENT_ID: MEASUREMENT_ID,
  appId: APP_ID,
} as const;

const devFirebaseConfig = {
  apiKey: DEV_API_KEY,
  authDomain: DEV_AUTH_DOMAIN,
  databaseURL: DEV_DATABASE_URL,
  projectId: DEV_PROJECT_ID,
  storageBucket: DEV_STORAGE_BUCKET,
  messagingSenderId: DEV_MESSAGE_SENDER_ID,
  MEASUREMENT_ID: DEV_MEASUREMENT_ID,
  appId: DEV_APP_ID,
} as const;

// リリースチャンネルの名前をみて、本番環境かどうかをチェック
function isEnvProduction(env: string) {
  if (env == null || env === '') return false;

  return env.indexOf('production') !== -1 ? true : false;
}

const config = isEnvProduction(Constants.manifest.releaseChannel) ? firebaseConfig : devFirebaseConfig;

export const Firebase = firebase.initializeApp(config);
```

注目する点が `impo rt Constants from 'expo-constants';` の部分です。このexpoが提供する `expo-constants`によってアプリの情報がみれます。リリースチャンネルを明記してbuildした場合には `Constants.manifest.releaseChannel` にてそのリリースチャンネルが出力されます。今回の場合、
`expo build:ios --release-channel prod-v1` を実行したため、出力として`prod-v1`が得られるはずです。反対に `expo start` のような開発モード実行ではリリースチャンネルは存在しません。[expo 開発モードでのリリースチャンネル]('https://docs.expo.io/distribution/release-channels/#access-channel-from-code')

4.　最後にFireStoreルール や CloudFunctionについてはそれぞれのプロジェクトごとにデプロイをする必要があります。
CLIにて、`firebase list` によってログイン中のアカウントで利用できるプロジェクト一覧を取得し、`firebase use <Project-Id>` をするとデプロイ先のプロジェクトを切り替えることができます。その後、`firebase deploy --only functions` 等でデプロイをしてください

いかがでしたでしょうか。基本的にexpoのドキュメントに書いてあることでしたが、これが誰かの役に立てば幸いです。