ReactNativeのAndroid側でFlavorで環境を切り分ける

ここでは、

- React Native の Android側で、
- **flavor**と**buildTypes**を組み合わせて
- 以下の6要素を
  1. Application Id
  2. アプリ名
  3. Deep LinkのScheme名またはHost名
  4. FCM用のgoogle-services.json
  5. signingConfigs(keystoreやパスワードなど)
  6.  JS側の諸々環境(APIのBaseUrlなど)
- develop/staging/production(かつそれぞれのdebug/release)毎に切り分けて
- Android Studio上で任意の環境でビルドする方法

について説明します。

なお、6. の `JS側の諸々環境(APIのBaseUrlなど)` では、js側で [react-native-config](https://github.com/luggit/react-native-config)を使うことを前提としています。

また、AndroidStudioとreact-native-configの環境は以下です。

```xml
Android Studio : 3.2.1
react-native-config : ^0.11.5,
```

**追記**

環境ごとのapk出力については[ReactNative(Android)でビルドバリアント毎にapk出力(署名つき)](https://qiita.com/nichi_yo/items/6cac03bbde8e6fb929da)という記事もあるので興味がある方はご参照ください。
リリースビルドの時にbundleが更新されずコードの差分が反映されない事があったので、その対応も書いています。

---------

## Flavorの追加

Android Studioを見てみると、初期段階では**buildTypes**として以下の２つが定義されています。

- debug
- release

####debug
文字通りデバッグ用で、アプリ実行中にHot ReloardやChromeのRemote Debuggerなどでデバッグができる。

####release
こちらも文字通り配布用。
社内テスト、UAT、本番配布などではこちらを使う。

####develop, staging, productionなどで切り替えたい

今回は、

- 社内テスト用かつdebug (develop x debug)
- 社内テスト用かつrelease (develop x release)
- UAT用かつdebug (staging x debug)
- UAT用かつrelease (staging x release)
- 本番用かつdebug (production x debug)
- 本番用かつrelease (production x release)

ということで**6種類**に切り分けます。

ここで使うのが、**productFlavors**(flavor)。
香りづけしましょうってことなのでしょうか。

今回の場合、 **develop, staging, productionというのがそれぞれflavor**です。

####productFlavorsの設定

1. AndroidStudioの上部メニューから**Build/Edit Flavors**を選択。
2. 左下の**+**を選択
3. Nameのところに`develop`を入力
4. 右下の**OK**を押して完了
5. **2.**に戻って、stagingとproductionのflavorも同様に設定

はい、これでとりあえずFlavorの設定が完了しました。

この状態で、Android Studio上部メニューから**Build/Select Build Variant**を選択すると、Build Variantとして

1. developDebug
2. developRelease
3. stagingDebug
4. stagingRelease
5. productionDebug
6. productionRelease

の6種類が選べると思います。

これで、**任意のBuild Variantを選んでビルド**することができます。

ただ、このままではBuild Variantを変更しても、もちろん何も変わりません。

#### 1.Application Idを切り分ける

お気づきかと思いますが、先ほどの `productFlavorsの設定`を進める過程で、Application Idを入力する項目があったと思います。ここに任意のIdを入力すれば、develop/staging/productionごとにApplication Idを切り替えることができます。

Android Manifest内で、`FileProvider`などのようにapplication idを書く場合には、application idを直書きするのではなく、**${applicationId}**を使います。

```xml
android:authorities="${applicationId}.provider"
```

なお、同様にSigningConfigも指定できますが、git管理している場合、本番用のkeystoreのパスワードなどもリポジトリに上がってしまいます。今回は、それを避けるために後ほど紹介する方法で設定します。

####2.アプリ名を切り分ける

**android/app/src**配下に、以下のフォルダを作成します。

- develop
- staging
- production

さらに、それぞれに**res**フォルダと**values**フォルダを親子関係になるように新規作成し、valuesの下に**environment.xml**を作成します。

階層的には以下のようになります。(アルファベット順)

```xml
app
  |_src
      |_develop
          |_res
              |_values
                  |_environment.xml
      |_main
      |_production
          |_res
              |_values
                  |_environment.xml
      |_staging
          |_res
              |_values
                  |_environment.xml
```

そして各environment.xmlは以下のようにします。

```xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">hogeDev</string>
</resources>
```
`hogeDev`のところは、環境毎に`hogeUat`, `hoge`などと任意に入力してください。

最後に、Manifestファイルで、以下のように**label**を指定します。

```xml
 <application
        android:name=".MainApplication"
        android:label="@string/app_name">
```

これで、アプリ名を変更できました。

####3.Deep LinkのScheme名またはHost名を切り替える

先のアプリ名の切り分け方と同じです。

`environment.xml`で以下のように追加し、(nameも任意です)

```xml
<resources>
    <string name="app_name">hoge</string>
    //Android App Linksの例
    <string name="app_links_host">hoge.com</string>
    //Custom Url Schemeの例
    <string name="custom_url_scheme">hoge</string>
</resources>
```
Manifestファイル内で以下のようにApp LinksやCustom Url Schemeを指定します。Deep Linkの設定の仕方自体はここでは説明しません。

```xml
//Custom Url Schemeの例
<data 
android:scheme="@string/custom_url_scheme"
android:host="user" />

//Universal Linksの例
<data
android:host="@string/app_links_host"
android:scheme="https"/>
         
```

####4. FCM用のgoogle-services.json
以下のように、develop/staging/productionの各フォルダ内に各google-services.jsonを設置します。

```xml
app
  |_src
      |_develop
          |_res
          |_google-services.json
      |_main
      |_production
          |_res
          |_google-services.json
      |_staging
          |_res
          |_google-services.json
```

参考： [google-services.json for different productFlavors](https://stackoverflow.com/a/34364376/8800049)

#### 5.signingConfigs(keystoreやパスワードなど)

まず、 **android/keystores**に、各環境で使うkeystoreを配置します。
私の場合は今回はdevelopとstagingは同じものを使い、productionは別のものを使うので、全部で2つです。

```xml
android
    |_app
    |_keystores
        |_develop.keystore
        |_production.keystore
```

次に、**android/app/**に**signingConfigs**フォルダ、そしてその下に**develop.gradle**と**production.gradle**を新規作成します。

```xml
android
    |_app
        |_signingConfigs
            |_develop.gradle
            |_production.gradle
    |_keystores
        |_develop.keystore
        |_production.keystore
```

develop.gradle

```
signingConfigs {
    develop {
        keyAlias 'hoge'
        keyPassword 'hogehoge'
        storeFile file('../../keystores/develop.keystore')
        storePassword 'hogehogehoge'
    }
}
```
production.gradle

```
signingConfigs {
    production {
        keyAlias 'fuga'
        keyPassword 'fugafuga'
        storeFile file('../../keystores/production.keystore')
        storePassword 'fugafugafuga'
    }
}
```

そして、 **android/app/signigConfig/production.gradle をgitignoreに追加**して追跡しないようにします。

次に、**app/build.gradle**に以下のように記述します。

```gradle
android{
...
    apply from: 'signingConfigs/production.gradle', to: android
    apply from: 'signingConfigs/develop.gradle', to: android
    flavorDimensions "default"
    productFlavors {
        production {
            applicationId 'hogeDev'
            signingConfig signingConfigs.production
        }
        staging {
            applicationId 'hogeUat'
            signingConfig signingConfigs.develop
        }
        develop {
            applicationId 'hoge'
            signingConfig signingConfigs.develop
        }
    }

}

```
これで設定完了です。


共同で開発する人には、このproduction.gradleを渡して上記ディレクトリに置いてもらいます。(今回はdevelop用のkeystore情報はprivateリポジトリ内で管理しました)。


この辺りは、 [今更ながらAndroid の keystore と 署名(signingConfigs) の管理・運用について考えてみた](https://qiita.com/taki4227/items/c5685ec722a195764237)を参考させていただきました。

####6.  JS側の諸々環境(APIのBaseUrlなど)
ここまではAndroidプロジェクト内での作業ですが、次は**JS側の環境でも同様にdevelop/staging/productionで切り替える方法**です。

前提として、[react-native-config](https://github.com/luggit/react-native-config)を使います。基本的な設定はREADMEで確認お願いします。

以後は、以下を満たしているものとします。
- [react-native-config](https://github.com/luggit/react-native-config)の設定を一通り終わり、js側に**env**, **env.staging**, **env.production**が設置されている
- **Advanced Android Setup**と**Different environments**に従ってbuild.gradleが設定されている

そして、この**envをflavorで切り替える方法を説明**します。

と言っても簡単で、Different environmentsに従って

```gradle
project.ext.envConfigFiles = [
    debug: ".env.development",
    release: ".env.production",
    anothercustombuild: ".env",
]
```

となっている箇所を、

```gradle
project.ext.envConfigFiles = [
    develop: ".env.development",
    staging: ".env.staging",
    production: ".env.production",
    anothercustombuild: ".env",
]
```
とするだけです！

これで、各Build Variantを指定してビルドした時に参照するenvは以下のようになります。

- developDebug => .env.development
- developRelease => .env.development
- stagingDebug => .env.staging
- stagingRelease => .env.staging
- productionDebug => .env.production
- productionRelease => .env.production

以上で全て設定し終わりました。