diff --git a/Apps/Playground/ios/Podfile b/Apps/Playground/ios/Podfile index eac32f246..65dddb0d0 100644 --- a/Apps/Playground/ios/Podfile +++ b/Apps/Playground/ios/Podfile @@ -1,6 +1,10 @@ require_relative '../node_modules/react-native-test-app/test_app' require_relative '../node_modules/react-native-permissions/scripts/setup' +# Match the @babylonjs/react-native pod's iOS 16 minimum (required for Xcode 26 +# builds, where the CMake-generated framework targets iOS 16 by default). +platform :ios, '16.0' + workspace 'Playground.xcworkspace' # react-native-permissions diff --git a/Modules/@babylonjs/react-native/README.md b/Modules/@babylonjs/react-native/README.md index 95a36e612..adca4f33f 100644 --- a/Modules/@babylonjs/react-native/README.md +++ b/Modules/@babylonjs/react-native/README.md @@ -16,7 +16,7 @@ The minimum Android SDK version is 21. This must be set as `minSdkVersion` in th ### iOS Configuration -The minimum deployment target version is 12. This must be set as `iOS Deployment Target` in the consuming project's `project.pbxproj`, and must also be set as `platform` in the consuming project's `Podfile`. +The minimum deployment target version is 16. This must be set as `iOS Deployment Target` in the consuming project's `project.pbxproj`, and must also be set as `platform` in the consuming project's `Podfile`. Make sure `pod install` is called from the ios folder after npm install. #### Workspace @@ -39,6 +39,12 @@ To use the system cmake (e.g. a Homebrew or system install) instead of the cmake export BABYLON_USE_SYSTEM_CMAKE=1 ``` +To override the iOS deployment target used by the generated `ReactNativeBabylon.xcodeproj` (defaults to `16.0`), set this variable before running `npm install` so it matches your app's Podfile `platform :ios` value: +``` +export BABYLON_IOS_DEPLOYMENT_TARGET=16.0 +``` +The previous default of iOS 12 caused Xcode 26's Clang to crash while compiling `DeviceImpl_iOS.mm`; the default is now `16.0` and this variable lets you raise it further (or, at your own risk, lower it) without patching the deployment target from a Podfile `post_install` hook. + ### Plugins selection Plugins can be disabled at build time. They are all enabled by default and disabling is done with environment variables: diff --git a/Modules/@babylonjs/react-native/android/build.gradle b/Modules/@babylonjs/react-native/android/build.gradle index 8296eaeff..52894ac97 100644 --- a/Modules/@babylonjs/react-native/android/build.gradle +++ b/Modules/@babylonjs/react-native/android/build.gradle @@ -66,6 +66,15 @@ def graphics_api = safeExtGet('GRAPHICS_API', "OpenGL") def rootBuildDir = "${rootProject.rootDir}/../Build/Android" def extractedLibDir = "${rootBuildDir}/lib" +// ARCore version. Consumers can override by setting ext.arcoreVersion in their +// root build.gradle (e.g. `ext.arcoreVersion = '1.26.0'` to resolve the +// duplicate-class conflict between com.google.ar:core:1.22 and +// com.android.installreferrer:installreferrer:2.2). A consumer's +// resolutionStrategy.force on com.google.ar:core also wins over this value, +// in which case the dynamic ARCORE_LIBPATH resolution below still picks up +// the actually-extracted AAR. +def arcoreVersion = safeExtGet('arcoreVersion', '1.22.0') + def BABYLON_NATIVE_PLUGIN_NATIVECAMERA = System.getenv("BABYLON_NATIVE_PLUGIN_NATIVECAMERA") != "0" def BABYLON_NATIVE_PLUGIN_NATIVEXR = System.getenv("BABYLON_NATIVE_PLUGIN_NATIVEXR") != "0" @@ -168,10 +177,17 @@ android { cmake { cppFlags "-fexceptions", "-frtti", "-std=c++1y", "-DONANDROID" abiFilters (*reactNativeArchitectures()) + // NOTE: -DARCORE_LIBPATH below points at the AAR matching the + // declared `arcoreVersion` (see top of this file). The actual + // resolved ARCore version may differ (consumer + // resolutionStrategy.force, dependency conflict resolution), + // so the argument is rewritten further down in this script + // (search for "arcoreAarFileName") to point at the AAR that + // Gradle actually extracted. arguments "-DANDROID_STL=c++_shared", "-DGRAPHICS_API=${graphics_api}", "-DREACT_NATIVE_VERSION=${REACT_NATIVE_VERSION}", - "-DARCORE_LIBPATH=${extractedLibDir}/core-1.22.0.aar/jni", + "-DARCORE_LIBPATH=${extractedLibDir}/core-${arcoreVersion}.aar/jni", "-DREACTNATIVE_DIR=${rootDir}/../node_modules/react-native/", "-DBABYLON_NATIVE_PLUGIN_NATIVECAMERA=${BABYLON_NATIVE_PLUGIN_NATIVECAMERA ? '1' : '0'}", "-DBABYLON_NATIVE_PLUGIN_NATIVEXR=${BABYLON_NATIVE_PLUGIN_NATIVEXR ? '1' : '0'}", @@ -286,9 +302,9 @@ repositories { dependencies { //noinspection GradleDynamicVersion implementation 'com.facebook.react:react-native:+' // From node_modules - implementation 'com.google.ar:core:1.22.0' + implementation "com.google.ar:core:${arcoreVersion}" - extractLibs 'com.google.ar:core:1.22.0' + extractLibs "com.google.ar:core:${arcoreVersion}" if (REACT_NATIVE_VERSION < 71) { logger.warn("BabylonReactNative: Extracting files from AAR (pre RN 0.71)") @@ -388,6 +404,19 @@ def nativeBuildDependsOn(dependsOnTask, variant) { buildTasks.forEach { task -> task.dependsOn(dependsOnTask) } } +// Resolve the actual ARCore AAR name. A consumer project may override +// `ext.arcoreVersion` or apply a resolutionStrategy.force to com.google.ar:core +// (e.g. to resolve a conflict with installreferrer 2.2 that requires +// com.google.ar:core 1.26+). Gradle extracts the AAR into a folder named after +// the *resolved* artifact (core-.aar), so the native build's +// -DARCORE_LIBPATH argument must match that resolved name rather than the +// version literal declared in the dependencies block. +def arcoreAar = configurations.extractLibs.resolvedConfiguration.resolvedArtifacts.find { it.name == 'core' } +def arcoreAarName = arcoreAar ? "${arcoreAar.name}-${arcoreAar.moduleVersion.id.version}.aar" : "core-${arcoreVersion}.aar" +android.defaultConfig.externalNativeBuild.cmake.arguments.replaceAll { + it.toString().startsWith('-DARCORE_LIBPATH=') ? "-DARCORE_LIBPATH=${extractedLibDir}/${arcoreAarName}/jni".toString() : it +} + configurations.extractLibs.files.each { file -> copy { from zipTree(file) diff --git a/Modules/@babylonjs/react-native/ios/CMakeLists.txt b/Modules/@babylonjs/react-native/ios/CMakeLists.txt index f23cc4068..7e3b08cd3 100644 --- a/Modules/@babylonjs/react-native/ios/CMakeLists.txt +++ b/Modules/@babylonjs/react-native/ios/CMakeLists.txt @@ -9,7 +9,23 @@ FetchContent_Declare(ios-cmake FetchContent_MakeAvailable(ios-cmake) set(CMAKE_TOOLCHAIN_FILE "${ios-cmake_SOURCE_DIR}/ios.toolchain.cmake" CACHE PATH "") set(PLATFORM "OS64COMBINED" CACHE STRING "") -set(DEPLOYMENT_TARGET "12" CACHE STRING "") + +# Allow the consuming project to override the iOS deployment target used when +# generating the Xcode project. Resolution order: +# 1. -DDEPLOYMENT_TARGET= on the cmake command line (highest priority) +# 2. BABYLON_IOS_DEPLOYMENT_TARGET environment variable (forwarded by +# postinstall.js so consumers can set it before `npm install`) +# 3. 16.0 (default). iOS 12 used to be the default, but it is known to crash +# Xcode 26's Clang while compiling DeviceImpl_iOS.mm; iOS 16 is now the +# supported minimum. +if(NOT DEFINED DEPLOYMENT_TARGET OR "${DEPLOYMENT_TARGET}" STREQUAL "") + if(DEFINED ENV{BABYLON_IOS_DEPLOYMENT_TARGET} AND NOT "$ENV{BABYLON_IOS_DEPLOYMENT_TARGET}" STREQUAL "") + set(DEPLOYMENT_TARGET "$ENV{BABYLON_IOS_DEPLOYMENT_TARGET}" CACHE STRING "iOS deployment target") + else() + set(DEPLOYMENT_TARGET "16" CACHE STRING "iOS deployment target") + endif() +endif() +message(STATUS "BabylonReactNative iOS DEPLOYMENT_TARGET: ${DEPLOYMENT_TARGET}") set(ENABLE_ARC OFF CACHE STRING "Enables or disables ARC support.") set(ENABLE_PCH OFF CACHE STRING "Enables or disables precompiled headers.") set(CMAKE_XCODE_GENERATE_SCHEME ON) @@ -91,7 +107,7 @@ target_link_libraries(BabylonNative # TODO: For some reason these don't work, so we specify these in the CMake command line args. set_target_properties(BabylonNative PROPERTIES XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC NO - XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET 12.0 + XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET ${DEPLOYMENT_TARGET} XCODE_ATTRIBUTE_ENABLE_BITCODE YES XCODE_GENERATE_SCHEME ON ) diff --git a/Modules/@babylonjs/react-native/postinstall.js b/Modules/@babylonjs/react-native/postinstall.js index a7da65d08..8c5591f92 100644 --- a/Modules/@babylonjs/react-native/postinstall.js +++ b/Modules/@babylonjs/react-native/postinstall.js @@ -24,11 +24,23 @@ function getCmakeExecutable() { function iosCMake() { const { spawn } = require('child_process'); - const cmake = spawn(getCmakeExecutable(), [ + const cmakeArgs = [ '-S', path.join(__dirname, 'ios'), '-B', path.join(__dirname, 'Build/iOS'), '-G', 'Xcode', - ], { stdio: 'inherit', cwd: __dirname }); + ]; + + // Allow the consuming project to override the iOS deployment target used + // when generating the Xcode project. The CMakeLists.txt default is 16.0 + // (iOS 12 used to be the default but is known to crash Xcode 26's Clang + // when compiling DeviceImpl_iOS.mm); set this if you need a different + // minimum to match your Podfile `platform :ios` value. + const deploymentTarget = process.env.BABYLON_IOS_DEPLOYMENT_TARGET; + if (deploymentTarget && deploymentTarget.trim() !== '') { + cmakeArgs.push(`-DDEPLOYMENT_TARGET=${deploymentTarget.trim()}`); + } + + const cmake = spawn(getCmakeExecutable(), cmakeArgs, { stdio: 'inherit', cwd: __dirname }); cmake.on('exit', code => { if (code !== 0) { diff --git a/Modules/@babylonjs/react-native/react-native-babylon.podspec b/Modules/@babylonjs/react-native/react-native-babylon.podspec index f18ea17fc..c1e80889d 100644 --- a/Modules/@babylonjs/react-native/react-native-babylon.podspec +++ b/Modules/@babylonjs/react-native/react-native-babylon.podspec @@ -54,7 +54,7 @@ Pod::Spec.new do |s| s.license = package["license"] s.authors = package["author"] - s.platforms = { :ios => "12.0" } + s.platforms = { :ios => "16.0" } s.source = { :git => package["repository"]["url"], :tag => s.version } s.source_files = "ios/*.{h,m,mm}"