diff --git a/example/ios/Podfile b/example/ios/Podfile index 629ae52aa..ccc91cbdf 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -59,5 +59,13 @@ target 'QuickCryptoExample' do end end + # fmt 11.0.2 (bundled by React Native 0.81) fails under Xcode 27's clang + # consteval checks; C++17 compiles it without consteval. + installer.pods_project.targets.select { |t| t.name == 'fmt' }.each do |target| + target.build_configurations.each do |config| + config.build_settings['CLANG_CXX_LANGUAGE_STANDARD'] = 'c++17' + end + end + end end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 4cc85ff53..c28228376 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -2837,7 +2837,7 @@ SPEC CHECKSUMS: MMKVCore: 3d16ce9f7d411e135020915fde98a056859a1efa NitroMmkv: c68bb2c29c154913707bc6e66791b2b30dd723c1 NitroModules: 1dc73273d6e3aee937eec41ef3ed9470804b9884 - QuickCrypto: a720b137bf86089e3ef05abc6184f298fbc2c914 + QuickCrypto: ff693664d419b708fbb3cb8ec3f860d44b0f0328 RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669 RCTDeprecation: c4b9e2fd0ab200e3af72b013ed6113187c607077 RCTRequired: e97dd5dafc1db8094e63bc5031e0371f092ae92a @@ -2909,6 +2909,6 @@ SPEC CHECKSUMS: SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 Yoga: 11c9686a21e2cd82a094a723649d9f4507200fb0 -PODFILE CHECKSUM: fdc825234ea7a57e35b0bf4e2e0e6049b1652015 +PODFILE CHECKSUM: 2b1fa12c2fb50678fff8af81d9e99e04de1f3032 COCOAPODS: 1.15.2 diff --git a/example/ios/QuickCryptoExample/AppDelegate.swift b/example/ios/QuickCryptoExample/AppDelegate.swift index 40ff82de0..51f9baf80 100644 --- a/example/ios/QuickCryptoExample/AppDelegate.swift +++ b/example/ios/QuickCryptoExample/AppDelegate.swift @@ -21,15 +21,32 @@ class AppDelegate: UIResponder, UIApplicationDelegate { reactNativeDelegate = delegate reactNativeFactory = factory - window = UIWindow(frame: UIScreen.main.bounds) + return true + } +} + +// iOS 27 terminates apps that have not adopted the UIScene lifecycle. +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + var window: UIWindow? - factory.startReactNative( + func scene( + _ scene: UIScene, + willConnectTo session: UISceneSession, + options connectionOptions: UIScene.ConnectionOptions + ) { + guard let windowScene = scene as? UIWindowScene, + let appDelegate = UIApplication.shared.delegate as? AppDelegate + else { return } + + let window = UIWindow(windowScene: windowScene) + self.window = window + appDelegate.window = window + + appDelegate.reactNativeFactory?.startReactNative( withModuleName: "QuickCryptoExample", in: window, - launchOptions: launchOptions + launchOptions: nil ) - - return true } } diff --git a/example/ios/QuickCryptoExample/Info.plist b/example/ios/QuickCryptoExample/Info.plist index 958e5f7be..f6f325c0d 100644 --- a/example/ios/QuickCryptoExample/Info.plist +++ b/example/ios/QuickCryptoExample/Info.plist @@ -39,6 +39,23 @@ MaterialCommunityIcons.ttf + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities diff --git a/example/test/e2e/runner.mjs b/example/test/e2e/runner.mjs index 014329b94..b325a4d09 100644 --- a/example/test/e2e/runner.mjs +++ b/example/test/e2e/runner.mjs @@ -694,9 +694,28 @@ async function runSuite(device, metroPort) { owned.session, ...selector, ]); + // iOS 27 reports a non-interactive full-screen toolbar above the content, so + // agent-device refuses the semantic press; a real tap at the frame works. + const runAll = ( + await agent([ + 'get', + 'attrs', + 'id="run-all-tests-button"', + '--session', + owned.session, + ...selector, + ]) + ).data?.node; + const pressTarget = + runAll?.interactionBlocked === 'covered' && runAll.rect + ? [ + String(Math.round(runAll.rect.x + runAll.rect.width / 2)), + String(Math.round(runAll.rect.y + runAll.rect.height / 2)), + ] + : ['id="run-all-tests-button"']; await agent([ 'press', - 'id="run-all-tests-button"', + ...pressTarget, '--settle', '--timeout', '10000', diff --git a/packages/react-native-quick-crypto/QuickCrypto.podspec b/packages/react-native-quick-crypto/QuickCrypto.podspec index 8e026e553..d3dad1c60 100644 --- a/packages/react-native-quick-crypto/QuickCrypto.podspec +++ b/packages/react-native-quick-crypto/QuickCrypto.podspec @@ -1,6 +1,7 @@ require "json" require "digest" require "shellwords" +require "pathname" package = JSON.parse(File.read(File.join(__dir__, "package.json"))) @@ -203,14 +204,24 @@ Pod::Spec.new do |s| # Every translation unit calls the renamed OpenSSL symbols. Missing a rename # is a link error, never a silent bind to a foreign OpenSSL. - openssl_include = File.join(openssl_dir, "include") - force_include = "-include \"#{openssl_prefix_header}\"" + # Real paths expressed relative to the Pods sandbox keep the evaluated spec (and + # so the Podfile.lock checksum) identical across checkout locations. + pods_relative = lambda do |path| + sandbox = defined?(Pod::Config) && Pod::Config.instance.sandbox_root + next path unless sandbox + sandbox_dir = Pathname.new(sandbox.to_s) + real_sandbox = sandbox_dir.exist? ? sandbox_dir.realpath : Pathname.new(File.realpath(sandbox_dir.dirname)).join(sandbox_dir.basename) + "$(PODS_ROOT)/" + Pathname.new(File.realpath(path)).relative_path_from(real_sandbox).to_s + end + + openssl_include = pods_relative.call(File.join(openssl_dir, "include")) + force_include = "-include \"#{pods_relative.call(openssl_prefix_header)}\"" xcconfig["OTHER_CFLAGS"] = "$(inherited) #{force_include}" xcconfig["OTHER_CPLUSPLUSFLAGS"] = "$(inherited) #{force_include}" # Add cpp subdirectories to header search paths cpp_headers = [ - # Absolute path as well as the pod-relative one: in monorepos the podspec is + # Real path (relative to $(PODS_ROOT)) as well as the pod-relative one: in monorepos the podspec is # reached through a symlink but sources resolve to their real paths. "\"$(PODS_TARGET_SRCROOT)/ios/openssl/include\"", "\"#{openssl_include}\"", @@ -228,10 +239,10 @@ Pod::Spec.new do |s| ] if sodium_enabled - # Use absolute path from __dir__ which resolves symlinks correctly. + # Use the real path from __dir__, which resolves symlinks correctly. # This is necessary in monorepo setups where the podspec is accessed via symlink # but the source files are resolved to their real paths during compilation. - real_sodium_include = File.join(__dir__, "ios", "libsodium-stable", "src", "libsodium", "include") + real_sodium_include = pods_relative.call(File.join(__dir__, "ios", "libsodium-stable", "src", "libsodium", "include")) sodium_headers = [ "\"$(PODS_TARGET_SRCROOT)/ios/libsodium-stable/src/libsodium/include\"", "\"$(PODS_TARGET_SRCROOT)/ios/libsodium-stable/src/libsodium/include/sodium\"",