Adopt the iOS UIScene lifecycle - #6179
Conversation
iOS 26 deprecates the UIApplicationDelegate window and lifecycle callbacks once an app ships a scene manifest, and Xcode 27 is expected to remove the UIDesignRequiresCompatibility opt-out we currently rely on. Move window creation, foreground/background transitions, deep links and home-screen quick actions onto a new SceneDelegate, and leave AppDelegate owning only app-wide concerns. React Native 0.79 has no scene-aware entry points, so the scene rebuilds the launch options RCTLinkingManager.getInitialURL reads from the scene connection options. Without that, a cold-launch deep link would resolve to null. Also replaces applicationIconBadgeNumber, deprecated since iOS 17.
9edd5d1 to
4119db1
Compare
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
4119db1 to
43ac2b5
Compare
📸🪓 Test evidence (verification gaps closed)
badge setbadgecount 🪓 HACK-FORCED: universal link cold launch 🪓 HACK-FORCED: quick action cold launch 🪓 HACK-FORCED: quick action warm before 🪓 HACK-FORCED: quick action warm after 🪓 HACK-FORCED: universal link warm continue Captured by the agent's in-app test run (build-and-test). |
Records what the UIScene adoption covers, the two React Native 0.79 workarounds it needed, how it was verified on the simulator, and the surfaces that still have to be normalized before Xcode 27 removes the UIDesignRequiresCompatibility opt-out.
43ac2b5 to
c05e922
Compare








CHANGELOG
Does this branch warrant an entry to the CHANGELOG?
Dependencies
none
Requirements
If you have made any visual changes to the GUI. Make sure you have:
Description
Asana: https://app.asana.com/0/1215088146871429/1214464087258597
Adopts the iOS
UIScenelifecycle, which is the first of the Track 2 items onthat task and the one that does not depend on the React Native 0.86 upgrade.
iOS 26 deprecates the
UIApplicationDelegatewindow and lifecycle callbacks oncean app ships a scene manifest, and Apple removes the
UIDesignRequiresCompatibilityopt-out in Xcode 27 (around September 2026), which is the deadline this is sized
against. The task audit flagged three deprecated call sites in
AppDelegate.swift;all three are the scene work:
UIWindow(frame: UIScreen.main.bounds),applicationDidEnterBackground, andapplicationWillEnterForeground.What changed:
ios/edge/SceneDelegate.swiftowns the window, the foreground/backgroundprivacy overlay, deep links, and home-screen quick actions.
Info.plistdeclares a single-sceneUIApplicationSceneManifest;AppDelegatevends the matching configuration and keeps app-wide duties(Sentry, Firebase, background fetch, the React Native factory).
applicationIconBadgeNumber, deprecated since iOS 17, becomesUNUserNotificationCenter.setBadgeCount, guarded for the iOS 15.6 deploymenttarget.
src/docs/ios-26-scene-lifecycle-migration.mdrecords what is still owed beforeXcode 27, with a per-surface list for the compatibility-flag removal.
Two React Native 0.79 details are worth a reviewer's attention:
RCTLinkingManagerhas no scene entry points, andgetInitialURLreads thecold-launch URL from
bridge.launchOptions. iOS stops putting deep-link keysinto
didFinishLaunchingWithOptionsonce a scene manifest exists, so the scenerebuilds those launch options from
UIScene.ConnectionOptions. Without it,Linking.getInitialURL()returns null on every cold-launch deep link andnothing crashes to signal it.
AppDelegatekeeps awindowproperty.@react-native-firebase/messagingreads
application.delegate.windowonUIApplicationDidFinishLaunchingNotification; a delegate with nowindowatall raises
doesNotRecognizeSelectorand the app aborts on launch. The scenemirrors its window into it.
react-native-contacts,react-native-shareandRN's own LogBox read the same property, so the mirror covers them too.
Out of scope here, and tracked in the doc: removing
UIDesignRequiresCompatibility(needs full-app visual QA on iOS 26) and the React Navigation upgrade (sequence it
after React Native 0.86).
Testing
Driven on an iOS 18.6 simulator against a debug build. Temporary logging and
input-forcing harnesses were used where noted, and all of it was removed before
commit;
git statusis clean of them.Driven with no forcing:
creation and the React Native start path work under the scene.
sceneDidEnterBackground; the next foreground observes theLaunchScreen privacy overlay present and removes it.
edge://...arrives withUIApplicationLaunchOptionsURLKeypopulated and the app raises its own "Unknown deep link format" banner, proving
the URL reached JS through
getInitialURL.edge://...reachesscene(_:openURLContexts:).setBadgeCountrenders the requested badge on the home-screen icon.xcodebuild -configuration Release).Driven with a forced input, because the OS cannot deliver the real trigger here:
not open under the test harness, so the real registered item (
contact_support,read back out of
UIApplication.shortcutItems) was replayed throughwindowScene(_:performActionFor:)and through the coldconnectionOptions.shortcutItempath. Both end in Safari on the action's URL.edge.appserves a 404 at/.well-known/apple-app-site-associationand Apple's CDN has no entry for thedomain, so iOS never hands the app a browsing-web activity. A synthesized
NSUserActivitywas pushed through the reallaunchOptions(from:)and throughscene(_:continue:); the app answered "Parsing link..." and "No wallets existthat support this link." respectively.
That association gap is pre-existing and unrelated to this change, but it does mean
universal links are dead app-wide until an AASA is published.
Note
Medium Risk
Changes core iOS startup, window ownership, deep-link cold launch, and background privacy behavior; regressions would be subtle (e.g. null initial URLs) without the new launch-options shim.
Overview
Moves iOS window creation and scene lifecycle off deprecated
AppDelegateAPIs by addingSceneDelegate, declaring a single-sceneUIApplicationSceneManifestinInfo.plist, and wiringAppDelegateto vend the scene configuration while keeping Sentry, Firebase, background fetch, and the React Native factory.Window, privacy overlay, deep links, and quick actions now live on the scene: React Native starts in the scene-owned window (mirrored back to
AppDelegate.windowfor Firebase and other libs that still read it), background/foreground uses the LaunchScreen privacy overlay viasceneDidEnterBackground/sceneWillEnterForeground, and linking/quick-action handlers moved from the app delegate toscene(_:openURLContexts:),scene(_:continue:), andwindowScene(_:performActionFor:). Cold-launch deep links and universal links are rebuilt into launch options fromUIScene.ConnectionOptionssoLinking.getInitialURL()still works under RN 0.79.Background-fetch badge updates switch from deprecated
applicationIconBadgeNumbertoUNUserNotificationCenter.setBadgeCountwith an iOS 15 fallback. A CHANGELOG entry andios-26-scene-lifecycle-migration.mddocument remaining Xcode 27 work (e.g. removingUIDesignRequiresCompatibility).Reviewed by Cursor Bugbot for commit c05e922. Bugbot is set up for automated code reviews on this repo. Configure here.