240

I am using react-native to build a cross-platform app, but I do not know how to set the environment variable so that I can have different constants for different environments.

Example:

development: 
  BASE_URL: '',
  API_KEY: '',
staging: 
  BASE_URL: '',
  API_KEY: '',
production:
  BASE_URL: '',
  API_KEY: '',
Penny Liu
  • 15,447
  • 5
  • 79
  • 98
Damon Yuan
  • 3,683
  • 4
  • 23
  • 31

24 Answers24

191

Instead of hard-coding your app constants and doing a switch on the environment (I'll explain how to do that in a moment), I suggest using the twelve factor suggestion of having your build process define your BASE_URL and your API_KEY.

To answer how to expose your environment to react-native, I suggest using Babel's babel-plugin-transform-inline-environment-variables.

To get this working you need to download the plugin and then you will need to setup a .babelrc and it should look something like this:

{
  "presets": ["react-native"],
  "plugins": [
    "transform-inline-environment-variables"
  ]
}

And so if you transpile your react-native code by running API_KEY=my-app-id react-native bundle (or start, run-ios, or run-android) then all you have to do is have your code look like this:

const apiKey = process.env['API_KEY'];

And then Babel will replace that with:

const apiKey = 'my-app-id';
Nimantha
  • 6,405
  • 6
  • 28
  • 69
chapinkapa
  • 2,623
  • 3
  • 14
  • 22
  • 1
    I just used the transform-inline-environment-variables plugin on its own; they seem to have fixed that React Native bug. – Scimonster Oct 15 '16 at 21:40
  • I went ahead and updated my answer. Thanks @Scimonster! – chapinkapa Oct 16 '16 at 00:39
  • This doesn't seem to be working in 0.36. The vars just never get read. – duhseekoh Nov 16 '16 at 17:02
  • I am not current on 0.36. I'll try to update the response once we upgrade. If anyone else has tried upgrading, I'll take a suggestion or up vote another answer. – chapinkapa Dec 19 '16 at 21:02
  • Im trying to get it to work with 0.38, but im struggeling to get the vars read. Trying to figure out where and how the babelrc is read to dig a bit into the problem, but I'm struggeling. – vonGohren Jan 18 '17 at 13:52
  • 14
    Sounds like great solution, but not works for me at RN@0.37.0. The only property on `process.env` is `NODE_ENV`. – Navidot Feb 25 '17 at 21:48
  • I will need to be upgrading here soon, I'll try out a new solution here soon. Thanks @Sanczol – chapinkapa Feb 27 '17 at 05:45
  • 1
    Does not seem to work anymore. For me the variables are undefined. Do I need to clear babel cache somehow or do something else to get this working? – Ville Miekk-oja Sep 12 '17 at 18:43
  • 10
    See the answer below by Jack Zheng... you cannot access the variable via `process.env.API_KEY`... use `process.env['API_KEY']` instead – Steven Yap Oct 02 '17 at 03:48
  • 1
    This seems like a great solution when debugging, will it work when building releases? You basically run gradle when releasing the app, so seems like you are out of luck with this approach. Or am I missing something? – Dejan Maksimovic Oct 11 '17 at 18:18
  • 1
    @DejanMaksimovic it has worked for me on release. When you package a release react-native's packager converts your JS, and so whenever you package your code, you need to have your ENV variables in the same process – chapinkapa Oct 12 '17 at 16:06
  • Thanks for your reply. My problem was indeed elsewhere. I think it was the fact that I didn't have react-native preset installed, so transform didn't get picked up at all. – Dejan Maksimovic Oct 15 '17 at 10:34
  • 11
    I am getting the process.env['API_KEY'] as undefined. Can anyone help me set this up – user1106888 Mar 08 '18 at 01:50
  • 5
    I had the same problem: undefined – Guto Marrara Marzagao May 04 '18 at 14:27
  • 3
    Does not work for me either. Damn shame as this is exactly what I was looking for. (55.1) – James Trickey Jun 28 '18 at 17:18
  • 17
    Works for me in v0.56. You have to clear bundler's cache by running `react-native start --reset-cache` every time you change the environment variables. – soheilpro Aug 29 '18 at 13:21
  • Is there any RN configuration library that supports defaults? Like Mozilla's Convict? https://www.npmjs.com/package/convict – Marecky Oct 08 '18 at 22:12
  • 3
    how can I use this when trying to release with gradle assembleRelease? – Raul H Sep 24 '19 at 18:02
  • @RaulH hi were you able to use it for release build – Jose Kj Jul 13 '20 at 15:29
  • I had the same problem. – Biplov Kumar Oct 10 '20 at 19:26
  • @StevenYap I know this is pretty old, but I just wanted to point out that `process.env.API_KEY` and `process.env['API_KEY']` are equivalent in javascript – Phillip Schmidt Feb 26 '22 at 17:06
95

In my opinion the best option is to use react-native-config. It supports 12 factor.

I found this package extremely useful. You can set multiple environments, e.g. development, staging, production.

In case of Android, variables are available also in Java classes, gradle, AndroidManifest.xml etc. In case of iOS, variables are available also in Obj-C classes, Info.plist.

You just create files like

  • .env.development
  • .env.staging
  • .env.production

You fill these files with key, values like

API_URL=https://myapi.com
GOOGLE_MAPS_API_KEY=abcdefgh

and then just use it:

import Config from 'react-native-config'

Config.API_URL  // 'https://myapi.com'
Config.GOOGLE_MAPS_API_KEY  // 'abcdefgh'

If you want to use different environments, you basically set ENVFILE variable like this:

ENVFILE=.env.staging react-native run-android

or for assembling app for production (android in my case):

cd android && ENVFILE=.env.production ./gradlew assembleRelease
Patrik Prevuznak
  • 2,181
  • 15
  • 14
  • 20
    It may be worth noting that in the README it states _Keep in mind this module doesn't obfuscate or encrypt secrets for packaging, so do not store sensitive keys in .env. It's basically impossible to prevent users from reverse engineering mobile app secrets, so design your app (and APIs) with that in mind_ – Marklar Apr 12 '17 at 04:28
  • Thing is it will not work with some frameworks like twitter which requires to have they key set as com.twitter.sdk.android.CONSUMER_KEY in your .env – thibaut noah Oct 06 '17 at 14:35
  • If you mean putting the key inside the Manifest, the extension supports it. It is just not described in this answer. You can use the variables in XML, Java and JS files. – sebastianf182 Oct 10 '17 at 19:27
  • 5
    react-native-config does not work with RN 0.56, it has unresolved issues and it is unmaintained for over 6 months. The issue witch kills its usage in RN is https://github.com/luggit/react-native-config/issues/267, here is some hacking to make it work https://github.com/luggit/react-native-config/issues/285 – Marecky Oct 09 '18 at 21:12
  • 1
    How does the xcode knows when you want to rollout a stage / prod version? – Ravi Maniyar Jun 03 '21 at 10:09
  • Wish I could upvote 100 times for this answer :) – Gokul Kulkarni Jun 18 '21 at 13:42
  • When you mention support `12 factor`, do you mean because it allows different `.env` configurations such as `.env.development`, `.env.staging`, etc.? Thank you! – Jarrett Oct 06 '21 at 11:39
  • How to build iOS production? I use Xcode to build (archive), not use build command like Android. – Robin Huy Apr 18 '22 at 04:49
80

The simplest (not the best or ideal) solution I found was to use react-native-dotenv. You simply add the "react-native-dotenv" preset to your .babelrc file at the project root like so:

{
  "presets": ["react-native", "react-native-dotenv"]
}

Create a .env file and add properties:

echo "SOMETHING=anything" > .env

Then in your project (JS):

import { SOMETHING } from 'react-native-dotenv'
console.log(SOMETHING) // "anything"
swrobel
  • 4,053
  • 2
  • 33
  • 42
Slavo Vojacek
  • 1,044
  • 8
  • 6
  • 4
    @Slavo Vojacek How do I use this to configure for example one `base_url` for both `staging` and `production`? – Compaq LE2202x Feb 21 '18 at 08:48
  • @CompaqLE2202x I am not quite sure I understand? Are you asking about using different `.env` files (per environment), or about _reusing_ some of your values in different `.env` files, so you don't duplicate them across, say, Staging and Production? – Slavo Vojacek Mar 17 '18 at 20:47
  • 8
    @SlavoVojacek I'm asking about different `.env` files per environment let's say `staging` and `production`. – Compaq LE2202x Mar 19 '18 at 06:52
  • @SlavoVojacek couldn't you overwrite values in a CI stage or at deployment? – mgamsjager Dec 04 '18 at 08:40
  • @CompaqLE2202x, this solution won't allow you to define custom environments, out of the box. But it does have two environments: [development and production](https://github.com/zetachang/react-native-dotenv#can-i-use-different-env-settings-for-production-) – Bogdan D Mar 25 '19 at 12:47
  • 2
    Please update ypur answer due to the latest changes in the package: "Rename the import from 'react-native-dotenv' to '@env'." Otherwise, it will raise an error "module fs is not found". See [this issue](https://github.com/goatandsheep/react-native-dotenv/issues/20) and the [migration guide](https://github.com/goatandsheep/react-native-dotenv/wiki/Migration-Guide). – Yao Feb 25 '21 at 00:49
  • Yes, i tried to this. Then i decided to abanton it because it doesn't work. I call everybody who makes such blugins and others to make possible simple solutions for such things. I use simpliest solution e.g in root directory conf.js: with content e.g "const Conf = {VAR1: 'value1', VAR2: 'value2'}"; export default Conf; . – user2301515 Jan 03 '22 at 09:57
27

React native does not have the concept of global variables. It enforces modular scope strictly, in order to promote component modularity and reusability.

Sometimes, though, you need components to be aware of their environment. In this case it's very simple to define an Environment module which components can then call to get environment variables, for example:

environment.js

var _Environments = {
    production:  {BASE_URL: '', API_KEY: ''},
    staging:     {BASE_URL: '', API_KEY: ''},
    development: {BASE_URL: '', API_KEY: ''},
}

function getEnvironment() {
    // Insert logic here to get the current platform (e.g. staging, production, etc)
    var platform = getPlatform()

    // ...now return the correct environment
    return _Environments[platform]
}

var Environment = getEnvironment()
module.exports = Environment

my-component.js

var Environment = require('./environment.js')

...somewhere in your code...
var url = Environment.BASE_URL

This creates a singleton environment which can be accessed from anywhere inside the scope of your app. You have to explicitly require(...) the module from any components that use Environment variables, but that is a good thing.

tohster
  • 6,973
  • 5
  • 38
  • 55
  • 26
    my problem is how to `getPlatform()`. I have make a file like this but cannot finish the logic here in React Native – Damon Yuan Oct 15 '15 at 01:23
  • @DamonYuan that depends entirely on how you're setting up your packages. I have no idea what `staging` or `production` even mean, because it's dependent on your environment. For example, if you want different flavors for IOS vs Android then you can initialize Environment by importing it your `index.ios.js` and `index.android.js` files and setting the platform there, e.g. `Environment.initialize('android')`. – tohster Oct 15 '15 at 01:45
  • @DamonYuan does what I put help at all, or do you need some more clarification? – chapinkapa Jun 14 '16 at 22:54
  • This is very nice when you have control over the code. I'm running a third part module which relies on process.env, so... – enapupe Jul 26 '16 at 22:21
  • 2
    If you create an `env.js` file be sure to ignore it from check-ins to the repository and copy the keys used, with empty string values, into another `env.js.example` file you do check-in so others can build your app more easily. If you accidentally check in project secrets consider [rewriting history](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History) to remove them not just from the source but the history thereof. – vhs Sep 22 '16 at 17:08
  • `React native does not have the concept of global variables`, it does. You can assign any value to global variable by doing `global.varA = 'valueA'` and you can access the value anywhere you want to – Isaac Jan 07 '19 at 01:24
  • "React native does not have the concept of global variables." well when did RN stop using Javascript global object? did they get around that somehow? – Felipe Valdes May 11 '19 at 17:36
25

I used the __DEV__ polyfill that is built into react-native in order to solve this problem. It is automatically set to true so long as you are not building react native for production.

E.g.:

//vars.js

let url, publicKey;
if (__DEV__) {
  url = ...
  publicKey = ...
} else {
  url = ...
  publicKey = ...
}

export {url, publicKey}

Then just import {url} from '../vars'and you'll always get the correct one. Unfortunately, this wont work if you want more than two environments, but its easy and doesn't involve adding more dependencies to your project.

Logister
  • 1,852
  • 23
  • 26
  • do you know a way to 'foce' __DEV__ to TRUE even when creating a release build in xcode? – realtebo Apr 16 '18 at 21:08
  • 1
    Nope. I just comment out the prod vars and then copy paste the dev vars into the prod section when I want to do a release build with dev variables. – Logister Apr 16 '18 at 22:32
  • 1
    I found this the most elegant solution – Dani Sh90 Jun 06 '19 at 22:05
  • 3
    Not a terrible solution, but it isn't great because it only handles boolean behavior. That is, either dev or not. How would I handle more than 2 environments? You may as well use `process.env.NODE_ENV` as it provides either `development` or `production`. Most people need to lift the app using dev, qa, staging, prod, etc. – Cody Mar 22 '21 at 03:40
6

i have created a pre build script for the same problem because i need some differents api endpoints for the differents environments

const fs = require('fs')

let endPoint

if (process.env.MY_ENV === 'dev') {
  endPoint = 'http://my-api-dev/api/v1'
} else if (process.env.MY_ENV === 'test') {
  endPoint = 'http://127.0.0.1:7001'
} else {
  endPoint = 'http://my-api-pro/api/v1'
}

let template = `
export default {
  API_URL: '${endPoint}',
  DEVICE_FINGERPRINT: Math.random().toString(36).slice(2)
}
`

fs.writeFile('./src/constants/config.js', template, function (err) {
  if (err) {
    return console.log(err)
  }

  console.log('Configuration file has generated')
})

And i have created a custom npm run scripts to execute react-native run..

My package-json

"scripts": {
    "start-ios": "node config-generator.js && react-native run-ios",
    "build-ios": "node config-generator.js && react-native run-ios --configuration Release",
    "start-android": "node config-generator.js && react-native run-android",
    "build-android": "node config-generator.js && cd android/ && ./gradlew assembleRelease",
    ...
}

Then in my services components simply import the auto generated file:

import config from '../constants/config'

fetch(`${config.API_URL}/login`, params)
Toni Chaz
  • 651
  • 11
  • 22
5

The specific method used to set environment variables will vary by CI service, build approach, platform and tools you're using.

If you're using Buddybuild for CI to build an app and manage environment variables, and you need access to config from JS, create a env.js.example with keys (with empty string values) for check-in to source control, and use Buddybuild to produce an env.js file at build time in the post-clone step, hiding the file contents from the build logs, like so:

#!/usr/bin/env bash

ENVJS_FILE="$BUDDYBUILD_WORKSPACE/env.js"

# Echo what's happening to the build logs
echo Creating environment config file

# Create `env.js` file in project root
touch $ENVJS_FILE

# Write environment config to file, hiding from build logs
tee $ENVJS_FILE > /dev/null <<EOF
module.exports = {
  AUTH0_CLIENT_ID: '$AUTH0_CLIENT_ID',
  AUTH0_DOMAIN: '$AUTH0_DOMAIN'
}
EOF

Tip: Don't forget to add env.js to .gitignore so config and secrets aren't checked into source control accidentally during development.

You can then manage how the file gets written using the Buddybuild variables like BUDDYBUILD_VARIANTS, for instance, to gain greater control over how your config is produced at build time.

vhs
  • 9,316
  • 3
  • 66
  • 70
  • overall i like the idea, but how does the `env.js.example` part work? let's say i want to launch the app in my local environment. if my `env.js` file is in gitignore and `env.js.example` is used as an outline, the `env.js.example` isn't a legitimate JS extension, so i'm just a little confused on what you meant by this part – volk Sep 27 '16 at 18:54
  • @volk The `env.js.example` file sits in the codebase as a reference document, a canonical source of truth as to what config keys the app wants to consume. It both describes the keys required to run the app, as well as the filename expected once copied and renamed. The pattern is common in Ruby apps using the [dotenv gem](https://github.com/bkeepers/dotenv), which is where I lifted the pattern from. – vhs Sep 27 '16 at 23:35
5

Follow these steps

  1. npm install react-native-dotenv
  2. set .env file with some variable names i.e APP_NAME="my-app"
  3. modify bable.config.js

module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: [
    ["module:react-native-dotenv", {
      "envName": "APP_ENV",
      "moduleName": "@env",
      "path": ".env",
      "safe": false,
      "allowUndefined": true,
      "verbose": false
    }]
  ]
};
  1. restart all the app
  2. try this one import {APP_NAME} from '@env'. if this not works then do this one const APP_NAME = process.env['APP_NAME'];

Cheers :)

ShoaibShebi
  • 59
  • 1
  • 5
4

I use babel-plugin-transform-inline-environment-variables.

What I did was put a configuration files within S3 with my different environments.

s3://example-bucket/dev-env.sh
s3://example-bucket/prod-env.sh
s3://example-bucket/stage-env.sh

EACH env file:

FIRSTENV=FIRSTVALUE
SECONDENV=SECONDVALUE

Afterwards, I added a new script in my package.json that runs a script for bundling

if [ "$ENV" == "production" ]
then
  eval $(aws s3 cp s3://example-bucket/prod-env.sh - | sed 's/^/export /')
elif [ "$ENV" == "staging" ]
then
  eval $(aws s3 cp s3://example-bucket/stage-env.sh - | sed 's/^/export /')
else
  eval $(aws s3 cp s3://example-bucket/development-env.sh - | sed 's/^/export /')
fi

react-native start

Within your app you will probably have a config file that has:

const FIRSTENV = process.env['FIRSTENV']
const SECONDENV = process.env['SECONDENV']

which will be replaced by babel to:

const FIRSTENV = 'FIRSTVALUE'
const SECONDENV = 'SECONDVALUE'

REMEMBER you have to use process.env['STRING'] NOT process.env.STRING or it won't convert properly.

Olcay Ertaş
  • 5,987
  • 8
  • 76
  • 112
Jack Zhang
  • 71
  • 1
  • `REMEMBER you have to use process.env['STRING'] NOT process.env.STRING or it won't convert properly.` Thanks! This is the one that trips me up!!! – Steven Yap Oct 02 '17 at 03:46
4

Step 1: Create separate component like this Component name : pagebase.js
Step 2: Inside this use code this

    export const BASE_URL = "http://192.168.10.10:4848/";
    export const API_KEY = 'key_token';

Step 3: Use it in any component, for using it first import this component then use it. Import it and use it:

        import * as base from "./pagebase";

        base.BASE_URL
        base.API_KEY
Jitendra Suthar
  • 2,111
  • 2
  • 16
  • 22
4

I used react-native-config to set up multiple environments for my project. The README file very clearly explains how to configure the library in your project. Just make sure to implement the Extra step for Android section.

Also while setting up multiple environments make sure to specify the correct start commands in your package.json, based upon your system terminal. I developed the Android code in a windows laptop and iOS code in Macbook, so my respective start commands in package.json were -

"scripts": {
        "android:dev": "SET ENVFILE=.env.dev && react-native run-android",
        "android:prod": "SET ENVFILE=.env.prod && react-native run-android",
        "ios:dev": "ENVFILE=.env.dev react-native run-ios",
        "ios:prod": "ENVFILE=.env.prod react-native run-ios",
},

In case you just need to maintain a single .env file, consider using react-native-dotenv as a lighter alternative, although I did face some issues in setting up multiple .env files for this library.

Nishant Kohli
  • 445
  • 6
  • 6
  • How do you read actual build time variables into your project? i.e. I may have some variables which are fine to add to those files. But some secret keys should come from the build environment? – Glennweb Apr 20 '22 at 06:56
4

hi there if you are facing this issue try this , this will work for me ,thanks me later

in bable.js

 plugins: [
      [
        "module:react-native-dotenv",
        {
          moduleName: "react-native-dotenv",
        },
      ],
    ],

use

import { YOURAPIKEY } from "react-native-dotenv";


inseted  of

import { YOURAPIKEY } from "@env";
Muhammad Awais
  • 109
  • 1
  • 3
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Nov 22 '21 at 07:42
  • Worked like a charm – Jafeth Yahuma Sep 19 '22 at 07:00
3

I think something like the following library could help you out to solve the missing bit of the puzzle, the getPlatform() function.

https://github.com/joeferraro/react-native-env

const EnvironmentManager = require('react-native-env');

// read an environment variable from React Native
EnvironmentManager.get('SOME_VARIABLE')
  .then(val => {
    console.log('value of SOME_VARIABLE is: ', val);

  })
  .catch(err => {
    console.error('womp womp: ', err.message);
  });

The only problem I see with this, that it's async code. There is a pull request to support getSync. Check it out too.

https://github.com/joeferraro/react-native-env/pull/9

leonfs
  • 710
  • 1
  • 5
  • 14
2

If you are using Expo there are 2 ways to do this according to the docs https://docs.expo.io/guides/environment-variables/

Method #1 - Using the .extra prop in the app manifest (app.json):

In your app.json file

{
  expo: {
    "slug": "my-app",
    "name": "My App",
    "version": "0.10.0",
    "extra": {
      "myVariable": "foo"
    }
  }
}

Then to access the data on your code (i.e. App.js) simply import expo-constants:

import Constants from 'expo-constants';

export const Sample = (props) => (
  <View>
    <Text>{Constants.manifest.extra.myVariable}</Text>
  </View>
);

This option is a good built-in option that doesn't require any other package to be installed.

Method #2 - Using Babel to "replace" variables. This is the method you would likely need especially if you are using a bare workflow. The other answers already mentioned how to implement this using babel-plugin-transform-inline-environment-variables, but I will leave a link here to the official docs to how to implement it: https://docs.expo.io/guides/environment-variables/#using-babel-to-replace-variables

Jo E.
  • 7,822
  • 14
  • 58
  • 94
2

IN TYPESCRIPT

  1. Install library: react-native-dotenv

    yarn add -D react-native-dotenv
    yarn add -D @types/react-native-dotenv
    
  2. Config file babel.config.js

    module.exports = {
    ...
      plugins: ['module:react-native-dotenv'],
    ...
    };
    
  3. Create file .env

    API_URL="api.com"
    
  4. Create folder types, then create file env.d.ts

    declare module '@env' {
      export const API_URL: string;
    }
    
  5. Add folder types into the tsconfig.json

    {
      ...
      "compilerOptions": {
        ...
        "typeRoots": ["./src/types/"]
        ...
      }
      ...
    }
    
  6. OK, now try it:

    import {API_URL} from '@env'
    console.log(API_URL)
    
Thiên Trần
  • 51
  • 1
  • 7
1

For latest RN versions, you can use this native module: https://github.com/luggit/react-native-config

Smakosh
  • 1,034
  • 12
  • 13
1

If you are developing your app using expo(managed workflow), you will have to create a file called app.config.js inside the root directory of your project and add the following code to the file:

const myValue = "My App";

export default () => {
    if (process.env.MY_ENVIRONMENT === "development") {
        return {
            name: myValue,
            version: "1.0.0",
            // All values in extra will be passed to your app.
            extra: {
                fact: "dogs are cool"
            }
        };
    } else {
        return {
            name: myValue,
            version: "1.0.0",
            // All values in extra will be passed to your app.
            extra: {
                fact: "kittens are cool"
            }
        };
    }
};

Then you should start/publish your app using the command below (this will work in Windows. For other operating systems, read the article I have mentioned at the end).

npx cross-env MY_ENVIRONMENT=development expo start/publish

This will start or publish your application using the environment variable mentioned above (MY_ENVIRONMENT). The application will load the appropriate configuration based on the environment variable. Now you can access the variable extra ,from your configuration, by importing a module called expo-constants into your project file. For instance:

import Constants from "expo-constants";

export default function App() {
    console.log(Constants.manifest.extra.fact);
    return (
        <>
            <View>
                <Text>Dummy</Text>
            </View>
        </>
    );
}

Using Constants.manifest we can access the object inside extra. So if your environment variable was development, this code should console.log "dogs are cool". I hope this was useful. For more information, go through this article.

0

you can also have different env scripts: production.env.sh development.env.sh production.env.sh

And then source them in when starting to work [which is just tied to an alias] so all the sh file has is export for each env variable:

export SOME_VAR=1234
export SOME_OTHER=abc

And then adding babel-plugin-transform-inline-environment-variables will allow access them in the code:

export const SOME_VAR: ?string = process.env.SOME_VAR;
export const SOME_OTHER: ?string = process.env.SOME_OTHER;
Pikachu-go
  • 3,058
  • 4
  • 20
  • 27
0

@chapinkapa's answer is good. An approach that I have taken since Mobile Center does not support environment variables, is to expose build configuration through a native module:

On android:

   @Override
    public Map<String, Object> getConstants() {
        final Map<String, Object> constants = new HashMap<>();
        String buildConfig = BuildConfig.BUILD_TYPE.toLowerCase();
        constants.put("ENVIRONMENT", buildConfig);
        return constants;
    } 

or on ios:

  override func constantsToExport() -> [String: Any]! {
    // debug/ staging / release
    // on android, I can tell the build config used, but here I use bundle name
    let STAGING = "staging"
    let DEBUG = "debug"

    var environment = "release"
    if let bundleIdentifier: String = Bundle.main.bundleIdentifier {
      if (bundleIdentifier.lowercased().hasSuffix(STAGING)) {
        environment = STAGING
      } else if (bundleIdentifier.lowercased().hasSuffix(DEBUG)){
        environment = DEBUG
      }
    }

    return ["ENVIRONMENT": environment]
  }

You can read the build config synchronously and decide in Javascript how you're going to behave.

vonovak
  • 1,555
  • 2
  • 14
  • 28
0

It is possible to access the variables with process.env.blabla instead of process.env['blabla']. I recently made it work and commented on how I did it on an issue on GitHub because I had some problems with cache based on the accepted answer. Here is the issue.

Srdjan Cosic
  • 324
  • 2
  • 9
0

[Source] From what I've found, it looks like by default, it's only possible to do production and development configs (no staging or other environments) – is that right?

Right now, I've been using a environment.js file that can be used to detect expo release channels and change the variables returned based on that, but for building, I need to update the non- DEV variable returned to be either staging or prod:

import { Constants } from 'expo';
import { Platform } from 'react-native';
const localhost = Platform.OS === 'ios' ? 'http://localhost:4000/' : 'http://10.0.2.2:4000/';
const ENV = {
  dev: {
    apiUrl: localhost,
  },
  staging: {
    apiUrl: 'https://your-staging-api-url-here.com/'
  },
  prod: {
    apiUrl: 'https://your-prod-api-url-here.com/'
  },
}
const getEnvVars = (env = Constants.manifest.releaseChannel) => {
  // What is __DEV__ ?
  // This variable is set to true when react-native is running in Dev mode.
  // __DEV__ is true when run locally, but false when published.
  if (__DEV__) {
    return ENV.dev;
  } else {
    // When publishing to production, change this to `ENV.prod` before running an `expo build`
    return ENV.staging;
  }
}
export default getEnvVars;

Alternatives

does anyone have experience using react-native-dotenv for projects built with expo? I'd love to hear your thoughts

https://github.com/goatandsheep/react-native-dotenv

Kemal Ahmed
  • 638
  • 6
  • 15
panchicore
  • 11,451
  • 12
  • 74
  • 100
  • You could define as many release channel names as you want, and test the name in order to define your environment variable. Where I see the limitation is in the dev environment where releaseChannel is undefined. So maybe you could use babel-plugin-transform-inline-environment-variables - you could pass on environment variables in your scripts and reference process.env['VAR_NAME'] in your environment.js file if dev? – colemerrick Aug 26 '19 at 16:06
0

Do not pass these variables like VAR=value react-native run-android or VAR=value react-native run-ios . These variables are only accessible if we pass them in start command i.e VAR=value react-native start --reset-cache.

You can achieve this with 3 simple steps:-

  1. Install babel-plugin-transform-inline-environment-variables by running npm i babel-plugin-transform-inline-environment-variables --save-dev.

  2. Add "plugins": [ "transform-inline-environment-variables" ] into your .bablerc or babel.config.js.

  3. Pass the variables while starting metro bundler i.e VAR=value reacti-native start --reset-cache, do not pass these variables in react-native run-android or react-native run-ios commands.

Please keep in mind that use of --reset-cache flag is required, otherwise changes in variables will not be applied.

Nimantha
  • 6,405
  • 6
  • 28
  • 69
Pawan Bishnoi
  • 1,759
  • 8
  • 19
0

After long efforts, I realized that react-native doesn't provide this feature officially. And this is in babel-ecosystem, so I should learn how to write a babel plugin...

/**
 * A simple replace text plugin in babel, such as `webpack.DefinePlugin`
 * 
 * Docs: https://github.com/jamiebuilds/babel-handbook
 */
function definePlugin({ types: t }) {
    const regExclude = /node_modules/;
    return {
        visitor: {
            Identifier(path, state) {
                const { node, parent, scope } = path;
                const { filename, opts } = state;
                const key = node.name;
                const value = opts[key];

                if (key === 'constructor' || value === undefined) { // don't replace
                    return;
                }
                if (t.isMemberExpression(parent)) { // not {"__DEV__":name}
                    return;
                }
                if (t.isObjectProperty(parent) && parent.value !== node) { // error
                    return;
                }
                if (scope.getBinding(key)) { // should in global
                    return;
                }
                if (regExclude.test(filename)) { // exclude node_modules
                    return;
                }
                switch (typeof value) {
                    case 'boolean':
                        path.replaceWith(t.booleanLiteral(value));
                        break;
                    case 'string':
                        path.replaceWith(t.stringLiteral(value));
                        break;
                    default:
                        console.warn('definePlugin only support string/boolean, so `%s` will not be replaced', key);
                        break;
                }
            },
        },
    };
}

module.exports = definePlugin;

That's all, then you can use like that:

module.exports = {
    presets: [],
    plugins: [
        [require('./definePlugin.js'), {
            // your environments...
            __DEV__: true,
            __URL__: 'https://example.org',
        }],
    ],
};

Packages that metioned by answerers are also great, and I also consult metro-transform-plugins/src/inline-plugin.js.

LitileXueZha
  • 512
  • 6
  • 11
0

You can do it with dotenv. First install the package,

npm i dotenv

Create .env file and set values,

API_KEY=your_api_key

Update .babelrc in the root directory, Or create new one with these lines,

{
  "presets": ["module:metro-react-native-babel-preset"],
  "plugins": [
    ["module:react-native-dotenv", {
      "moduleName": "@env",
      "path": ".env",
      "blacklist": null,
      "whitelist": null,
      "safe": false,
      "allowUndefined": true
    }]
  ]
}

Then you can use them like this,

import { API_KEY } from '@env';

const key = API_KEY;

Or

process.env.API_KEY

Or

process.env['API_KEY']
vimuth
  • 5,064
  • 33
  • 79
  • 116