In the last five years I have worked for four companies. In three of them I faced issues with managing environment variables for React Native apps.
Here's a brief overview of the projects:
- In the first company I worked on 3 different apps. Each of them had 2 environments:
staging
andproduction
. - In the second one I worked on about 10 branded apps that were based on the same white labeled codebase. All of them had 2 environments:
staging
andproduction
. - In the third one I worked on only one app. But this app had 4 different environments:
staging
,staging-beta
,production
andproduction-candidate
.
In all of these cases I got the following limitations:
- Branded apps should have different bundle ids
- Sometimes branded apps should have different version numbers
- Apps for different environments should have different bundle ids. It's required to be able to install both staging and production apps on the same iPhone device
- Both branded apps and environments require different settings files for services like AppCenter, Branch.io, Sentry, etc...
Developers who worked on the projects before me used one of the classic approaches:
- Using .env files and libraries like
react-native-dotenv
- Using Android flavors and iOS build targets for branded apps and environments
- Having a lot of copies of the same files like
build.gradle
,Info.plist
,sentry.settings
,branch.json
, for different environments
None of these approaches worked well:
- Env files approach doesn't work with bundle ids and version numbers in
build.gradle
for Android andInfo.plist
for iOS. Moreover, it doesn't work with settings files likesentry.settings
orbranch.json
. It can only pass environment variables to JavaScript code - Flavors and build targets approach makes it hard to add new brands and to manage the existing ones. Moreover, it doesn't work well with services settings files. And it doesn't allow to pass variables to JavaScript code
- File copies approach makes codebase look messy. And if you need to add something or to change something in one of the settings files, you need to go through all of the copies.
In all of the projects I solved the problems of managing environments and brands with the following approach:
- I created template files for all the service settings files, for all the JavaScript constants files and for the
build.gradle
andInfo.plist
files. In these template files I put variable keys wrapped by@
signs. E.g.:@VARIABLE_KEY@
- I created a config file describing all the variable values for all the environments and brands.
- I created a config file describing where to copy these template files
- I put all the copy destinations to
.gitignore
to avoid making git changes every time I set another environment. - I wrote a script that takes these configs and templates, fills variables into templates for needed environment and copies these filled templates to their destination paths.
It always worked great!
So I decided to release my tool to open-source. I called it Variabler.
Let's see how easily you can manage environments using it.
Case 1: Two environments
Let's say, we need to have two environments for our app: staging
and production
.
Step 1: We create template files:
api.js
:
const baseURL = '@API_URL@'
export const get = url => fetch('GET', `${baseUrl}/${url}`)
build.gradle
:
...
applicationId "@BUNDLE_ID@"
versionName "@VERSION@"
...
Step 2: We create variables config:
{
"common": {
"VERSION": "1.2.3"
},
"env": {
"staging": {
"API_URL": "https://staging.example.com",
"BUNDLE_ID": "com.example.app.staging"
},
"production": {
"API_URL": "https://production.example.com",
"BUNDLE_ID": "com.example.app"
}
}
}
Step 3: We add template paths config:
[
{ "from": "api.js", "to": "./src/api.js" },
{ "from": "build.gradle", "to": "./android/app/build.gradle" }
]
Step 4: We add file destination paths to .gitignore
:
/android/app/build.gradle
/src/api.js
So that's it!
Now we can easily set environment using the Variabler:
variabler set env:staging
Result: this command execution created two files.
android/app/build.gradle
:
...
applicationId "com.example.app.staging"
versionName "1.2.3"
...
src/api.js
:
const baseURL = 'https://staging.example.com'
export const get = url => fetch('GET', `${baseUrl}/${url}`)
Case 2: Two environments and two brands
Let's say, we need:
- to have two environments for our app:
staging
andproduction
- to build the app for two different brands:
cola
andpepsi
Step 1: We create build.gradle
file template:
...
applicationId "@BUNDLE_ID@@BUNDLE_EXTENSION@"
versionName "@VERSION@"
...
Step 2: We create variables config:
{
"brand": {
"cola": {
"BUNDLE_ID": "com.example.cola"
},
"pepsi": {
"BUNDLE_ID": "com.example.pepsi"
}
},
"common": {
"VERSION": "1.2.3"
},
"env": {
"staging": {
"BUNDLE_EXTENSION": ".staging"
},
"production": {
"BUNDLE_EXTENSION": ""
}
}
}
Step 3: We add template paths config:
[{ "from": "build.gradle", "to": "./android/app/build.gradle" }]
Step 4: We add file destination paths to .gitignore
:
/android/app/build.gradle
That's it.
Now we can set variables:
variabler set brand:pepsi env:staging
Result: we gonna see the following code in android/app/build.gradle
:
...
applicationId "com.example.pepsi.staging"
versionName "1.2.3"
...
Getting started with Variabler
You don't need to make all the described steps manually.
Variabler can do it for you.
To start using Variabler you need to install it and init it in your project repository:
npm i -g variabler
cd ./your-react-native-project
variabler init
Then you can start make files to be managed by Variabler:
variabler add ./android/app/build.gradle
After you add variable keys to templates and variable values to variables.json
you can simply run something like:
variabler set env:staging
That's simple!
For getting better understanding of how to install and start using Variabler visit the GitHub repository.
Not only React Native but React and Node.js
Even if Variabler was created for React Native, indeed there're no reasons why it can't be used for React and Node.js applications or any other type of JavaScript projects. It's absolutely platform independent.
Afterwords
I hope Variabler will serve a good service for some developers.
Feel free to report bugs, create issues on GitHub and send me your pull requests.
Top comments (0)