skip to Main Content

Background Info

We are not using eas or expo go, instead we are using our own provisioning profiles and a combination of app.json and babel.config.js.

We want to transition from dev environment to qa/staging environments. We are using azure pipelines to run expo’s prebuild command for ios and android builds. For example: npx expo prebuild --platform ios --clean --npm

I was successful in setting up the dev environment to run with the following command… npx expo run dev-android (which points to the following script in package.json "dev-android": "cross-env NODE_ENV=development npx expo run:android --port 8082"

The following successfully prints out in the development environment…

enter image description here

The Structure

enter image description here

The Code…

App.tsx

import React, { useEffect } from 'react';
import * as SplashScreen from 'expo-splash-screen';
import '@app/utils/IgnoreWarnings';
import Toast from 'react-native-toast-message';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { GlobalContextProvider, useLoadingStore } from '@app/stores';
import { ToastInit } from '@app/components';
import { NavigationConductor } from '@app/navigation';
import { toastFailedProps } from '@app/utils';
import { RANDOM_ENV_DEV, RANDOM_ENV_QA } from '@env';
import type { LoadingStore } from '@app/types';

// Keep the splash screen visible while we fetch resources
SplashScreen.preventAutoHideAsync();

export default function App() {
  // variables
  const { loadInitialAppData } = useLoadingStore((store: LoadingStore) => store);

  // setup
  useEffect(() => {
    console.log('NODE_ENV:', process.env.NODE_ENV);
    console.log('RANDOM_ENV_DEV:', RANDOM_ENV_DEV);
    console.log('RANDOM_ENV_QA:', RANDOM_ENV_QA);
    const loadApp = async () => {
      try {
        await loadInitialAppData();
      } catch (error: any) {
        Toast.show(toastFailedProps(error.message));
      } finally {
        await SplashScreen.hideAsync();
      }
    };
    loadApp();
  }, [loadInitialAppData]);

  // render
  return (
    <GlobalContextProvider>
      <SafeAreaProvider>
        <NavigationConductor />
        <ToastInit />
      </SafeAreaProvider>
    </GlobalContextProvider>
  );
}

.env

RANDOM_ENV_DEV=HelloWorldDev
RANDOM_ENV_QA=HelloWorldQa

babel.config.js

module.exports = function (api) {
  api.cache(true);
  return {
    presets: ['module:metro-react-native-babel-preset', 'babel-preset-expo'],
    plugins: [
      [
        require.resolve('babel-plugin-module-resolver'),
        {
          extensions: ['.js', '.jsx', '.ts', '.tsx', '.android.js', '.android.tsx', '.ios.js', '.ios.tsx'],
          alias: {
            '@': './',
            '@app': './app'
          }
        }
      ],
      [
        'module:react-native-dotenv',
        {
          envName: 'APP_ENV',
          moduleName: '@env',
          path: '.env',
          blocklist: null,
          allowlist: null,
          blacklist: null, // DEPRECATED
          whitelist: null, // DEPRECATED
          safe: false,
          allowUndefined: true,
          verbose: false
        }
      ]
    ]
  };
};

app.json (config file)

{
  "expo": {
    "name": "PROJECTNAME",
    "slug": "PROJECTSLUG",
    "version": "1.0.0",
    "scheme": "msauth",
    "orientation": "portrait",
    "icon": "./assets/images/qa-icon-1024.png",
    "userInterfaceStyle": "light",
    "backgroundColor": "#001689",
    "splash": {
      "image": "./assets/images/qa-splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#001689"
    },
    "assetBundlePatterns": ["**/*"],
    "ios": {
      "supportsTablet": true,
      "bundleIdentifier": "com.example.qa",
      "buildNumber": "1.0.0"
    },
    "android": {
      "adaptiveIcon": {
        "foregroundImage": "./assets/images/qa-icon-1024.png",
        "backgroundColor": "#001689"
      },
      "package": "com.example.qa",
      "versionCode": 1
    },
    "web": {
      "favicon": "./assets/favicon.png"
    },
    "plugins": [
      [
        "expo-build-properties",
        {
          "android": {
            "minSdkVersion": 23
          }
        }
      ],
      [
        "expo-image-picker",
        {
          "photosPermission": "Allow $(PRODUCT_NAME) to access your photos",
          "cameraPermissions": "Allow $(PRODUCT_NAME) to access your camera"
        }
      ]
    ]
  }
}

package.json

{
  "name": "Project-Name",
  "version": "1.0.0",
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "ts:check": "tsc",
    "dev-android": "cross-env NODE_ENV=development npx expo run:android --port 8082",
    "dev-ios": "cross-env NODE_ENV=development npx expo run:ios --port 8082",
    "android": "expo run:android",
    "ios": "expo run:ios"
  },
  "dependencies": {
    "@expo/vector-icons": "^13.0.0",
    "@react-native-async-storage/async-storage": "1.18.2",
    "@react-native-community/netinfo": "9.3.10",
    "@react-navigation/native": "^6.1.9",
    "@react-navigation/stack": "^6.3.20",
    "axios": "^1.6.2",
    "core-js": "^3.35.0",
    "expo": "~49.0.15",
    "expo-auth-session": "~5.0.2",
    "expo-build-properties": "^0.8.3",
    "expo-constants": "~14.4.2",
    "expo-file-system": "~15.4.5",
    "expo-image-manipulator": "~11.3.0",
    "expo-image-picker": "~14.3.2",
    "expo-secure-store": "~12.3.1",
    "expo-splash-screen": "~0.20.5",
    "expo-status-bar": "~1.6.0",
    "expo-system-ui": "~2.4.0",
    "expo-web-browser": "~12.3.2",
    "formik": "^2.4.5",
    "jwt-decode": "^4.0.0",
    "lodash": "^4.17.21",
    "react": "18.2.0",
    "react-native": "0.72.10",
    "react-native-device-info": "^11.1.0",
    "react-native-element-dropdown": "^2.10.1",
    "react-native-gesture-handler": "~2.12.0",
    "react-native-get-random-values": "~1.9.0",
    "react-native-safe-area-context": "4.6.3",
    "react-native-screens": "~3.22.0",
    "react-native-toast-message": "^2.1.9",
    "react-query": "^3.39.3",
    "scandit-react-native-datacapture-barcode": "^6.21.3",
    "scandit-react-native-datacapture-core": "^6.21.3",
    "uri-scheme": "^1.1.0",
    "uuid": "^9.0.1",
    "yup": "^1.3.2",
    "zustand": "^4.4.7"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0",
    "@types/lodash": "^4.14.202",
    "@types/react": "~18.2.14",
    "@types/uuid": "^9.0.7",
    "cross-env": "^7.0.3",
    "react-native-dotenv": "^3.4.10",
    "typescript": "^5.1.3"
  },
  "private": true
}

Env.ts

declare module '@env' {
  export const RANDOM_ENV_DEV: string;
  export const RANDOM_ENV_QA: string;
}
declare var process: {
  env: {
    NODE_ENV: string;
  };
};

Results

This is working in the development environment where process.env.NODE_ENV reads as development.

The Question

Now how do we make it where when we use expo prebuild command to package a build that would include process.env.NODE_ENV will read as uat or qa?

2

Answers


  1. Chosen as BEST ANSWER

    The only solution I could find for this was still a bit hacky, but at least it is in one place.

    babel.config.js

    change to...

    module.exports = function (api) {
      api.cache(false);
      return {
        ...,
        plugins: [
          ...
          [
            ...
            {
              ...
              path: '.env.dev', // .env.qa | .env.uat | ect...
              ...
            }
          ]
        ]
      };
    };
    

    Just avoid doing any rebase between the branches and when you upload the changes per branch in the right order then future merges will not overwrite this value.

    It's still not an ideal solution, but at least the value is changed in only one location.


  2. Since you already have react-native-dotenv you can create multiple .env files to cater your needs on UAT or QA

    .env.dev
    .env.uat
    .env.qa
    

    In your package.json file

    "scripts": {
         "dev-android": "cross-env NODE_ENV=development npx expo run:android --port 8082",
         "dev-ios": "cross-env NODE_ENV=development npx expo run:ios --port 8082",
         "uat-android": "cross-env NODE_ENV=uat npx expo run:android --port 8082",
         "uat-ios": "cross-env NODE_ENV=uat npx expo run:ios --port 8082",
         "qa-android": "cross-env NODE_ENV=qa npx expo run:android --port 8082",
         "qa-ios": "cross-env NODE_ENV=qa npx expo run:ios --port 8082",
      },
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search