[React Native Guide 2024] Part 1: Builds and flavors.
Published: 2024-04-23 04:10:06


> “_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](https://github.com/fullstackNRJ/rn-setup-2024) 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](https://12factor.net/) philosophy.


In the mobile apps world, we follow something similar called build variants. If you’re not aware of it please read [this ](https://developer.android.com/build)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.**


```bash
 npx react-native@0.73.6 init MyApp --version 0.73.6
```

**Step 2: Install** _**react-native-config**_ **package**


```bash
cd MyApp **&&**  npm i react-native-config
```

**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.


	```groovy
	
	 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.


	```groovy
	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.


```groovy
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
           
        }
    }
```


**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.

```groovy

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() : "";
```


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 


```javascript
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>
  );
```



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


```groovy
versionCode project.env.get("VERSION_CODE").toInteger()
```


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


```java
 URL url = new URL(BuildConfig.API_URL);
```



and inside iOS files


```objective-c
 //import header
#import "ReactNativeConfig.h"
// then read individual key like:
NSString *apiUrl = [ReactNativeConfig envFor:@"API_URL"];
```


**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.


```json
"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"
  },
```


_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,](https://twitter.com/devnrj07) [Linkedin](https://www.linkedin.com/in/devnrj07/).