1

Attempting to upgrade React Native from 0.63.2 to 0.65.2 has run into a number of issues, and I find myself playing android build wack-a-mole. The latest build error that I'm attempting to circumvent is:

The value for property 'resValues' cannot be changed any further

There is an existing article ( The value for property 'resValues' cannot be changed any further. after upgrading gradle from 4.1.3 to 4.2.1 ) however it indicates to remove or update the "heapanalytics" plugin, which my project does not have.

I have attempted to uninstall / reinstall npm library "expo-constants" which seems to be where the problem is occurring.

Has anyone run into any similar problem? Or can anyone better explain what might cause this issue, or potential steps to resolve? Any help or suggestions are appreciated!

Some additional info about the environment: android > build.gradle

buildscript {
    ext {
        googlePlayServicesVersion = "15.0.1"
        buildToolsVersion = "30.0.2"
        minSdkVersion = 23
        compileSdkVersion = 30
        targetSdkVersion = 30
        ndkVersion = "20.1.5948944"
    }
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath('com.android.tools.build:gradle:4.2.1')
        classpath('com.google.gms:google-services:4.3.3')

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        mavenCentral()
        mavenLocal()
        maven {
            // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
            url("$rootDir/../node_modules/react-native/android")
        }
        maven {
            // Android JSC is installed from npm
            url("$rootDir/../node_modules/jsc-android/dist")
        }
        maven { url 'https://www.jitpack.io' }
        google()
    }
}

additionally android > app > build.gradle

apply plugin: "com.android.application"
import com.android.build.OutputFile
project.ext.react = [
    enableHermes: false,  // clean and rebuild if changing
]
apply from: '../../node_modules/react-native-unimodules/gradle.groovy'  // expo unimodules
apply from: "./env.gradle"
apply from: "../../node_modules/react-native/react.gradle"
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
def enableSeparateBuildPerCPUArchitecture = false
def enableProguardInReleaseBuilds = false
def jscFlavor = 'org.webkit:android-jsc:+'
def enableHermes = project.ext.react.get("enableHermes", false);
android {
    ndkVersion rootProject.ext.ndkVersion

    compileSdkVersion rootProject.ext.compileSdkVersion

    signingConfigs { // must come before defaultConfig since defaultConfig references it.
        release {
            keyAlias = 'totally'
            storePassword 'real'
            keyPassword 'secret'
            storeFile file('stuff.keystore')
        }

        debug {
            keyAlias = 'more'
            storePassword 'super'
            keyPassword 'secret'
            storeFile file('stuff.keystore')
        }
    }
    defaultConfig {
        applicationId "com.redacted.someid"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode 6
        versionName "1.3.0"
        signingConfig signingConfigs.release
    }
    splits {
        abi {
            reset()
            enable enableSeparateBuildPerCPUArchitecture
            universalApk false  // If true, also generate a universal APK
            include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
        }
    }
    buildTypes {
        release {
            minifyEnabled enableProguardInReleaseBuilds
            proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
            signingConfig signingConfigs.release
        }

        debug {
            signingConfig signingConfigs.debug
        }
    }
    packagingOptions {
        pickFirst "lib/armeabi-v7a/libc++_shared.so"
        pickFirst "lib/arm64-v8a/libc++_shared.so"
        pickFirst "lib/x86/libc++_shared.so"
        pickFirst "lib/x86_64/libc++_shared.so"
    }
    // applicationVariants are e.g. debug, release
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            // For each separate APK per architecture, set a unique version code as described here:
            // https://developer.android.com/studio/build/configure-apk-splits.html
            def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
            def abi = output.getFilter(OutputFile.ABI)
            if (abi != null) {  // null for the universal-debug, universal-release variants
                output.versionCodeOverride =
                        defaultConfig.versionCode * 1000 + versionCodes.get(abi)
            }
        }
    }
    dexOptions {
        javaMaxHeapSize "4g"
    }
}
dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    //noinspection GradleDynamicVersion
    implementation "com.facebook.react:react-native:+"  // From node_modules

    implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"

    debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
        exclude group:'com.facebook.fbjni'
    }

    debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
        exclude group:'com.facebook.flipper'
        exclude group:'com.squareup.okhttp3', module:'okhttp'
    }

    debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
        exclude group:'com.facebook.flipper'
    }

    implementation 'androidx.appcompat:appcompat:1.1.0'

    addUnimodulesDependencies()  // expo unimodules

    if (enableHermes) {
        def hermesPath = "../../node_modules/hermes-engine/android/";
        debugImplementation files(hermesPath + "hermes-debug.aar")
        releaseImplementation files(hermesPath + "hermes-release.aar")
    } else {
        implementation jscFlavor
    }
}
task copyDownloadableDepsToLibs(type: Copy) {
    from configurations.implementation
    into 'libs'
}
project.ext.vectoricons = [
        iconFontNames: [ 'MaterialIcons.ttf' ] // Name of the font files you want to copy
]
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
apply plugin: 'com.google.gms.google-services'

and of course package.json

{
  "name": "redacted",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "postinstall": "patch-package && npx jetify",
    "android": "react-native run-android",
    "ios": "react-native run-ios",
    "start": "react-native start",
    "test": "jest",
    "typescriptcheck": "tsc --noEmit --skipLibCheck"
  },
  "engines": {
    "npm": "^6.14.12"
  },
  "dependencies": {
    "@react-native-async-storage/async-storage": "^1.15.8",
    "@react-native-community/async-storage": "^1.12.1",
    "@react-native-community/datetimepicker": "^3.0.4",
    "@react-native-community/masked-view": "0.1.11",
    "@react-native-community/progress-bar-android": "^1.0.3",
    "@react-native-community/progress-view": "^1.2.2",
    "@react-native-community/push-notification-ios": "1.8.0",
    "@react-native-firebase/app": "10.4.0",
    "@react-native-firebase/messaging": "10.4.0",
    "@react-navigation/core": "5.15.3",
    "@react-navigation/drawer": "5.12.5",
    "@react-navigation/native": "5.9.4",
    "@react-navigation/routers": "5.7.2",
    "@react-navigation/stack": "5.14.5",
    "@types/react-native-app-link": "^1.0.0",
    "autolinker": "^3.11.1",
    "crypto-js": "^3.1.9-1",
    "expo-constants": "^11.0.2",
    "expo-screen-capture": "^3.2.0",
    "jail-monkey": "^2.3.3",
    "jetifier": "^1.6.5",
    "moment": "^2.24.0",
    "moment-timezone": "^0.5.27",
    "patch-package": "^6.2.2",
    "react": "17.0.2",
    "react-native": "0.65.1",
    "react-native-android-open-settings": "^1.3.0",
    "react-native-app-link": "^1.0.0",
    "react-native-autoheight-webview": "^1.5.4",
    "react-native-bootsplash": "^3.1.5",
    "react-native-custom-tabs": "^0.1.7",
    "react-native-device-info": "^8.0.2",
    "react-native-fingerprint-scanner": "^5.0.0",
    "react-native-gesture-handler": "1.10.3",
    "react-native-keychain": "^4.0.5",
    "react-native-localize": "^2.0.2",
    "react-native-modal-datetime-picker": "^9.0.0",
    "react-native-pdf": "6.2.0",
    "react-native-phone-call": "^1.0.9",
    "react-native-picker-select": "^6.4.0",
    "react-native-reanimated": "1.13.3",
    "react-native-safari-view": "^2.1.0",
    "react-native-safe-area-context": "3.2.0",
    "react-native-screens": "3.4.0",
    "react-native-svg": "^12.1.0",
    "react-native-unimodules": "^0.14.8",
    "react-native-vector-icons": "^7.0.0",
    "react-native-webview": "^10.9.2",
    "react-redux": "^7.2.0",
    "redux": "^4.0.5",
    "rn-fetch-blob": "0.12.0",
    "rxjs": "^6.5.4",
    "tslint": "^5.12.1",
    "typescript": "^4.2.2",
    "unimodules-barcode-scanner-interface": "^6.1.0",
    "unimodules-camera-interface": "^6.1.0",
    "unimodules-constants-interface": "^6.1.0",
    "unimodules-face-detector-interface": "^6.1.0",
    "unimodules-file-system-interface": "^6.1.0",
    "unimodules-font-interface": "^6.1.0",
    "unimodules-image-loader-interface": "^6.1.0",
    "unimodules-permissions-interface": "^6.1.0",
    "unimodules-sensors-interface": "^6.1.0",
    "uuid": "^3.3.3"
  },
  "devDependencies": {
    "@babel/core": "^7.12.9",
    "@babel/plugin-transform-strict-mode": "^7.10.4",
    "@babel/runtime": "^7.12.5",
    "@types/crypto-js": "^3.1.43",
    "@types/jest": "25.1.0",
    "@types/moment-timezone": "^0.5.12",
    "@types/react": "^16.9.43",
    "@types/react-native": "^0.63.2",
    "@types/react-native-custom-tabs": "^0.1.1",
    "@types/react-native-safari-view": "^2.0.4",
    "@types/react-native-vector-icons": "^6.4.5",
    "@types/react-redux": "^7.1.9",
    "@types/react-test-renderer": "^16.9.2",
    "@types/remote-redux-devtools": "^0.5.3",
    "@types/source-map": "^0.5.7",
    "@types/uuid": "^3.4.6",
    "babel-jest": "^26.6.3",
    "babel-plugin-module-resolver": "^3.2.0",
    "jest": "^26.6.3",
    "jest-transform-stub": "^2.0.0",
    "metro-react-native-babel-preset": "^0.66.0",
    "react-native-codegen": "^0.0.7",
    "react-native-dotenv": "^0.2.0",
    "react-native-typescript-transformer": "^1.2.13",
    "react-test-renderer": "17.0.2",
    "remote-redux-devtools": "^0.5.16",
    "source-map": "^0.7.3",
    "ts-jest": "25.1.0"
  },
  "optionalDependencies": {
    "fbjs": "1.0.0",
    "fsevents": "1.2.9"
  },
  "jest": {
    "verbose": true,
    "preset": "react-native",
    "moduleFileExtensions": [
      "ts",
      "tsx",
      "js"
    ],
    "transform": {
      "^.+\\.(js)$": "<rootDir>/node_modules/react-native/jest/preprocessor.js",
      "^.+\\.(ts|tsx)$": "ts-jest"
    },
    "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
    "testPathIgnorePatterns": [
      "\\.snap$",
      "<rootDir>/node_modules/",
      "<rootDir>/ios/",
      "<rootDir>/.history/"
    ],
    "cacheDirectory": ".jest/cache",
    "moduleNameMapper": {
      "^.+.(png)$": "jest-transform-stub"
    },
    "setupFiles": [
      "<rootDir>/jest.setup.js"
    ]
  },
  "react-native-unimodules": {
    "android": {
      "exclude": [
        "expo-file-system",
        "expo-image-loader",
        "expo-permissions"
      ]
    },
    "ios": {
      "exclude": [
        "expo-file-system",
        "expo-image-loader",
        "expo-permissions"
      ]
    }
  }
}

*********** Update **********

The problem was related to processing .env and .env.production files. Although the below code was fine in previous iterations, it was throwing the resValue error in this version of gradle. (see loop processing variants, it attempts to set variant.resValue)

android > app > env.gradle

dependencies {
    implementation 'org.apache.commons:commons-text:1.6'
}

tasks.whenTaskAdded { task ->
    if (task.name == 'preDebugBuild') {
        task.doFirst {
            buildResourceValues('debug')
        }
    } else if (task.name == 'preReleaseBuild') {
        task.doFirst {
            buildResourceValues('release')
        }
    }
}

def buildResourceValues(buildVariant) {
    def env = readEnvFile(buildVariant)
    android.applicationVariants.all { variant ->
        if (variant.name.indexOf(buildVariant) > -1) {
            env.each { key, val ->
                variant.resValue 'string', key, escapeResourceValue(val)
                variant.buildConfigField 'String', key, "\"${groovy.json.StringEscapeUtils.escapeJava(val)}\""
            }
        }
    }
}

def readEnvFile(buildVariant) {
    def env = [:]
    def envFile = '.env'

    if (buildVariant != 'debug') {
        envFile = '.env.production'
    }

    File file = new File("$project.rootDir/../$envFile")
    if (file.exists()) {
        file.eachLine { line ->
            if (line[0] != '#' && line.indexOf('=') > -1) {
                def split = line.indexOf('=')
                def key = line.substring(0, split)
                def val = line.substring(split + 1, line.length())
                env.put(key, val)
            }
        }
    }
    return env
}

def static escapeResourceValue(str) {
    // & and < are automatically escaped, but other chars need to be manually escaped
    def escapes = [
            "@": "\\@",
            "?": "\\?",
            "%": "%%",
            "\'": "\\'",
            "\"": "\\\"",
            "\\": "\\\\"
    ]
    def retString = str.replaceAll(/[@?%'"\\]/) {match ->
        return escapes[match[0]]
    }
    return retString
}
Glorifundel
  • 746
  • 1
  • 8
  • 16

0 Answers0