Multi Environments Distribution with React Native using Azure AppCenter Part.1 Configuring react-native-config
Starting next month, we are planning to start implementing our very first mobile app with ReactNative, but none of us had a good experience with Native app distribution on Apple app store or Google play store. So I went ahead and tried some deployment and distribution scenarios. There are some requirements that I wanted to achieve:
- Local debug environment for each stage
- .env configuration should not uploaded on repository
- Auto distribution with git commit(pull request)
- Code push
- Works for both iOS and Android
- App display name should be different for each environment.
There are many great tutorials on what I want to achieve, but I had to open up so many browsers and switching here and there to finally make the goal. So I decided to write this article to summarize what I found and experienced.
This tutorial contains a lot of screenshots, so if you get bored during the tutorial and just want the code, please go ahead and check out this github repository :D
Init react native project
In this article, we will start with typescript template.
npx react-native init rn_multi_env --template react-native-template-typescript
Try run both iOS and Android by:
npm run ios
or
npm run android
Now we are ready to add our packages for multi environments.
Install react-native-config
We are going to use react-native-config package. This package is little bit difficult to configure, but it does what it supposed to do.
Install the package:
npm install react-native-config --save
Link the library for RN < 0.60:
Use below command if your React Native version is below 0.60.
react-native link react-native-config
Once you link the library, below files will be modified:
settings.gradle, MainApplication.java for android.
Podfile for iOS.
Link the library for RN > 0.60:
Follow next guide if your React Native version is above 0.60.
Add below lines to android/settings.gradle
include ':react-native-config'project(':react-native-config').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-config/android')
Add below line to android/app/src/main/java/com/{MyProject}/MainApplication.java
import com.lugg.ReactNativeConfig.ReactNativeConfigPackage;
Add below line to android/app/build.gradle
//link react-native-configimplementation project(':react-native-config')
Now we are going to create files that have our environment variables. development, staging, production.
In .env.development:
stage=development
In .env.staging:
stage=staging
In .env.production
stage=production
Make sure you add these files in .gitignore
We will skip iOS link since this guide is based on RN version > 0.60 and by default all you have to do is
cd ios & pod install
If you need detailed instruction on manual link iOS, lease refer Setup section of react-native-config
Android Configurations
We will use product flavors to distinguish our environments.
Add below lines to android/app/build.gradle
project.ext.envConfigFiles = [ dev: ".env.development", stg: ".env.stating", prd: ".env.production"]apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle"
In order to support different build variants(ex: development, staging, product), add below line as well. Refer to Advanced Android Setup for more detail.
defaultConfig {
...
resValue "string", "build_config_package", "com.rn_multi_env"
}
Note that “com.rn_multi_env” is your package name from AndroidManifest.xml <manifest package=“…”> tag.
Add product flavors:
Using flavorDemensions property, we will create “default” flavor dimension to group “dev”, “stg” and “prd” product flavors.
flavorDimensions "default"productFlavors { dev { dimension "default" applicationIdSuffix ".dev" versionNameSuffix "-dev" } stg { dimension "default" applicationIdSuffix ".stg" versionNameSuffix "-stg" } prd { dimension "default" applicationIdSuffix ".prd" versionNameSuffix "-prd" }}
Create source sets:
When you create a new build variant, you need to add equivalent source sets under {MyProject}/app/src.
You can manually create these source sets or use android studio to create them.
1. Open the Project pane and select the Project view from the drop-down menu at the top of the pane.
2. Navigate to MyProject/app/src/.
3. Right-click the src directory and select New > XML > Values XML File.
4. Enter the name for the XML file or keep the default name.
5. From the drop-down next to Target Source Set.
6. Type strings.xml for Values File Name.
7. Click Finish.
Now we need to add below line to newly created strings.xml inside <resource>…</resource> tag
<string name="app_name">rn_multi_env.dev</string>
Do the same for staging and production:
Add run scripts to package.json:
"android:dev": "react-native run-android --variant=devDebug --appIdSuffix=dev","android:stg": "react-native run-android --variant=stgDebug --appIdSuffix=stg","android": "react-native run-android --variant=prdDebug",
Run the script for dev environment:
npm run android:dev
Run the script for stg environment:
npm run android:stg
Run the script for prd environment:
npm run android
Check out App info to see app name has changed according to our strings.xml:
iOS Configurations
We will start iOS configuration with adding new configurations and schemes to support multi environments.
Add build configurations and schemes:
Start Xcode and open project’s ios workspace({ProjectFolder}/ios).
Select your project in Project navigator, and select your project under PROJECT again.
Search for Configurations and locate+ button. We will create total 6 configurations here.
Because our final goal is to have multiple environments and each stage will have debug and release configuration, we will create both Debug and Release configuration for each stage.
Create Debug and Release configurations for Development:
Create Debug and Release configurations for Staging:
Skip for the Production Configurations as we will use “Debug” and “Release” for production configuration.
So now we have total 6 configurations.
“Development” for development debug
“Release.Development” for development release
“Staging” for staging debug
“Release.Staging” for staging release
“Debug” for production debug(Default)
“Release” for production release(Default)
Add schemes:
Click on current scheme and select Edit Scheme…
Duplicate scheme from “{ProejctName}” and name it {ProjectName}.dev
Expand “Build” settings on left panel and click “Pre-actions”, then select “New Run Script Action” from + sign at the bottom.
Add below line to apply .env.development to build environment.
echo ".env.development" > /tmp/envfile
Change configurations of “Run”, “Test”, and “Analyze” from “Debug” to “Development”
Change configurations of “Profile” and “Archive” from Release to “Release.Development”
Duplicate scheme for Staging and add Pre-actions script as below.
echo ".env.staging" > /tmp/envfile
Change Build Configurations for Staging as well.
No need to change Build Configurations for {ProjectNmae} scheme except Pre-actions script as below.
echo ".env.production" > /tmp/envfile
Click Manage Schemes… button and enable “Shared” check box for all schemes.
Different Product Name and Identifier:
Now we need to give proper Bundle Identifier and Product Name for each configurations.
Select project Target from TARGETS panel and in the Build Settings tab, search for Product Name under Packaging category.
For both Development and Release.Development, change the value as {ProjectName}.dev
Do the same for Staging and Release.Staging {ProjectName}.stg
Note that Product Bundle Identifier also changed according to Product Name.
Change Info.plist for apply Bundle display name:
Ok we are done with xcode now. Let’s go back to vscode and change our podfile.
Open up Podfile under {ProjectFoler}/ios and insert below lines.
platform :ios, '9.0'require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
// add below linesproject 'rn-multi-env','Development' => :debug,'Development.Release' => :release,'Staging' => :debug,'Staging.Release' => :release
Locate all the occurrence of ‘Debug’ under flipper configurations.
Change this to array of debug configuration: [‘Development’, ‘Staging’, ‘Debug’]
Change directory to {MyProject}/ios and install pod:
(cd ios; pod install)
Add below scripts to package.json
"ios:dev": "react-native run-ios --configuration Development --scheme rn_multi_env.dev","ios:stg": "react-native run-ios --configuration Staging --scheme rn_multi_env.stg","ios": "react-native run-ios --configuration Production --scheme rn_multi_env",
Run each script to see the Product name.
Testing environment variable
Now we have to verify if our .env variables are properly applied to each configuration.
Create config.ts file at the project’s root and add below lines.
import env from 'react-native-config';export const {stage} = env;
Import { stage } property from App.tsx.
import {stage} from './config';
Add below line inside<View>…</View> tag.
Run each script and verify if the environment variables are properly applied.
That’s all from configuring multi environments with react-native-config.
I have uploaded this tutorial on my github repository. So feel free to drop by and try it out.
I will comeback with Part.2 Configuring AppCenter next week.
Thanks for reading:D