DEV Community

Cover image for [React Native Guide 2024] Part 1: Builds and flavors.
Neeraj Mukta
Neeraj Mukta

Posted on • Edited on

[React Native Guide 2024] Part 1: Builds and flavors.

Before building an actual rocket, one should build a prototype. Before experimenting on the actual rocket, one should experiment on the prototype.” - not elon musk.

Update: react-native-config breaks with latesst react-native 0.74

Source code for the impatients.

Almost all of the web apps I have ever worked on had some sort of dev/prod parity setup. The larger teams and codebases even had more build types like local, QA, preprod etc. And it does make sense to separate out your main build from the one being actively developed. It is also in conjecture with 12 factor app philosophy.

In the mobile apps world, we follow something similar called build variants. If you’re not aware of it please read this blog first. It explains buildtypes and productflavors in detail. To put it in simple words. We have something called Build types, Product flavors cross product of the two gives us Build variants.

💡 BuildTypes * ProductFlavors = BuildVariants

The way it works in react native is also similar, we define buildTypes and productFlavor in app/build.gradle file. BuildTypes usually represent debug and release mode bundle whereas ProductFlavor is further divided into two more axis.
One axis is our environments like local, dev, qa, test, prod, etc. and the other one flavorDimensions, which is optional but useful in apps that have for ex. free and pro versions with almost similar code, or uber like separate apps for the riders and the drivers. You can enable flavors and write conditional code which gets built and compiled based on productflavors and flavorDimensions.

Now that we’re clear about build types and product flavors let’s set it up in a new RN project.

Step 1: Create a new RN project using React-native cli.

 npx react-native@0.73.6 init MyApp --version 0.73.6
Enter fullscreen mode Exit fullscreen mode

Step 2: Install react-native-config package

cd MyApp **&&**  npm i react-native-config
Enter fullscreen mode Exit fullscreen mode

Step 3: Define config for the build types, product Flavors in app/build.gradle.

  1. Add the following lines in the same file in the defaultConfig block. This will be applied to all the

    buildVarirants and we can also override these values.

    
     defaultConfig {
            resValue "string", "build_config_package", "com.myapp"
            resValue "string", "build_config_package", "com.myapp"
            applicationId "com.mmadmin"
            minSdkVersion rootProject.ext.minSdkVersion
            targetSdkVersion rootProject.ext.targetSdkVersion
            versionCode project.env.get("VERSION_CODE").toInteger()
            versionName project.env.get("VERSION_NAME")
     }
    

    b. You’ll notice buildTypes already exists. You can add/edit the configuration related to the specific bundle for ex. enableHermes option or enable source map. We’ll leave it as it is for now.

    buildTypes {
            debug {
                signingConfig signingConfigs.debug
            }
            release {
                // Caution! In production, you need to generate your own keystore file.
                // see https://reactnative.dev/docs/signed-apk-android.
                signingConfig signingConfigs.debug
                minifyEnabled enableProguardInReleaseBuilds
                proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
            }
        }
    

c. Let’s now define flavorDimensions and productFlavors. Since, we need only one app we will use default as flavorDismension. But we need 4 productFlavors local, dev, qa, and prod hence we add four blocks with some values. We’re overriding app_name and adding applicationIdSuffix and versionNameSuffix for each flavor. This will help us differentiate between apps and generate different buildVariants. Helps with testing on the same device or simulator.

flavorDimensions "default"
    productFlavors {
        local {
            resValue "string", "app_name", "MyApp Debug"
            applicationIdSuffix '.debug'
            versionNameSuffix "-DEBUG"
            minSdkVersion rootProject.ext.minSdkVersion
            targetSdkVersion rootProject.ext.targetSdkVersion

        }
        dev {
            resValue "string", "app_name", "MyApp Dev"
            applicationIdSuffix '.dev'
            versionNameSuffix "-DEV"+versionBuild
            minSdkVersion rootProject.ext.minSdkVersion
            applicationId "com.reactnativeproject.stage"
            targetSdkVersion rootProject.ext.targetSdkVersion

        }
        qa {
            resValue "string", "app_name", "MyApp Qa"
            applicationIdSuffix '.qa'
            versionNameSuffix "-QA"+versionBuild
            minSdkVersion rootProject.ext.minSdkVersion
            applicationId "com.reactnativeproject.stage"
            targetSdkVersion rootProject.ext.targetSdkVersion

        }
        prod {
           resValue "string", "app_name", "MyApp"
            minSdkVersion rootProject.ext.minSdkVersion
            applicationId "com.reactnativeproject.prod"
            targetSdkVersion rootProject.ext.targetSdkVersion

        }
    }
Enter fullscreen mode Exit fullscreen mode

Step 4: Setup environments

  1. Create your environment files like .env.local, .env.dev, .env.prod files and add the keys.
  2. Update app/build.gradle file with the following code at the beginning of the file.

project.ext.envConfigFiles = [
        localdebug:".env.local",
        localrelease :".env.local",
        devdebug: ".env.dev",
        devrelease: ".env.dev",
        qadebug:'.env.qa',
        qarelease:'.env.qa',
        proddebug:'.env.prod',
        prodrelease:'.env.prod',
]

apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle"
def versionBuild = project.hasProperty('BUILD_NUMBER') ? "+" + project.findProperty('BUILD_NUMBER').toString() : "";
Enter fullscreen mode Exit fullscreen mode

Notice how we have different combinations for buildTypes and productFlavors like productFlavorbuildTypes. So, every productFlavor has two buildTypes debug and release.

Important, thing here is to match the buildVaraint name with the correct .env file.

Now, we can access our variables inside the react components

import Config from 'react-native-config';

function HomeScreen({navigation}) {
  return (
    <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
      <Text>Home screen</Text>
      <Text>
        _**ENVIROMENT:{Config.ENVIROMENT} ::: APP_ID:{Config.APP_ID}**_
      </Text>
      <Button
        title="Go to Details"
        onPress={() => navigation.navigate('Stack_Details')}
      />
    </View>
  );
Enter fullscreen mode Exit fullscreen mode

We can also access them inside the build.gradle file ex.

versionCode project.env.get("VERSION_CODE").toInteger()
Enter fullscreen mode Exit fullscreen mode

To access env variables inside android .java/.kt files

 URL url = new URL(BuildConfig.API_URL);
Enter fullscreen mode Exit fullscreen mode

and inside iOS files

 //import header
#import "ReactNativeConfig.h"
// then read individual key like:
NSString *apiUrl = [ReactNativeConfig envFor:@"API_URL"];
Enter fullscreen mode Exit fullscreen mode

Final Step: Update Package.json with build commands.

Now, we just need to update scripts to build and run different build variants of our app.

"scripts": {
    "android:local": "react-native run-android  --mode=localDebug",
    "android:dev":"react-native run-android  --mode=devDebug",
    "release": "cd android && .\\gradlew assembleProdRelease",
    "release-aab": "cd android && .\\gradlew bundleProdRelease",
    "release-dev": "cd android && .\\gradlew assembleDevRelease",
    "release-qa": "cd android && .\\gradlew assembleQaRelease",
    "cleanDeps": "rm -rf node_modules && npm i",
    "lint": "eslint .",
    "start": "react-native start",
    "test": "jest"
  },
Enter fullscreen mode Exit fullscreen mode

scripts are modified to run on windows terminal.*

We can run the app locally with npm run android:local, we can generate .apk for prod, dev, qa with npm run release, npm run release-dev , npm run release-qa respectively.

And, we can generate .aab file for playstore submission with npm run release-aab.

Hey, I’m Neeraj Mukta I expertise in crafting resilient apps tailored for startups. I’ve been crafting web and mobile apps for 8+ years now. If you would like to chat or need assistance with technical problems, feel free to reach out on X, Linkedin.

Top comments (0)