Example: adopt the UIScene lifecycle so it launches on iOS 27 - #31
Conversation
From iOS 27 UIKit stops an app that has not adopted the UIScene lifecycle at launch (EXC_BREAKPOINT in _UIApplicationEvaluateRuntimeIssueForNoSceneLifecycleAdoption). BareExample, from the React Native template, declared no scene and trapped before its JS ran on the iOS 27 simulators. Info.plist now declares one window scene, and SceneDelegate creates the window from it and starts React Native there. The factory stays in AppDelegate, which keeps the window too, for code that reads the app delegate's window. launch e2e passes on an iOS 27.0 simulator, where the app trapped before, and on iOS 26.5. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Stale comment
Deep review (6f9469d)
BareExample has to adopt UIScene because a build with the iOS 27 SDK is stopped at launch (
_UIApplicationEvaluateRuntimeIssueForNoSceneLifecycleAdoption) if it has not. That matches TN3187 and the RN 0.87 template still shippingUIWindow(frame: UIScreen.main.bounds)fromAppDelegate.The split in this PR is the right 0.87-shaped workaround:
RCTReactNativeFactoryis still created inapplication(_:didFinishLaunchingWithOptions:)— UIKit calls that beforescene(_:willConnectTo:options:), so the factory exists when the scene connects.- The window is
UIWindow(windowScene:), notUIScreen.main.bounds, andstartReactNativeis what callsmakeKeyAndVisible(RN 0.87.1RCTReactNativeFactory.mm).appDelegate.windowis assigned before start. RN 0.87RCTDeviceInfo/ other native code still reads that property; keeping it avoids the nil-window crash class of facebook/react-native#53645.Info.plistis a single scene (UIApplicationSupportsMultipleScenesfalse) with$(PRODUCT_MODULE_NAME).SceneDelegate.PRODUCT_NAMEisBareExample; the same plist already expands$(EXECUTABLE_NAME)/$(PRODUCT_BUNDLE_IDENTIFIER), so the class name expands the same way. Apple does not requireapplication(_:configurationForConnecting:)when the manifest is complete.SceneDelegatelives inAppDelegate.swift, which is already in the Compile Sources phase, so the Xcode project does not need a new file reference.I walked the callers this change actually hits:
App.tsxchooseScenario()(URI first, then iOSSettingslaunch args, then JSON),e2e/scenario.ts(iOS has no URL scheme; every iOS launch passes-bugseeE2e…),BGSRNSdkWalkedWindows(already branches onUIApplicationSceneManifestand will now take the scene-windows path, which is what the Cocoa SDK walks), andBGSRNSdkKeyWindow(already iteratesconnectedScenes). Deployment target is 15.1;UIWindow(windowScene:)is iOS 13.No P0–P3 with a current failure path.
Looked at and not raised
launchOptions: niland unusedconnectionOptions. This is a real delta versus the oldAppDelegate(which forwarded UIKit’s launch options intoRCTHost) and versus template#251. It does not break this app today: iOS e2e steers throughNSUserDefaultsvolatile args /Settings.get('bugseeE2e…'), and the example registers no URL scheme, soLinking.getInitialURL()is already nil. MappingconnectionOptions.urlContextsstill would not make cold-start links work on 0.87 — that needs the scene Linking APIs that only landed for 0.88 (cipolleschi’s reason for closing template#251). Residual if this example later grows an iOS URI channel.
startReactNativeon everywillConnectTo.RCTRootViewFactory.viewWithModuleNamereuses the host but always creates a new Fabric surface. A scene disconnect/reconnect in-process (memory pressure,SupportsMultipleScenesstill false) would remountAppand callBugsee.launch()again. Template#251 has the same shape; 0.88RCTSceneDelegateis the real fix. The e2e terminates the process between runs.Cocoa 7.0.0-beta3 screenshot flake. Author already recorded 2/11
secure-componentruns with no screenshot (SDK drops it when it sees no key window, or a protected ancestor animating).view-treeandlaunchpassed on iOS 27 and 26.5. This bootstrap is the correct scene window (makeKeyAndVisibleon awindowScenewindow); if beta3 still missesUIApplication.keyWindow, that is an SDK follow-up, not a reason to keep the pre-sceneAppDelegatewindow (that path does not launch on iOS 27).Did not run Xcode or device e2e from this Linux review host. Author reports CocoaPods simulator build +
launchon iOS 27.0 and 26.5. CI is in flight (ios e2e (simulator)pending at review time).
- Overall risk: Low
- Merge recommendation: Safe to merge
- Most important issues to fix: None in this PR. Residual: 0.87 has no scene-aware Linking (accepted); watch Cocoa beta3 key-window screenshots on iOS 27; SPM path is the same sources, CI will cover it.
- Positives: Factory stays in
didFinishLaunching(better than creating it in the scene delegate and hoping); window is scene-attached;delegate.windowis kept for RN 0.87; single-scene manifest matches the iPhone e2e; the RN wrapper’s window walk was already written for a scene manifest.Sent by Cursor Automation: Bugsee code review
SceneDelegate starts React Native with no launch options and implements no scene(_:openURLContexts:), so Linking sees no URL on iOS. That is deliberate: the app registers no URL scheme, and the e2e steers its iOS launches through launch arguments. The comment says so, and names RCTLinkingManager for an app that does open URLs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Stale comment
Re-review (e0dd777)
e0dd777is documentation only. Production path is unchanged from 6f9469d: factory indidFinishLaunching, scene-attached window,startReactNativewithlaunchOptions: nil,appDelegate.windowmirrored, single-scene manifest.The new SceneDelegate comment matches the code and the e2e:
- No
connectionOptions.urlContextsmapping and noscene(_:openURLContexts:)— soLinkingstays empty on iOS.- The app still has no
CFBundleURLTypes;e2e/scenario.tssteers iOS through-bugseeE2e…launch arguments /Settings.- Pointing a URL-using app at
RCTLinkingManageris the right 0.87 bridge (feed the existing AppDelegate-shaped APIs from the scene callbacks). 0.88 is what adds first-class scene methods.No new P0–P3. Prior residual still residual (0.87 has no scene Linking; unguarded reconnect surface; Cocoa beta3 screenshot flake).
- Overall risk: Low
- Merge recommendation: Safe to merge
- Most important issues to fix: None
- Positives: The previously implicit “we drop launch URLs” choice is now stated next to
launchOptions: nil, which is what a later reader needs.Sent by Cursor Automation: Bugsee code review
There was a problem hiding this comment.
Deep review (e0dd777)
BareExample has to adopt UIScene: a binary built with the iOS 27 SDK is stopped at launch (_UIApplicationEvaluateRuntimeIssueForNoSceneLifecycleAdoption) if it has not. That matches TN3187. The 0.87 template this app came from still creates UIWindow(frame: UIScreen.main.bounds) in AppDelegate.
The split is the right 0.87-shaped workaround (0.88’s RCTSceneDelegate / connectionOptions: are not in this tree; cipolleschi closed template#251 for that reason):
RCTReactNativeFactoryis still created inapplication(_:didFinishLaunchingWithOptions:). UIKit calls that beforescene(_:willConnectTo:options:), so the factory exists when the scene connects.- The window is
UIWindow(windowScene:), notUIScreen.main.bounds. RN 0.87.1RCTReactNativeFactory.mmstartReactNativeWithModuleName:inWindow:…assignsrootViewControllerand callsmakeKeyAndVisible. appDelegate.windowis assigned before start. 0.87.1RCTDeviceInfoalready goes throughRCTKeyWindow()(the #53645 crash class), but keeping the property still covers anything that KVC-readsUIApplication.shared.delegate.window(the unrecognized-selector failure in facebook/react-native#53602 if the property were removed).Info.plistis a single scene (UIApplicationSupportsMultipleScenesfalse) with$(PRODUCT_MODULE_NAME).SceneDelegate.PRODUCT_NAMEisBareExample; the same plist already expands$(EXECUTABLE_NAME)/$(PRODUCT_BUNDLE_IDENTIFIER), so the class name expands the same way. Apple does not requireapplication(_:configurationForConnecting:)when the manifest is complete.SceneDelegatelives inAppDelegate.swift, already in Compile Sources.
Callers this change actually hits:
App.tsxchooseScenario()— URI first, then iOSSettingslaunch args, then JSON.e2e/scenario.ts— iOS has no URL scheme; every iOS launch passes-bugseeE2e….RCT_jsLocationis also an NSUserDefaults arg, notlaunchOptions.BGSRNSdkWalkedWindowsalready branches onUIApplicationSceneManifestand will now takekeyWindow.windowScene.windows, which is the Cocoa SDK’s walk.BGSRNSdkKeyWindowalready iteratesconnectedScenes.- Deployment target 15.1;
UIWindow(windowScene:)is iOS 13.
e0dd777 is comment-only. It records why launchOptions is nil and why there is no scene(_:openURLContexts:). That matches the app: no CFBundleURLTypes, iOS e2e never uses Linking.
No P0–P3 with a current failure path.
Looked at and not raised
launchOptions: nil / unused connectionOptions. Real delta versus the old AppDelegate (which forwarded UIKit’s launch options into RCTHost). It does not break this app: iOS e2e steers through the volatile argument domain / Settings.get('bugseeE2e…'), and Linking.getInitialURL() was already nil (no scheme). Mapping connectionOptions.urlContexts still would not make cold-start links work on 0.87 — that needs the scene Linking APIs that landed in 0.88. Residual only if this example later grows an iOS URI channel; the new comment says so.
startReactNative on every willConnectTo. RCTRootViewFactory.viewWithModuleName reuses the host but always creates a new Fabric surface. A scene disconnect/reconnect in-process would remount App and call Bugsee.launch() again. Template#251 has the same shape. The e2e terminates the process between runs (simctl launch --terminate-running-process / devicectl --terminate-existing).
Cocoa 7.0.0-beta3 screenshot flake. Author recorded 2/11 secure-component runs with no screenshot (SDK drops it when it sees no key window, or a protected ancestor animating). view-tree and launch passed on iOS 27 and 26.5. This bootstrap is the correct scene window (makeKeyAndVisible on a windowScene window). If beta3 still misses UIApplication.keyWindow, that is an SDK follow-up — not a reason to keep the pre-scene AppDelegate window, which does not launch on iOS 27.
Did not run Xcode or device e2e from this Linux review host. Author reports CocoaPods simulator build + launch on iOS 27.0 and 26.5. CI at review time: RN compat jobs green; ios e2e (simulator), CocoaPods, SPM, Android, and lint still pending. GitHub macos-15 picks the first available iPhone iOS simulator, which is not an iOS 27 proof.
- Overall risk: Low
- Merge recommendation: Safe to merge
- Most important issues to fix: None in this PR. Residual: 0.87 has no scene-aware Linking (accepted and now commented); watch Cocoa beta3 key-window screenshots on iOS 27; SPM path is the same sources.
- Positives: Factory stays in
didFinishLaunching; window is scene-attached;delegate.windowis kept; single-scene manifest matches the iPhone e2e; the wrapper’s window walk was already written for a scene manifest, so adopting one makes the example exercise the SDK’s real path; the docs commit is precise about why URLs are not forwarded.
Sent by Cursor Automation: Bugsee code review


Summary
EXC_BREAKPOINTin_UIApplicationEvaluateRuntimeIssueForNoSceneLifecycleAdoption). BareExample, from the React Native template, declared no scene and trapped before its JS ran on the iOS 27 simulators.Info.plistdeclares one window scene.SceneDelegatecreates the window from it and starts React Native there; the factory stays inAppDelegate, which keeps the window too for code that reads the app delegate's window.Test plan
launchon an iOS 27.0 simulator (trapped before) and on iOS 26.5launchandview-treepass on iOS 27 and 26.5.secure-componentwith 7.0.0-beta3 passed 9 of 11 runs; in 2 early runs the report came without a screenshot (beta3 drops it when it finds no key window, or sees an ancestor of a protected view animating for about a second). Not reproduced on a fresh simulator; the cause is unknown🤖 Generated with Claude Code