There are some things that I found trivial and a little difficult to do on React Native that are so easy inside a web application; one of these things is handling different environment variables.
It’s normal to have only one environment when you are still developing the app, but when some other users are starting to test it or when you are going to release it you may want to use different URLs for the APIs, like a develop version of the APIs that has access to also new endpoints or a more catch-all logger, and a production-ready version, well tested and with the production database for the release version.
Sometimes you need more than 2 environments, like:
- Develop: This is where you are going to develop all the new features of your app; the environment is ready to help you with better logs and fake databases that you can delete and recreate whenever you want if you screw a lot;
- Staging:The app version in staging is tested by the client, friends or by some colleagues from Quality Assurance and the environment is an almost-replica of the Production Version;
- Production:this is the version that you will release to the Stores
So how to store (and use) different variables for different environments?
Obviously I tried to Google the answer, but the solutions that I found seemed too difficult or not flexible enough for me.
The most used way to manage different environments on React Native is with react-native-dotenv: at first I though this was a port of node dotenv library that normally I use on my web applications. Unfortunately it seems that handles at most 2 environments (one development and one release/production) and also has some terrible cache of the variables. One time I spent 2 hours Skipe-ing with another dev to understand why the app on his machine was using another API endpoint (solution: after you change a variable inside the .env, you must change/add a line to the file that import that variable or react-native-dotenv will continue to serve the old variable)
An easier solution
Here is the method that I’m currently using; prepare your fingers because you will need to create some new files.
First, we will store all our environments in JSON files in a envs folder.
I’m going to create 3 environments for this article (development, staging and production) and each one will have a different API_URL
envs/development.json
{
"API_URL": "https://development.api.com",
}
envs/staging.json
{
"API_URL": "https://staging.api.com",
}
envs/production.json
{
"API_URL": "https://production.api.com",
}
Still with me? Now we are going to create a node script that will change the environment file every time we want to switch enviroment.
I put it on a scriptsfolder (where I also have other scripts to automate other parts of my building process)
scripts/set-environment.js
#!/bin/node
const fs = require("fs");
//Obtain the environment string passed to the node script
const environment = process.argv[2]
//read the content of the json file
const envFileContent = require(`../envs/${environment}.json`);
//copy the json inside the env.json file
fs.writeFileSync("env.json", JSON.stringify(envFileContent, undefined, 2));
This set-enviroment.js is only 4 lines long: first it store in a variable the first argument when you call the script, then it will requirethe file inside envswith the same name, and finally will create a env.json file on the root folder with the content of envFileContent.
Now we can try it and call
node scripts/set-environment.js development
And ✨MAGIC ✨… a new env.json file is in the right place, ready to serve all your variables.
Also, you can setup some scripts in your package.json file to be able to switch environment without typing all that words in your Terminal: just type yarn env:dev and you are good to go.
{
"name": "your-app-name",
"scripts": {
"start": "react-native start",
"env:staging": "node scripts/set-environment.js staging",
"env:dev": "node scripts/set-environment.js development",
"env:prod": "node scripts/set-environment.js production",
...
},
"dependencies": {...},
...
}
How to use it the new .env file in your code
Now you just need to import from the src/env.json file your variables and you are ready to go! 🚀
How to automate the switch of environment
Now we need a way to automatically switch environment.
This is almost free if we create some new scripts inside package.json:
{
"name": "your-app-name",
"scripts": {
"start": "react-native start",
"env:staging": "node scripts/set-environment.js staging",
"env:dev": "node scripts/set-environment.js development",
"env:prod": "node scripts/set-environment.js production",
"_ios": "react-native run-ios",
"ios": "yarn env:dev && yarn _ios",
"ios:staging": "yarn env:staging && yarn _ios",
"ios:prod": "yarn env:prod && yarn _ios",
"_build:ios": "react-native bundle --platform ios ...",
"build:ios": "yarn env:dev && yarn _build:ios",
"build:ios:staging": "yarn env:staging && yarn _build:ios",
"build:ios:prod": "yarn env:prod && yarn _build:ios",
},
"dependencies": {...},
...
}
I removed some scripts and all the dependencies to maintain the code part light. The Android scripts are the same as the ios counterpart.
So now, every time I want to launch on the Simulator my iOS app with the production environment, I just need to launch
yarn ios:prod
And the environment variables will be loaded on your App.
What if I want to build the iOS release version to submit to the App Store?
yarn build:ios:prod
This is the only command that you will need ✨