From e16f546e12ba5edcb73169dc18639f4f7814b389 Mon Sep 17 00:00:00 2001 From: gre Date: Sun, 29 Mar 2026 17:42:55 +0200 Subject: [PATCH] Fix Android release APK crash: enable new architecture for RN 0.84 The release APK was crashing with "TurboModuleRegistry.getEnforcing(...): 'PlatformConstants' could not be found" because: 1. newArchEnabled was false while MainApplication.kt uses bridgeless APIs (loadReactNative, getDefaultReactHost). RN 0.84 requires new arch for CoreReactPackage which provides PlatformConstants. 2. The JS spec eagerly called TurboModuleRegistry.getEnforcing at module load time and used an outdated isTurboModuleEnabled check based on global.__turboModuleProxy (always true in bridgeless mode). 3. Top-level Platform.OS access in index.tsx forced eager loading of NativePlatformConstantsAndroid which calls getEnforcing('PlatformConstants'). Fixes: - Enable newArchEnabled=true in gradle.properties (default for RN 0.84) - Modernize TurboModule spec: use TurboModuleRegistry.get() instead of manual isTurboModuleEnabled check with getEnforcing (follows pattern used by react-native-safe-area-context and other maintained libraries) - Move Platform.OS access from module top-level into validateOptions() to defer native module loading until first use - Pass -PnewArchEnabled=false only for Detox Android builds Co-Authored-By: Claude Opus 4.6 (1M context) --- example/.detoxrc.js | 3 ++- example/android/gradle.properties | 3 +-- src/index.tsx | 14 ++++++-------- src/specs/NativeRNViewShot.ts | 13 +++---------- 4 files changed, 12 insertions(+), 21 deletions(-) diff --git a/example/.detoxrc.js b/example/.detoxrc.js index 208bee6d..8a2acd97 100644 --- a/example/.detoxrc.js +++ b/example/.detoxrc.js @@ -19,7 +19,8 @@ module.exports = { 'android.debug': { type: 'android.apk', binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk', - build: 'cd android && ./gradlew assembleDebug assembleAndroidTest', + build: + 'cd android && ./gradlew assembleDebug assembleAndroidTest -PnewArchEnabled=false', testBinaryPath: 'android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk', reversePorts: [8081], diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 6553b728..56ca8ea7 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -32,8 +32,7 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 # your application. You should enable this flag either if you want # to write custom TurboModules/Fabric components OR use libraries that # are providing them. -# Temporarily disabled for Detox E2E testing compatibility -newArchEnabled=false +newArchEnabled=true # Use this property to enable or disable the Hermes JS engine. # If set to false, you will be using JSC instead. diff --git a/src/index.tsx b/src/index.tsx index 7d8ccc53..fd3c422f 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -98,14 +98,6 @@ export interface ViewShotProperties { style?: StyleProp; } -const acceptedFormats = ["png", "jpg"].concat( - Platform.OS === "android" ? ["webm", "raw"] : [], -); - -const acceptedResults = ["tmpfile", "base64", "data-uri"].concat( - Platform.OS === "android" ? ["zip-base64"] : [], -); - const defaultOptions: CaptureOptions = { format: "png", quality: 1, @@ -119,6 +111,12 @@ function validateOptions(input?: CaptureOptions): { options: CaptureOptions; errors: string[]; } { + const acceptedFormats = ["png", "jpg"].concat( + Platform.OS === "android" ? ["webm", "raw"] : [], + ); + const acceptedResults = ["tmpfile", "base64", "data-uri"].concat( + Platform.OS === "android" ? ["zip-base64"] : [], + ); const options: CaptureOptions = { ...defaultOptions, ...input, diff --git a/src/specs/NativeRNViewShot.ts b/src/specs/NativeRNViewShot.ts index 87449062..84a198a9 100644 --- a/src/specs/NativeRNViewShot.ts +++ b/src/specs/NativeRNViewShot.ts @@ -1,6 +1,6 @@ import type {TurboModule} from "react-native"; -import {TurboModuleRegistry, NativeModules, Platform} from "react-native"; -import {Int32, WithDefault} from "react-native/Libraries/Types/CodegenTypes"; +import {TurboModuleRegistry} from "react-native"; +import {WithDefault} from "react-native/Libraries/Types/CodegenTypes"; export interface Spec extends TurboModule { releaseCapture: (uri: string) => void; @@ -11,11 +11,4 @@ export interface Spec extends TurboModule { captureScreen: (options: Object) => Promise; } -// Support both old and new architecture -const isTurboModuleEnabled = global.__turboModuleProxy != null; - -const RNViewShotModule = isTurboModuleEnabled - ? TurboModuleRegistry.getEnforcing("RNViewShot") - : NativeModules.RNViewShot; - -export default RNViewShotModule as Spec; +export default TurboModuleRegistry.get("RNViewShot");