React Native Expo Environment Variables

Viewed 24398

So I'm happy with the concept of environment variables as explained in this article and others https://www.freecodecamp.org/news/how-to-gracefully-use-environment-variables-in-a-react-native-app/

Great, I've got my SOMETHING="something" stored so I can just use env.SOMETHING or whatever

The part I'm a little lost on is where you keep the live variables?

I would rather not do a solution like this as it seems you are still keeping your keys quite public and that you are just choosing based on the environment with if statements

Manage environment with expo react native

For example with an Express App deployment we have, we specify

let endPointURL = env.endPointURL

and then we keep a versoin of that variable locally and when it sits with AWS it is overridden by AWS servers as explained here

I was just wondering does something like that exist for Android and iOS builds (on the respective stores) or through Expo?

Thanks all

3 Answers

Honestly I think the way they go about it is a little silly. There may be a better way to go about than this, but I think I followed their documentation suggestions.

https://docs.expo.io/versions/latest/distribution/release-channels/#using-release-channels-for-environment-variable-configuration

They have a code snippet suggesting you create a function to look at the release configuration itself.

I interpreted it that you might do something like the code below and store your environment variables in a variables.js file and pull in your environment variables as such.

import Constants from 'expo-constants';

export const prodUrl = "https://someapp.herokuapp.com";

const ENV = {
  dev: {
    apiUrl: "http://localhost:3000"
  },
  staging: {
    apiUrl: prodUrl
  },
  prod: {
    apiUrl: prodUrl
  }
};

function getEnvVars(env = "") {
  if (env === null || env === undefined || env === "") return ENV.dev;
  if (env.indexOf("dev") !== -1) return ENV.dev;
  if (env.indexOf("staging") !== -1) return ENV.staging;
  if (env.indexOf("prod") !== -1) return ENV.prod;
}

export default getEnvVars(Constants.manifest.releaseChannel);

Edit:

Now that Expo supports config file as app.config.js or app.config.ts, we can use the dotenv. Check this: https://docs.expo.io/guides/environment-variables/#using-a-dotenv-file

A simpler approach would be to export the env object instead of the function:

import Constants from 'expo-constants';
import { Platform } from "react-native";

const localhost =
 Platform.OS === "ios" ? "localhost:8080" : "10.0.2.2:8080";


const ENV = {
    dev: {
      apiUrl: localhost,
      amplitudeApiKey: null,
    },
    staging: {
      apiUrl: "[your.staging.api.here]",
      amplitudeApiKey: "[Enter your key here]",
      // Add other keys you want here
    },
    prod: {
      apiUrl: "[your.production.api.here]",
      amplitudeApiKey: "[Enter your key here]",
      // Add other keys you want here
    }
};

const getEnvVars = (env = Constants.manifest.releaseChannel) => {
  if (env === null || env === undefined || env === "" || env.indexOf("dev") !== -1) return ENV.dev;
  if (env.indexOf("staging") !== -1) return ENV.staging;
  if (env.indexOf("prod") !== -1) return ENV.prod;
}

const selectedENV = getEnvVars();

export default selectedENV;

// Import
import env from '..xxx/utility/env';

Get it in your ios-generated file, based on .env file:

  1. In .env, write down GOOGLE_MAPS_API=abcde...
  2. yarn add react-native-config
  3. cd ios
  4. pod install
  5. In your Objective-C-compiled code, for example, AppDelegate.m:
#import "ReactNativeConfig.h"
NSString *mapsApiKey = [ReactNativeConfig envFor:@"GOOGLE_MAPS_API"];
[GMSServices provideAPIKey:mapsApiKey];

Credits to: ReactNative: Pass JS variable to AppDelegate based on https://github.com/luggit/react-native-config.

Android should work as well, but haven't tested / followed https://github.com/luggit/react-native-config. Edit: required steps for Android:

  1. <meta-data android:name="com.google.android.geo.API_KEY" android:value="@string/GOOGLE_MAPS_API"/> in AndroidManifest.xml.
  2. In settings.gradle:
include ':react-native-config'
project(':react-native-config').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-config/android')

right after rootProject.name = 'XYZ'

  1. In build.gradle: apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle" right below import com.android.build.OutputFile and implementation project(':react-native-config') right below implementation "com.facebook.react:react-native:+" // From node_modules

Regarding "normal" usage in .ts, .tsx, .js files I'm declaring variables in .env based on https://github.com/goatandsheep/react-native-dotenv by declaring "module:react-native-dotenv" in babel.config.js in plugins, and it works like a charm like so:

import { ACCESS_TOKEN } from "@env";
...
headers: {
  Authorization: `Bearer ${ACCESS_TOKEN}`,
  Accept: "application/json",
},

Edit: important the eas build ignores .gitignore-declared variable, so if your .env is in .gitignore the production bundle won't have it included.

Related