From 3040ff62f2f66339e2e8f89788db4847b60bcd55 Mon Sep 17 00:00:00 2001 From: Saurabh Chavan Date: Wed, 14 Oct 2026 15:39:49 +0530 Subject: [PATCH 1/4] fix: update the firebase BOM and versions --- .mx/known_dependencies.json | 6 +++--- android/build.gradle | 9 +++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.mx/known_dependencies.json b/.mx/known_dependencies.json index ea74c9c72..6be9983b8 100644 --- a/.mx/known_dependencies.json +++ b/.mx/known_dependencies.json @@ -1,7 +1,7 @@ { - "@react-native-firebase/app": "20.1.0", - "@react-native-firebase/messaging": "20.1.0", - "@react-native-firebase/crashlytics": "20.1.0", + "@react-native-firebase/app": "21.14.0", + "@react-native-firebase/messaging": "21.14.0", + "@react-native-firebase/crashlytics": "21.14.0", "react-native-notify-kit": "10.4.7", "react-native-maps": "1.20.1" } diff --git a/android/build.gradle b/android/build.gradle index e08132800..39843172b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -44,13 +44,10 @@ buildscript { project.ext { set('react-native', [ versions: [ - // Override Firebase BOM version to avoid updating to react-native-firebase 20.2.0+, - // which adds many new node_modules increasing the size significantly. - // Affected dependencies include the entire react-native-firebase suite e.g.: - // @react-native-firebase/app, @react-native-firebase/analytics, @react-native-firebase/crashlytics, @react-native-firebase/messaging - // NOTE: If updating to react-native-firebase 20.2.0+ is done, this override should be removed. + // Firebase BOM used by react-native-firebase and android/app/build.gradle (firebaseBomVersion). + // Keep in sync with sdkVersions.android.firebase of the @react-native-firebase/app version in .mx/known_dependencies.json. firebase: [ - bom: "33.1.1" + bom: "33.12.0" ], ], ]) From de78be02d3beddf0b54d966cc21179b68546a059 Mon Sep 17 00:00:00 2001 From: Saurabh Chavan Date: Wed, 14 Oct 2026 17:31:23 +0530 Subject: [PATCH 2/4] fix: changelog updated --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 703536b0d..2dea029b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +- We updated react-native-firebase (`@react-native-firebase/app`, `@react-native-firebase/messaging` and `@react-native-firebase/crashlytics`) from v20.1.0 to v21.14.0, which uses Firebase iOS SDK v11.11.0. This resolves a CocoaPods dependency conflict with react-native-vision-camera-barcode-scanner v5 (GoogleMLKit v9.0.0) on iOS. +- We updated the Android Firebase BoM from v33.1.1 to v33.12.0 to match react-native-firebase v21.14.0. + ## [19.1.7] - 2026-09-23 - We removed some unused Android Permissions coming in from Manifest mergers. From 57535b342e4e50dc2658f2d746ea6da6e1078558 Mon Sep 17 00:00:00 2001 From: Saurabh Chavan Date: Wed, 21 Oct 2026 11:39:27 +0530 Subject: [PATCH 3/4] fix: update the changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dea029b1..feee93b1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Changes + - We updated react-native-firebase (`@react-native-firebase/app`, `@react-native-firebase/messaging` and `@react-native-firebase/crashlytics`) from v20.1.0 to v21.14.0, which uses Firebase iOS SDK v11.11.0. This resolves a CocoaPods dependency conflict with react-native-vision-camera-barcode-scanner v5 (GoogleMLKit v9.0.0) on iOS. - We updated the Android Firebase BoM from v33.1.1 to v33.12.0 to match react-native-firebase v21.14.0. +- We made this Native Template version the minimum required version for Barcode Scanner widget v4.6.0 or above (Native Mobile Resources version 12.6.0 or above) on iOS when push notifications are enabled. Older Native Template versions will fail to build in this setup. ## [19.1.7] - 2026-09-23 From 968f7967d2a82075cd5e738857fd3b0004c320a1 Mon Sep 17 00:00:00 2001 From: Saurabh Chavan Date: Thu, 22 Oct 2026 12:50:33 +0530 Subject: [PATCH 4/4] fix: added mlkit support for arm64 simulators --- ios/Podfile | 4 + ios/mlkit_simulator_support.rb | 170 +++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 ios/mlkit_simulator_support.rb diff --git a/ios/Podfile b/ios/Podfile index 30b56d1bf..0889b2f35 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -24,6 +24,7 @@ node_require('react-native/scripts/react_native_pods.rb') node_require('react-native-permissions/scripts/setup.rb') require_relative './mendix_utils' +require_relative './mlkit_simulator_support' deployment_target = "15.5" platform :ios, deployment_target @@ -155,4 +156,7 @@ post_install do |installer| end end + + # ML Kit has no arm64 simulator slice; see mlkit_simulator_support.rb. + MLKitSimulatorSupport.apply(installer) end diff --git a/ios/mlkit_simulator_support.rb b/ios/mlkit_simulator_support.rb new file mode 100644 index 000000000..8319ae059 --- /dev/null +++ b/ios/mlkit_simulator_support.rb @@ -0,0 +1,170 @@ +# Google ML Kit (pulled in by react-native-vision-camera-barcode-scanner) ships fat static +# frameworks with only an x86_64 simulator slice and an arm64 *device* slice. Linking an arm64 +# simulator build (Apple Silicon) fails with: +# building for 'iOS-simulator', but linking in object file ... built for 'iOS' +# +# A fat binary can't hold two arm64 slices, so we keep the original frameworks untouched for +# device builds and archives, and create a copy next to them ("Frameworks-simulator") whose arm64 +# objects are re-tagged as iOS-simulator (LC_BUILD_VERSION platform 2 -> 7). The generated +# xcconfigs then point FRAMEWORK_SEARCH_PATHS at "$(MLKIT_FRAMEWORKS_DIR)", which only resolves to +# the copy for sdk=iphonesimulator*, so only simulator builds ever link the patched binaries. +# +# The ML Kit podspecs also set EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64 (forcing Rosetta), +# which leaves the Pods aggregate target with no architectures on an arm64 simulator, so that +# arm64 exclusion is dropped as well unless another pod asks for it too. +require 'fileutils' + +module MLKitSimulatorSupport + POD_NAME_PATTERN = /\A(GoogleMLKit|MLKit|MLImage)/ + SIMULATOR_FRAMEWORKS_DIR = "Frameworks-simulator" + SIMULATOR_EXCLUDED_ARCHS = "EXCLUDED_ARCHS[sdk=iphonesimulator*]" + FRAMEWORKS_DIR_SETTING = "MLKIT_FRAMEWORKS_DIR" + + FAT_MAGIC = 0xcafebabe + MH_MAGIC_64 = 0xfeedfacf + CPU_TYPE_ARM64 = 0x0100000c + LC_BUILD_VERSION = 0x32 + PLATFORM_IOS = 2 + PLATFORM_IOSSIMULATOR = 7 + + module_function + + def apply(installer) + pods_root = installer.sandbox.root + pods = installer.pod_targets.map(&:pod_name).uniq.select { |name| name =~ POD_NAME_PATTERN } + pods = pods.select { |pod| create_simulator_frameworks(pods_root + pod) } + return if pods.empty? + + Pod::UI.puts "Adding arm64 simulator support for #{pods.join(', ')}".green + include_arm64 = !other_pods_exclude_arm64?(installer) + xcconfig_paths(installer).each { |path| update_xcconfig(path, pods, include_arm64) } + end + + def other_pods_exclude_arm64?(installer) + others = installer.pod_targets.reject { |target| target.pod_name =~ POD_NAME_PATTERN } + names = others.select do |target| + target.spec_consumers.any? do |consumer| + [consumer.user_target_xcconfig, consumer.pod_target_xcconfig].any? do |xcconfig| + xcconfig[SIMULATOR_EXCLUDED_ARCHS].to_s.split.include?("arm64") + end + end + end.map(&:pod_name) + return false if names.empty? + + Pod::UI.warn "#{names.join(', ')} exclude arm64 for the simulator, so arm64 simulator builds will not work." + true + end + + def create_simulator_frameworks(pod_dir) + frameworks_dir = pod_dir + "Frameworks" + return false unless frameworks_dir.directory? + + simulator_dir = pod_dir + SIMULATOR_FRAMEWORKS_DIR + FileUtils.rm_rf(simulator_dir) + FileUtils.mkdir_p(simulator_dir) + Dir.glob(frameworks_dir + "*.framework").each do |framework| + FileUtils.cp_r(framework, simulator_dir) + name = File.basename(framework, ".framework") + patch_binary(simulator_dir + "#{name}.framework" + name) + end + true + end + + def xcconfig_paths(installer) + aggregate = installer.aggregate_targets.flat_map do |target| + target.xcconfigs.keys.map { |variant| target.xcconfig_path(variant) } + end + pod_targets = installer.pod_targets.flat_map do |target| + target.build_settings.keys.map { |variant| target.xcconfig_path(variant) } + end + (aggregate + pod_targets).uniq.select { |path| File.exist?(path) } + end + + def update_xcconfig(path, pods, include_arm64) + original = File.read(path) + lines = original.lines.reject { |line| line.start_with?(FRAMEWORKS_DIR_SETTING) } + + search_paths = lines.index { |line| line.start_with?("FRAMEWORK_SEARCH_PATHS =") } + if search_paths + line = pods.reduce(lines[search_paths]) do |value, pod| + value.gsub("\"${PODS_ROOT}/#{pod}/Frameworks\"", "\"${PODS_ROOT}/#{pod}/$(#{FRAMEWORKS_DIR_SETTING})\"") + end + if line != lines[search_paths] + lines[search_paths] = line + lines << "\n" unless lines.last.end_with?("\n") + lines << "#{FRAMEWORKS_DIR_SETTING} = Frameworks\n" + lines << "#{FRAMEWORKS_DIR_SETTING}[sdk=iphonesimulator*] = #{SIMULATOR_FRAMEWORKS_DIR}\n" + end + end + + if include_arm64 + lines = lines.map do |line| + next line unless line.start_with?("#{SIMULATOR_EXCLUDED_ARCHS} =") + + archs = line.delete_prefix("#{SIMULATOR_EXCLUDED_ARCHS} =").split - ["arm64"] + archs.empty? ? nil : "#{SIMULATOR_EXCLUDED_ARCHS} = #{archs.join(' ')}\n" + end.compact + end + + File.write(path, lines.join) if lines.join != original + end + + def patch_binary(path) + buf = File.binread(path) + patched = 0 + if be32(buf, 0) == FAT_MAGIC + be32(buf, 4).times do |i| + arch = 8 + i * 20 + next unless be32(buf, arch) == CPU_TYPE_ARM64 + + start = be32(buf, arch + 8) + patched += patch_slice(buf, start, start + be32(buf, arch + 12)) + end + else + patched += patch_slice(buf, 0, buf.bytesize) + end + File.binwrite(path, buf) if patched > 0 + end + + def patch_slice(buf, start, finish) + buf.byteslice(start, 8) == "!\n" ? patch_archive(buf, start, finish) : patch_mach_o(buf, start) + end + + def patch_archive(buf, start, finish) + offset = start + 8 + patched = 0 + while offset + 60 <= finish + name = buf.byteslice(offset, 16).strip + size = buf.byteslice(offset + 48, 10).strip.to_i + data = offset + 60 + # BSD long names: "#1/" means the name is stored at the start of the member data. + data += name[3..].to_i if name.start_with?("#1/") + patched += patch_mach_o(buf, data) + offset += 60 + size + (size % 2) + end + patched + end + + def be32(buf, offset) + buf.byteslice(offset, 4).unpack("N").first + end + + def le32(buf, offset) + buf.byteslice(offset, 4).unpack("V").first + end + + def patch_mach_o(buf, offset) + return 0 unless le32(buf, offset) == MH_MAGIC_64 && le32(buf, offset + 4) == CPU_TYPE_ARM64 + + cmd_offset = offset + 32 + patched = 0 + le32(buf, offset + 16).times do + if le32(buf, cmd_offset) == LC_BUILD_VERSION && le32(buf, cmd_offset + 8) == PLATFORM_IOS + buf[cmd_offset + 8, 4] = [PLATFORM_IOSSIMULATOR].pack("V") + patched += 1 + end + cmd_offset += le32(buf, cmd_offset + 4) + end + patched + end +end