React Native Multiple Release Variants

By default, React Native gives you a debug variant and a release variant. In debug mode, the app connects to a running packager service, and enables various __DEV__ checks that could slow down performance. In release mode, the JavaScript, CSS and JSX are bundled into the app itself, and optimizations are enabled.

You may have a use case for multiple release variants. For example, you want a staging and production build of you app, and you want to deploy them both to physical devices for testing.

In that case, you would add the following to the buildTypes section of your android/app/build.gradle file:

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

Note that the naming convention for releaseStaging is actually significant. We originally tried just staging, and ended up getting the following error when deployed to a physical device:

java.lang.RuntimeException com.facebook.react.devsupport.JSException
Could not get BatchedBridge, make sure your bundle is packaged correctly

It turned out to be that the build had not bundled assets. You can actually correct that by using the project.ext.react directive in android/app/build.gradle, as noted in the in-line comments of that file. However, though that gave us a working build, performance was suddenly horrible. We eventually figured out that the build was in __DEV__ mode.

Looking at the React Native source, we found this reference in eeact.gradle:

def devEnabled = !targetName.toLowerCase().contains("release")
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
    commandLine("cmd", "/c", *nodeExecutableAndArgs, "node_modules/react-native/local-cli/cli.js", "bundle", "--platform", "android", "--dev", "${devEnabled}",
            "--reset-cache", "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir, *extraPackagerArgs)
} else {
    commandLine(*nodeExecutableAndArgs, "node_modules/react-native/local-cli/cli.js", "bundle", "--platform", "android", "--dev", "${devEnabled}",
            "--reset-cache", "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir, *extraPackagerArgs)
}

In other words, you must have the token "release" in the build variant name for regular release build behavior to apply.



I'm currently working at NerdWallet, a startup in San Francisco trying to bring clarity to all of life's financial decisions. We're hiring like crazy. Hit me up on Twitter, I would love to talk.

Follow @chase_seibert on Twitter