Skip to content

Empty Config object in one of android build flavors used with --appIdSuffix (RN 0.75.5) #850

@code-by

Description

@code-by

Empty Config object in one of android build flavors invoked with --appIdSuffix

Hello there. I'm created two build flavors for have two builds of my react native application. While I run production flavor in debug mode, Config object is populated with correct values, while Config object is empty in staging build.
I have tried to fix issue with different build configs and settings, but even suggestions from AI (gemini) not result to obtain correct Config in staging build. If there missing some other info - please let me now, I will add ASAP.
I would appreciate any help, thank you in advice.

For my application there will be two buildId
production: health.company.myapp
staging: health.company.myapp.staging

Log of Config object for production debug build:

{"AMPLITUDE_API_KEY": "abc123", "APPLICATION_ID": "health.company.myapp", "APP_ENV": "production", "BUILD_TYPE": "debug", "DEBUG": true, "FLAVOR": "production", "IS_HERMES_ENABLED": true, "IS_NEW_ARCHITECTURE_ENABLED": false, "VERSION_CODE": 1, "VERSION_NAME": "0.0.1", "getConstants": [Function anonymous]}

this is log of Config object for staging build:

{}

Versions of RN and modules used:

node 22.17.1
yarn 4.9.2

react 18.2.0
react-native 0.75.5
react-native-config 1.5.9

App invoked by yarn scripts:

"android:runStagingDebug": "cross-env ENVFILE=.env.staging npx react-native run-android --mode=stagingDebug --appIdSuffix staging",
"android:runStagingRelease": "cross-env ENVFILE=.env.staging npx react-native run-android --mode=stagingRelease --appIdSuffix staging",
"android:runProductionDebug": "cross-env ENVFILE=.env.production npx react-native run-android --mode=productionDebug",

Bellow content of some files I think need to be verified:

/app/build.gradle:

apply plugin: "com.android.application"
apply plugin: "org.jetbrains.kotlin.android"
apply plugin: "com.facebook.react"
apply plugin: 'com.google.gms.google-services'

react {
    debuggableVariants = ["debug", "stagingDebug", "productionDebug"]
    autolinkLibrariesWithApp()
}

def enableProguardInReleaseBuilds = true

def jscFlavor = 'org.webkit:android-jsc:+'

project.ext.envConfigFiles = [
    productionDebug: "../../.env.production",
    productionRelease: "../../.env.production",
    // stagingDebug: "../../.env.staging", // Handled manually for staging flavor
    // stagingRelease: "../../.env.staging"  // Handled manually for staging flavor
]

println("Current project.ext.envConfigFiles: " + project.ext.envConfigFiles)

android {
    flavorDimensions "environment"
    ndkVersion rootProject.ext.ndkVersion
    compileSdk rootProject.ext.compileSdkVersion

    namespace "health.company.myapp"
    defaultConfig {
        applicationId "health.company.myapp"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode 1
        versionName "0.0.1"
    }
    signingConfigs {
        debug {
            storeFile file('debug.keystore')
            storePassword 'android'
            keyAlias 'androiddebugkey'
            keyPassword 'android'
        }
    }

    applicationVariants.all { variant ->
        if (variant.name == "stagingDebug" || variant.name == "stagingRelease") {
            def envFile = project.ext.envConfigFiles.get(variant.name) // This will now be null for staging
            println "For staging variant ${variant.name}, configured env file in project.ext.envConfigFiles is: ${envFile}"
        }
    }

    productFlavors {
        staging {
            dimension "environment"
            applicationId "health.company.myapp.staging"
            resValue "string", "app_name", "MyApp Staging"
            // build_config_package is still useful as a hint for other tools, or if RNC tries to read it.
            resValue "string", "build_config_package", "health.company.myapp.staging"
            versionCode 100000 + android.defaultConfig.versionCode
            versionName "100001.0.1-staging"

            // Manually load .env.staging for this flavor
            def stagingEnvFilePath = "../../.env.staging"
            def stagingEnvFile = file(stagingEnvFilePath) // Resolve file relative to this build.gradle
            if (stagingEnvFile.exists()) {
                println "Manually loading .env.staging for 'staging' flavor from ${stagingEnvFile.absolutePath}"
                stagingEnvFile.eachLine { line ->
                    line = line.trim()
                    if (line.startsWith("#") || line.isEmpty() || !line.contains("=")) {
                        return // Skip comments, empty lines, or lines without '='
                    }
                    def parts = line.split("=", 2)
                    if (parts.length == 2) {
                        def key = parts[0].trim()
                        def value = parts[1].trim()
                        if ((value.startsWith("\"") && value.endsWith("\"")) || (value.startsWith("'") && value.endsWith("'"))) {
                            value = value.substring(1, value.length() - 1)
                        }
                        println "  + Adding buildConfigField from .env.staging: ${key} = ${value}"
                        buildConfigField "String", "${key}", "\"${value.replace("\"", "\\\"")}\""
                    }
                }
            } else {
                println "WARN: .env.staging file not found at ${stagingEnvFile.absolutePath} (path was ${stagingEnvFilePath}) for manual loading."
            }
        }
        production {
            dimension "environment"
            applicationId android.defaultConfig.applicationId
            resValue "string", "app_name", "MyApp"
            resValue "string", "build_config_package", "health.company.myapp"
            versionCode android.defaultConfig.versionCode
            versionName android.defaultConfig.versionName
        }
    }
    sourceSets {
        staging {
            java.srcDirs = ['src/staging/java']
            manifest.srcFile 'src/staging/AndroidManifest.xml'
            res.srcDirs = ['src/staging/res']
        }
    }
    buildTypes {
        debug {
            signingConfig signingConfigs.debug
        }
        release {
            signingConfig signingConfigs.debug
            minifyEnabled enableProguardInReleaseBuilds
            proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
        }
    }
}

apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle"

dependencies {
    implementation("com.facebook.react:react-android")
    implementation("androidx.core:core-splashscreen:1.0.0")
    implementation 'com.facebook.android:facebook-android-sdk:18.0.3'
    implementation project(':react-native-config')

    if (hermesEnabled.toBoolean()) {
        implementation("com.facebook.react:hermes-android")
    } else {
        implementation jscFlavor
    }
}

android\app\src\staging\AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <application
        android:name="health.company.myapp.staging.MainApplication"
        tools:replace="android:name">
        <activity
            android:name="health.company.myapp.MainActivity"
            tools:node="remove" />
        <activity
            android:name="health.company.myapp.staging.MainActivity"
            android:exported="false" />
        <activity-alias
            android:name="health.company.myapp.MainActivity"
            android:targetActivity="health.company.myapp.staging.MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>
    </application>
</manifest>

android\app\build\generated\source\buildConfig\staging\debug\health\company\myapp\BuildConfig.java:

package health.company.myapp;

public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");
  public static final String APPLICATION_ID = "health.company.myapp.staging";
  public static final String BUILD_TYPE = "debug";
  // Field from product flavor: staging
  public static final String FLAVOR = "staging";
  public static final int VERSION_CODE = 100001;
  public static final String VERSION_NAME = "100001.0.1-staging";
  // Field from product flavor: staging
  public static final String AMPLITUDE_API_KEY = "abc123";
  // Field from product flavor: staging
  public static final String APP_ENV = "staging";
  // Field from default config.
  public static final boolean IS_HERMES_ENABLED = true;
  // Field from default config.
  public static final boolean IS_NEW_ARCHITECTURE_ENABLED = false;
}

android\app\build\generated\source\buildConfig\production\debug\health\company\myapp\BuildConfig.java

package health.company.myapp;

public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");
  public static final String APPLICATION_ID = "health.company.myapp";
  public static final String BUILD_TYPE = "debug";
  // Field from default config.
  public static final String FLAVOR = "production";
  public static final int VERSION_CODE = 1;
  public static final String VERSION_NAME = "0.0.1";
  // Field from default config.
  public static final String AMPLITUDE_API_KEY = "abc123";
  // Field from default config.
  public static final String APP_ENV = "production";
  // Field from default config.
  public static final boolean IS_HERMES_ENABLED = true;
  // Field from default config.
  public static final boolean IS_NEW_ARCHITECTURE_ENABLED = false;
}

There default MainActivity.kt and MainApplication.kt in android\app\src\main\java\health\company\myapp\ and these two with diffent content in android\app\src\staging\java\health\company\myapp\staging\
I could provide all of these if needed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions