Building React Native app for multiple environments (2019 update)

After weeks of caffeine-fueled coding, your React Native project is slowly taking shape and is now ready to enter the user testing phase. Suddenly you find yourself in a situation where you need to create different builds for each environment and tester group combination. While manually changing settings and replacing files works, it’s often very time consuming and error prone, one forgotten step can lead to many hours of hair pulling. This is clearly unacceptable and we demand a better method!

Here is where iOS Build Configurations and Android Build Variants comes in. Together they will allow you to simultaneously install multiple versions of your app on the same device, each with their own custom settings, display name and icons. These works especially well if your project uses a git flow like branching model to isolate the environments.

Let’s begin with our iOS version.

1. Adding new configurations

Select your project under PROJECT in XCode and in the Info tab, search for Configurations. In there you should have two default configurations — Debug and Release. Click on the + button and duplicate both, name them Beta.Debug and Beta.Release respectively.

2. Adding new schemes

Click on your current active scheme and from the drop down select Edit Scheme, this will open the scheme management window.

Click on the Duplicate Scheme button at the bottom of the window to make a copy of your current scheme, name it Beta. Don’t close this window as we are not done yet! Notice on the left panel we have a few run/build options? Change the Build Configuration of Run, Test and Analyze to use the Beta.Debug we created earlier. Change Profile and Archive to Beta.Release.

3. Different display names

Select your Target and in the Build Settings tab, search for Product Name. For both Beta.Debug and Beta.Release, change the product name to MyAppName Beta.

In the project’s info.plist, change the value of Bundle display name to $(PRODUCT_NAME)

4. React Native Schemes Manager to the rescue!

Depending on your React Native version(this guide is using v0.58.1), if you try to run the Beta scheme now with cmd+R, it might fail with a Lexical or Preprocessor Issue.

This reason for this error is because the dependency libraries doesn’t have the build configurations we just created. To fix this problem, we will enlist the help of an amazing library called React Native Scheme Manager by Kevin Brown and Thinkmill.

Setup React Native Schemes Manager

  1. Add the library to the project.
yarn add --dev react-native-schemes-manager
  1. Setup package.json according to library documentations. The two main additions is the postinstall script and a new xcodeSchemes section.
{
"name": "BusDue",
"version": "0.0.1",
"scripts": {
"postinstall": "react-native-schemes-manager all"
},
"xcodeSchemes": {
"Debug": [
"Beta.Debug"
],
"Release": [
"Beta.Release"
],
"projectDirectory": "iOS"
},
"projectDirectory": "iOS"
}
}
  1. Clean the project in Xcode with Cmd+Shift+Alt+K.
  2. Manually run the fix scripts once. `yarn run postinstall`
  3. Run the project again and it should now build without any errors.

5. Running multiple configurations side by side

You might have noticed that if you run the original scheme then run the new Beta scheme, the two versions will replace each other. To be able to run multiple configurations side by side, we will need to set a unique bundle ID for each of them.

Select your Target and in the Build Settings tab, click on the + button and select Add User-Defined Settings to create a new user setting, give it the name BUNDLE_ID_SUFFIX. Add the suffix .beta to Beta.Debug and Beta.Release.

In the project’s info.plist, change the value of Bundle Identifier to $(PRODUCT_BUNDLE_IDENTIFIER)$(BUNDLE_ID_SUFFIX)

You should now be able to have both versions installed at the same time.

6. Different icons for different configurations

To be able to quickly distinguish the different versions at a glance, you might want to use separate icons for each configuration. You can generate your icons for free at https://makeappicon.com.

In Xcode create a new Icon Set, name it Appicon.beta

Copy and paste all the Beta version icons assets to ios/<project_name>/Images.xcassets/AppIcon.beta.appiconset

Select your Target and in the Build Settings tab, locate the Asset Catalog App Icon Set Name setting. Change the values of Beta.Debug and Beta.Release to AppIcon$(BUNDLE_ID_SUFFIX)

Run the app again and each version should now be using it’s own icons

7. Run any configuration via command line

You can run the different configurations via command line by adding the following scripts to package.json.

"runSim":"react-native run-ios --simulator\"iPhone8\" --scheme Beta --configuration Beta.Debug",
"runDevice":"react-native run-ios --device\"MyIPhoneName\" --scheme Beta --configuration Beta.Debug",

Then in the command line

yarn run runSim

And this concludes the iOS part of our tour. Next stop Android!

Android

The Android equivalent of iOS’s Build Configuration is called Build Variant. A Build Variant is the combination of a Build Type and a Product Flavor. By default our React Native project already has a Debug and a Release Build Type so we don’t need to do anything here. We will be creating two new Product Flavors — dev and beta. (note: once you add Product Flavors to your app, you will no longer be able to build a flavorless version)

1. Adding Product Flavors

Open your project’s android/app/build.gradle file, add a new section called productFlavors and put our flavor specific configuration here. Use a different applicationId for each flavor so we can have them all installed on the device at the same time. And don’t forget the add the flavorDimensions “default” line or the build will error.

android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion

flavorDimensions "default" // <-- !!! add this line !!!

defaultConfig {...}
splits {...}
buildTypes {...}
productFlavors {
dev {
minSdkVersion rootProject.ext.minSdkVersion
applicationId 'com.busdue.dev'
targetSdkVersion rootProject.ext.targetSdkVersion
resValue "string", "build_config_package", "com.myapp"
}
beta {
minSdkVersion rootProject.ext.minSdkVersion
applicationId 'com.busdue.beta'
targetSdkVersion rootProject.ext.targetSdkVersion
resValue "string", "build_config_package", "com.myapp"
}
}
applicationVariants.all {...}
}

2. Custom icons for each build variant

Go to your app’s android/app/src folder and in there you should already have a folder called main. Let’s create two more folders here, one for each of our product flavors.

Place any flavor specific icons inside these folders. Take a look inside the main folder if you need a reminder of the folder path for each type of app resources. The way this work is that when you build your app, the build process will first try to find the resources inside a folder that matches the name of the flavor. If it can’t find one, it will fall back to main.

3. Different app names for each build variant

Let’s say we want to change the display name of our beta build to BusDue Beta. We can achieve this by adding a strings.xml to android/app/src/beta/res/values

<resources>
<string name="app_name">BusDue Beta</string>
</resources>

Please note that the contents of strings.xml will be merged with the strings.xml inside the main folder instead of replacing it.

4. Running and building the Build Variants

We should now be able to run our Android project using a specific flavor by passing in a variant argument

— variant=<productFlavour><BuildType>

So to run the our dev version in debug mode, we will use

react-native run-android --variant=devDebug --appIdSuffix=dev

And to build the release version, we use the command

assemble<ProductFlavour><BuildType>

Example for building a beta release

cd android && ./gradlew assembleBetaRelease

(special thanks to MovieMaker for the “appIdSuffix=dev” method)

That’s all folks!

I hope you find this guide useful. If you have any questions or suggestions please leave a comment below. Thank you very much for reading and happy coding!

*If you want to use .env files for things like API key and URLs, check out a library called react-native-config.

*It’s a long shot but if you happen to use buses in London, please checkout my BusDue app for live bus arrival time, it’s made with React Native and available on iOS and Android. Cheers!

*Special thanks to Circle Engineering for their awesome iOS tutorial, a large portion of the iOS steps in this guide is based on what I learned from them.