React Native Android で development / staging / production を共存させる

Android で development / staging / production それぞれのビルドを同一デバイス上に共存させたい !! というわけで gradle をいじっていく。

development は手元での開発用、 production はユーザーが触るもの、 staging は production と同じだけど検証用途などで限られた範囲に配るもの、という前提ですすめる。

元ネタはこれ。

https://medium.com/@paterw00t_17915/android-react-native-fastlane-working-with-multiple-build-types-a9a6641c5704


buildType を追加する
---

`debug` と `release` という buildType がすでに定義されているので流用する。

環境名 | buildType
---|---
development | debug
staging | staging
production | release

次のように `buildTypes` のところに `debug` と `staging` の定義を追加する。

```groovy:build.gradle
android {

    // snip

    buildTypes {
        debug {
            applicationIdSuffix ".debug"
        }
        staging {
            minifyEnabled enableProguardInReleaseBuilds
            proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
            signingConfig signingConfigs.release
            applicationIdSuffix ".staging"
        }
        release {
            // snip
        }
    }

    // snip
}
```

注意すべきは 2 点。

1. `applicationIdSuffix` を使って applicationId をそれぞれで違うものにしてやる
2. staging は production と同様の設定をひっぱってくる


staging 用設定を追加する
---

`android/app/build.gradle` の `react.gradle` を apply する直前くらいで次を定義する。

```groovy:build.gradle
project.ext.react = [
    bundleInStaging: true,
    devDisabledInStaging: true,
]

apply from: "../../node_modules/react-native/react.gradle"
```

`bundleInStaging` の設定については次に書いてある。

https://github.com/facebook/react-native/blob/master/local-cli/templates/HelloWorld/android/app/build.gradle#L21-L41

実際に指定できるものは次を読めばわかる。

https://github.com/facebook/react-native/blob/master/react.gradle

ここでは staging ビルド時に production と同様、 js を bundle したかったのでその設定をしている。

google-services を使っている場合
---

`package_name` で指定されているものが見つからない、と文句を言ってくるので各 build variant ごとで上書き設定してやる。次のようなディレクトリー構成にする。

```
android/app/
  google-services.json      # デフォルト設定
  src/
    debug/
      google-services.json  # debug 用上書き設定
    staging/
      google-services.json  # staging 用上書き設定
```

この辺は次に書いてある。

https://developer.android.com/studio/build/build-variants.html?hl=ja

で、中身は `package_name` に `applicationIdSuffix` までコミの値を指定してやる。 debug 用の設定の場合、次のようになる。

```json:google-services.json
{
  "client": [
    {
      "client_info": {
        "android_client_info": {
          "package_name": "com.example.your.app.id.debug"
        }
      },
    }
  ]
}
```

react-native-config を使っている場合
---

少しハマったが、公式ページに書いてあった。

https://github.com/luggit/react-native-config#advanced-android-setup

`defaultConfig` に次のような設定を追加して解決。

```groovy:build.gradle
android {
    // snip

    defaultConfig {
        // snip

        resValue "string", "build_config_package", "com.example.your.app.id"
    }

    // snip
}
```

アプリ名を変更したい場合
---

いくつかやり方があるが、 build variant ごとに上書きするのが楽かつ生成物が汚れないかな、と思う。

```
android/app/
  src/
    debug/res/values/
      strings.xml           # debug 用上書き設定
    main/res/values/
      strings.xml           # デフォルト設定
    staging/res/values/
      strings.xml           # staging 用上書き設定
```

debug 用ビルドであれば次のように。

```xml:strings.xml
<resources>
    <string name="app_name">開 MyApp</string>
</resources>
```

アイコンを変更したい場合は同様に各 build variant ごとに `res/mipmap-*/ic_launcher.png` を差し替えてやればいい。

`react-native run-android` で development 版が起動しない !!
---

`react-native run-android` コマンドはそのままだと suffix を考慮してくれない。認識させるためには `--appIdSuffix` オプションを使う。

```sh
react-native run-android --appIdSuffix debug
```

このへんで定義されている。

https://github.com/facebook/react-native/blob/master/local-cli/runAndroid/runAndroid.js#L320

うつの忘れるので npm script として定義しておくと楽。

```json:package.json
{
  "scripts": {
    "android:development": "react-native run-android --appIdSuffix debug",
  }
}
```

staging ビルドする際は
---

次を叩くと `android/app/build/outputs/apk/app-staging.apk` が生成される。

```sh
cd android
./gradlew assembleStaging
```

release ビルドはいままでと同じ `assembleRelease` タスク。