Setting up Multiple Environments on React Native for iOS and Android

Maxime Julian
May 2 · 7 min read
Image for post
Image for post
Photo by Guillaume Bolduc on Unsplash

While working on Peel, we quickly realized we 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 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"
}

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

  • Add developmentrelease build type with the ".development" suffix:
buildTypes {
...
developmentrelease {
initWith debug
applicationIdSuffix ".development"
}
}
  • 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
Image for post
Image for post

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
Image for post
Image for post
  • 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
Image for post
Image for post
  • 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.
Image for post
Image for post
  • 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.
Image for post
Image for post

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.
Image for post
Image for post

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
Image for post
Image for post

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!

P.S.: My best friend and I just released Peel; our own dating app for serious relationships, available on both iOS and Android! It’s built on React Native and it’s awesome; check it out! 😁

The Startup

Medium's largest active publication, followed by +670K people. Follow to join our community.

Sign up for Top Stories

By The Startup

A newsletter that delivers The Startup's most popular stories to your inbox once a month. Take a look

Create a free Medium account to get Top Stories in your inbox.

Thanks to Anthony Riera

Maxime Julian

Written by

Passionate about web and mobile programming 🤓 Currently developing a dating app for serious relationships (peel.dating)

The Startup

Medium's largest active publication, followed by +670K people. Follow to join our community.

Maxime Julian

Written by

Passionate about web and mobile programming 🤓 Currently developing a dating app for serious relationships (peel.dating)

The Startup

Medium's largest active publication, followed by +670K people. Follow to join our community.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store