Skip to content

Example: adopt the UIScene lifecycle so it launches on iOS 27 - #31

Merged
krassx merged 2 commits into
mainfrom
fix/example-uiscene-lifecycle
Oct 2, 2026
Merged

krassx merged 2 commits into
mainfrom
fix/example-uiscene-lifecycle

Conversation

@dsheikherev

Copy link
Copy Markdown
Contributor

Summary

  • 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 declares one window scene. 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.

Test plan

  • CocoaPods build for the simulator
  • e2e launch on an iOS 27.0 simulator (trapped before) and on iOS 26.5
  • With iOS: keep secure rectangles and the view tree on windows away from the screen's origin #30: launch and view-tree pass on iOS 27 and 26.5. secure-component with 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
  • SPM delivery build with this change (not run)
  • CI

🤖 Generated with Claude Code

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>

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 shipping UIWindow(frame: UIScreen.main.bounds) from AppDelegate.

The split in this PR is the right 0.87-shaped workaround:

  • RCTReactNativeFactory is still created in application(_:didFinishLaunchingWithOptions:) — UIKit calls that before scene(_:willConnectTo:options:), so the factory exists when the scene connects.
  • The window is UIWindow(windowScene:), not UIScreen.main.bounds, and startReactNative is what calls makeKeyAndVisible (RN 0.87.1 RCTReactNativeFactory.mm).
  • appDelegate.window is assigned before start. RN 0.87 RCTDeviceInfo / other native code still reads that property; keeping it avoids the nil-window crash class of facebook/react-native#53645.
  • Info.plist is a single scene (UIApplicationSupportsMultipleScenes false) with $(PRODUCT_MODULE_NAME).SceneDelegate. PRODUCT_NAME is BareExample; the same plist already expands $(EXECUTABLE_NAME) / $(PRODUCT_BUNDLE_IDENTIFIER), so the class name expands the same way. Apple does not require application(_:configurationForConnecting:) when the manifest is complete.
  • SceneDelegate lives in AppDelegate.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.tsx chooseScenario() (URI first, then iOS Settings launch args, then JSON), e2e/scenario.ts (iOS has no URL scheme; every iOS launch passes -bugseeE2e…), BGSRNSdkWalkedWindows (already branches on UIApplicationSceneManifest and will now take the scene-windows path, which is what the Cocoa SDK walks), and BGSRNSdkKeyWindow (already iterates connectedScenes). Deployment target is 15.1; UIWindow(windowScene:) is iOS 13.

No P0–P3 with a current failure path.

Looked at and not raised

launchOptions: nil and unused connectionOptions. This is a real delta versus the old AppDelegate (which forwarded UIKit’s launch options into RCTHost) and versus template#251. It does not break this app today: iOS e2e steers through NSUserDefaults volatile args / Settings.get('bugseeE2e…'), and the example registers no URL scheme, so Linking.getInitialURL() is already nil. Mapping connectionOptions.urlContexts still 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.

startReactNative on every willConnectTo. RCTRootViewFactory.viewWithModuleName reuses the host but always creates a new Fabric surface. A scene disconnect/reconnect in-process (memory pressure, SupportsMultipleScenes still false) would remount App and call Bugsee.launch() again. Template#251 has the same shape; 0.88 RCTSceneDelegate is the real fix. The e2e terminates the process between runs.

Cocoa 7.0.0-beta3 screenshot flake. Author already 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 (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 + launch on iOS 27.0 and 26.5. CI is in flight (ios e2e (simulator) pending at review time).


  1. Overall risk: Low
  2. Merge recommendation: Safe to merge
  3. 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.
  4. Positives: Factory stays in didFinishLaunching (better than creating it in the scene delegate and hoping); window is scene-attached; delegate.window is 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.
Open in Web View Automation 

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>

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Re-review (e0dd777)

e0dd777 is documentation only. Production path is unchanged from 6f9469d: factory in didFinishLaunching, scene-attached window, startReactNative with launchOptions: nil, appDelegate.window mirrored, single-scene manifest.

The new SceneDelegate comment matches the code and the e2e:

  • No connectionOptions.urlContexts mapping and no scene(_:openURLContexts:) — so Linking stays empty on iOS.
  • The app still has no CFBundleURLTypes; e2e/scenario.ts steers iOS through -bugseeE2e… launch arguments / Settings.
  • Pointing a URL-using app at RCTLinkingManager is 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).


  1. Overall risk: Low
  2. Merge recommendation: Safe to merge
  3. Most important issues to fix: None
  4. Positives: The previously implicit “we drop launch URLs” choice is now stated next to launchOptions: nil, which is what a later reader needs.
Open in Web View Automation 

Sent by Cursor Automation: Bugsee code review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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):

  • RCTReactNativeFactory is still created in application(_:didFinishLaunchingWithOptions:). UIKit calls that before scene(_:willConnectTo:options:), so the factory exists when the scene connects.
  • The window is UIWindow(windowScene:), not UIScreen.main.bounds. RN 0.87.1 RCTReactNativeFactory.mm startReactNativeWithModuleName:inWindow:… assigns rootViewController and calls makeKeyAndVisible.
  • appDelegate.window is assigned before start. 0.87.1 RCTDeviceInfo already goes through RCTKeyWindow() (the #53645 crash class), but keeping the property still covers anything that KVC-reads UIApplication.shared.delegate.window (the unrecognized-selector failure in facebook/react-native#53602 if the property were removed).
  • Info.plist is a single scene (UIApplicationSupportsMultipleScenes false) with $(PRODUCT_MODULE_NAME).SceneDelegate. PRODUCT_NAME is BareExample; the same plist already expands $(EXECUTABLE_NAME) / $(PRODUCT_BUNDLE_IDENTIFIER), so the class name expands the same way. Apple does not require application(_:configurationForConnecting:) when the manifest is complete. SceneDelegate lives in AppDelegate.swift, already in Compile Sources.

Callers this change actually hits:

  • App.tsx chooseScenario() — URI first, then iOS Settings launch args, then JSON.
  • e2e/scenario.ts — iOS has no URL scheme; every iOS launch passes -bugseeE2e…. RCT_jsLocation is also an NSUserDefaults arg, not launchOptions.
  • BGSRNSdkWalkedWindows already branches on UIApplicationSceneManifest and will now take keyWindow.windowScene.windows, which is the Cocoa SDK’s walk. BGSRNSdkKeyWindow already iterates connectedScenes.
  • 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.


  1. Overall risk: Low
  2. Merge recommendation: Safe to merge
  3. 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.
  4. Positives: Factory stays in didFinishLaunching; window is scene-attached; delegate.window is 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.
Open in Web View Automation 

Sent by Cursor Automation: Bugsee code review

@dsheikherev
dsheikherev requested a review from krassx October 2, 2026 09:27
@krassx
krassx merged commit 928df79 into main Oct 2, 2026
14 checks passed
@krassx
krassx deleted the fix/example-uiscene-lifecycle branch October 2, 2026 09:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants