Setting up Multiple Environments on React Native for iOS and Android

Maxime Julian
The Startup
Published in
7 min readMay 2, 2020

--

Photo by Guillaume Bolduc on Unsplash

Edit 20/01/21: update tutorial for React Native 0.63.4

While working on a side project, I quickly realized I needed to be able to set up multiple targets for the same React Native project. There’s several big advantages to doing that:

  • Store your environment variables in separate files
  • Multiple instances of your app with different environments can be installed on the same phone (with different bundle identifiers)
  • No need to add extra-logic to swap between API urls, constants, keys etc.

There’s some great articles describing how to do that on React Native, but all the ones I could find were written a few years back and things have changed a bit since then. So I’ve decided to give an up-to-date tutorial on the matter.

Before we start

The goal of this tutorial is for you to set up multiple environments on your own React Native app. However, if you want to follow along for the sake of learning I’ve created this repository which you can clone on your machine.

Install react-native-config

We are going to use the package react-native-config to handle our environment variables. In your repo, run:

npm install react-native-config --save

You then need to link the library, even if you are using Cocoapods:

npx react-native link react-native-config

If you’re using Cocoapods, you also need to run a pod install:

cd ios
pod install
cd ../

We will then create the files that will store our environment variables. We’ll set two environments: development and production, so we create two files at the root of our project: .env.development and .env.production.

For the sake of this tutorial, we’ll only store one variable in each file in order to know in what environment our app is running.

In .env.development:

ENVIRONMENT=development

And in .env.production:

ENVIRONMENT=production

In a real app, these files would contain all your API urls, constants, API keys… anything that is specific to each environment.

Setting up build types on Android

We will be using build types with suffixes to create different versions of our app on Android.

  • Open android/app/build.gradle
  • At the very top of the file, we’ll assign an environment file to each build type:
project.ext.envConfigFiles = [
release: ".env.production",
debug: ".env.development",
developmentrelease: ".env.development",
]

The naming of your build types is important! Make sure to use only lowercase letters with no special characters and append “release” at the end of the name of your custom build types, or they might not work.

  • Add this line after apply plugin:
apply from: project(":react-native-config").projectDir.getPath() + "/dotenv.gradle"
  • Since we’ll use suffixes, we need to edit our default config:
defaultConfig {
...
resValue "string", "build_config_package", "com.myapp"
manifestPlaceholders = [label: 'MyApp']
}

Replace com.myapp with the bundle identifier of your app.

Note we also added a manifestPlaceholders property. We will use the label value it contains to change the name of the app depending on the environment it’s running in. More on that later.

  • Add developmentrelease build type with the ".development" suffix:
buildTypes {
...
developmentrelease {
initWith debug
applicationIdSuffix ".development"
manifestPlaceholders = [label: 'MyApp Dev']
}
}
  • Add matching fallbacks to debug build type:
debug {
...
matchingFallbacks = ['debug', 'release']
}

That’s it! Your environment variables are now available to your native code, as well as your Javascript code:

import Config from 'react-native-config';Config.ENVIRONMENT; // "development" or "production"

If you are following this tutorial using the repository I gave earlier, add this line at the top of the App.js file:

import Config from 'react-native-config';

And change line 21 to:

const environment = Config.ENVIRONMENT;

You can now run both versions of the app on your device.
Development:

npx react-native run-android --variant=developmentrelease

Production:

npx react-native run-android --variant=release

Adding a target on iOS

We will be using targets to handle our environments on iOS. The configuration is a bit more daunting than for Android, so hold tight!

  • Open the .xcworkspace file of your app in Xcode, then right click on your project > New Group
  • Rename this group to MyAppDevelopment (replacing “MyApp” with the name of your app)
  • Right click on your main target ( MyApp for me) > Duplicate > Duplicate Only
  • Rename the duplicated target to MyAppDevelopment
  • In the General, set the Bundle Identifier to com.myapp.development. By having a different bundle identifier, we’ll be able to install the two versions of our app (development and production) on the same device.
  • Move the new Info.plist file that got automatically created when you duplicated the target into MyAppDevelopment group, then rename it to Info.plist. Note that you’ll need to actually copy and paste the file into MyAppDevelopment group, if you just slide it from Xcode only the reference will be moved, not the actual file!
  • If your main target contains a .entitlements file (MyApp.entitlements), duplicate it and move it inside MyAppDevelopment group, then rename it to MyAppDevelopment.entitlements. This will permit you to support different capabilities for each target (remote notifications, app groups…).
  • In the Build Settings tab of MyAppDevelopment target, search for the Packaging section then set Info.plist File to the path of the Info.plist we just moved; MyAppDevelopment/Info.plist in our case.

If in the previous step you duplicated a .entitlements file, you’ll also need to search for the section Signing and set Code Signing Entitlements to the path of the .entitlements file (MyAppDevelopment/MyAppDevelopment.entitlements).

  • Click on MyApp in the left corner of the top bar > Manage Schemes and scroll down to find the newly created scheme MyApp copy. Rename it to MyAppDevelopment, then click on Edit in the bottom left corner.
  • Click on Build accordion (without expanding it), then on the + in the bottom left corner. Search for “React” and add the item named React (dah). Make sure to untick Parallelize build.

Our new target should now be able to run, but we need to configure react-native-config.

  • Expand Build accordion, click on Pre-actions then on the + in the bottom left corner > New Run Script Action.
  • Replace the content of the script with:
echo ".env.development" > /tmp/envfile
  • Repeat the same action for the main target, this time replacing the content of the script with:
echo ".env.production" > /tmp/envfile

We’re done playing with Xcode! We’ll now edit the Podfile of the project (ios/Podfile).

  • Since you’re reading this tutorial, chances are your project is articulated around one main target. We’ll first need to set an abstract target, so multiple targets can use the same pods and also use their own pods. In the Podfile, replace the line:
target 'MyApp' do

With:

abstract_target 'App' do

App can be replaced with anything you want, but shouldn’t be the same as any of your targets’ names!

  • We now need to define our targets, that will inherit from our abstract target. Right bellow the last pod ‘SOME_PLUGIN’, :path => ‘../node_modules/SOME_PLUGIN’ (were SOME_PLUGIN will probably be react-native-config since that’s the last plugin we installed), add our two targets:
target 'MyApp' do
#Production-specific pods
target 'MyAppTests' do
inherit! :complete
# Pods for testing
end
end
target 'MyAppDevelopment' do
#Development-specific pods
end

Note: we moved the default test target MyAppTests inside MyApp to respect the original architecture.

If you used the repository I linked at the beginning of this tutorial, your Podfile should look like this:

  • Finally, if you use Cocoapods you’ll need to run pod install again:
cd ios
pod install
cd ../

That’s it! Your environment variables are now available to your native code, as well as your Javascript code.

If you want to access your environment variables from your Info.plist files, follow these extra steps for each scheme.

You can now run both versions of the app on your device.
Development:

npx react-native run-ios --scheme "MyAppDevelopment"

Production:

npx react-native run-ios

I hope this tutorial was helpful to you. If you’d like to see the codebase on Github with all the environments set up, here it is.

Feel free to ask me any questions you have by commenting on this post. Have fun!

--

--