🛠

React Nativeアプリの本番・開発環境の設定切り替えをEASで実現する

2022/02/02に公開

こんにちは。
いくつかのReact Nativeアプリを個人開発しているHALと申します。

開発用環境と本番用環境を分けたい

React Native + firebaseで開発しているアプリがいくつかあるのですが、これまでは1環境のみで開発をしてきました。
ただ、1環境しかないと、大幅な変更を加えたい場合に、既存のユーザーへの影響が怖くてなかなか実施できません。
そこで、開発専用の環境を用意して、環境変数的なもので設定を切り替えて使いたいと思いました。

通常のWEBアプリなんかですと、いわゆるdotenvファイルを複数用意して、ビルドオプションなどで切り替えることで、接続先のDBなどを変えることができます。
React Nativeの場合はどうすれば良いのかを調べて試してみたところ、少し戸惑う場面もあったため、記事に残すことにしました。

React Nativeでdotenvは使えるのか?

ライブラリとしては、react-native-dotenvと言うのがあります。

https://github.com/goatandsheep/react-native-dotenv

最初はこれでやろうかと思ったのですが、どうもうまく設定することができませんでした。
何がまずかったのかよくわからない・・・。

https://qiita.com/hotchpotch/items/f5661def49e0f54ec276

この辺とかも参考にしたんですが・・・。

また、雰囲気的に、TypeScriptで型が効かないっぽい。
まぁ、envファイルなのでそりゃそうだよなという感じです。
なので、dotenv的アプローチはやめることにしました。

Babelでビルド時に埋め込むことにする

自分のアプリはExpoのbare workflowなので、Expoのサイトを漁っていたところ、以下のようなページを見つけました。

https://docs.expo.dev/guides/environment-variables/#using-babel-to-replace-variables

Managed Workflowの場合は、「Using app manifest .extra」の通り、Constantsからアクセス可能な値を使ってできるようです。
ただ、自分はbare workflowなので、「Using Babel to "replace" variables」の方を使用する必要があります。
これは要するに、アプリをビルドするときに、環境変数風の変数に直接値を書き込んでしまう、と言う方法のようです。

WEBアプリなんかですと、実行時ではなくビルド時に埋め込んでしまうのは不都合も色々起きそうですが、スマホアプリの場合はそんなに不都合ではありません。
この方法を採用することにしました。
以下、Expoのドキュメントに沿って導入手順です。

babel-plugin-transform-inline-environment-variablesの追加

まずはbabelプラグインをdevインストールします。

npm install babel-plugin-transform-inline-environment-variables --save-dev
# または
yarn add --dev babel-plugin-transform-inline-environment-variables

babel.config.jsの設定

続いてbabel.config.jsに追記します。

babel.config.js
module.exports = function (api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
    plugins: [
      'transform-inline-environment-variables', // ここを追記
      [
        "@babel/plugin-proposal-decorators",
        {
          "legacy": true
        }
      ]
    ]
  };
};

環境変数により設定を切り替えるコードを記述

最後に、実際に環境変数(的な)値を使用するために、設定の切り替え用のコードを書きます。

ENV.ts

// 本番環境用firebase設定
const production = {
    apiKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXX',
    authDomain: 'XXXXXXXXXXXXXXXXXXXXXXXXXXX.firebaseapp.com',
    databaseURL: 'https://XXXXXXXXXXXXXXXXXXXXXXXXXXX.firebaseio.com',
    projectId: 'XXXXXXXXXXXXXXXXXXXXXXXXXXX',
    storageBucket: 'XXXXXXXXXXXXXXXXXXXXXXXXXXX.appspot.com',
    messagingSenderId: 'XXXXXXXXXXXXXXXXXXXXXXXXXXX',
    appId: 'XXXXXXXXXXXXXXXXXXXXXXXXXXX2'
}

// 開発環境用firebase設定
const develop = {
    apiKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXX',
    authDomain: 'XXXXXXXXXXXXXXXXXXXXXXXXXXX.firebaseapp.com',
    databaseURL: 'https://XXXXXXXXXXXXXXXXXXXXXXXXXXX.firebaseio.com',
    projectId: 'XXXXXXXXXXXXXXXXXXXXXXXXXXX',
    storageBucket: 'XXXXXXXXXXXXXXXXXXXXXXXXXXX.appspot.com',
    messagingSenderId: 'XXXXXXXXXXXXXXXXXXXXXXXXXXX',
    appId: 'XXXXXXXXXXXXXXXXXXXXXXXXXXX2'
}

const ENVs = {
    development: {
        // 開発環境の変数
        firebaseInitOption: develop,
    },
    production: {
        // 本番環境の変数
        firebaseInitOption: production,
    },
};

function getEnvVars() {
    const appEnv = process.env.APP_ENV || "development";

    if (appEnv === "production") {
        return ENVs.production;
    } else {
        return ENVs.development;
    }
}

export const ENV = getEnvVars();

※envの切り替えコードは、こちらを参考にさせていただきました。
https://scrapbox.io/ebiken/ReactNative_(Expo)_環境変数

あとは、ビルド時に環境変数としてAPP_ENV=production or developを渡せば、それぞれの環境にあった値が採用されます。
実際に利用する方は、以下のようにします。

example.ts
import { ENV } from '../ENV';

firebase.initializeApp(ENV.firebaseInitOption);

これで、各環境ごとに設定を切り替えることができるようになりました。
また、設定値は実質ただのTSファイルなので、TypeScriptで型定義も効き、「あれ、変数名なんだっけ・・・」なんて悩む必要もありません。
あとは、ビルドを実行する環境で環境変数を設定しておけばOKです。

EASでのビルド時に環境変数を設定する

さて、ローカルでビルドする場合は自分のビルド環境の環境変数を設定すれば良いですが、EASビルドでも対応しておきたいところです。
EASビルドについてはこちらとかを参考にしてみてください。

https://zenn.dev/shwld/articles/9abcd1ce2dd695

https://zenn.dev/hal1986/articles/20211225-eas-build-and-submit

EASの場合はeas.jsonファイルに設定を書けば、環境変数として利用することが可能なようです。

https://docs.expo.dev/build-reference/variables/

eas.json
{
  "cli": {
    "version": ">= 0.42.4"
  },
  "build": {
    "development": {
      "distribution": "internal",
      "releaseChannel": "development",
      "android": {
        "gradleCommand": ":app:assembleDebug"
      },
      "ios": {
        "buildConfiguration": "Debug"
      },
      "env": {
        "APP_ENV": "development"
      }
    },
    "staging": {
      "distribution": "internal",
      "releaseChannel": "staging",
      "android": {
        "gradleCommand": ":app:assembleDebug"
      },
      "ios": {
        "buildConfiguration": "Debug"
      },
      "env": {
        "APP_ENV": "staging"
      }
    },
    "production": {
      "releaseChannel": "production",
      "ios": {
        "buildConfiguration": "Release"
      },
      "android": {},
      "env": {
        "APP_ENV": "production"
      }
    }
  }
}

設定しているのはenvの箇所ですね。
このAPP_ENVの値を変えることで、EAS Buildの際に環境変数として展開してくれます。
あとは、普通にEAS Buildを実行するだけです。
簡単ですね!

これで、開発時の設定と本番の設定を切り替えることができるようになりました。
また、EAS Buildでも設定の切り替えを利用することができるようになりました。
EAS Buildでは実機にインストール可能な内部テストむけアプリのビルド&ダウンロードも可能なので、テスト環境での実機テストもサクッと実施できるようになりますね。

Discussion