Skip to content

fix(ios): adopt the UIScene life cycle so Xcode 27 builds launch - #29649

Draft
chrisnojima wants to merge 3 commits into
masterfrom
nojima/HOTPOT-as-01-uiscene
Draft

chrisnojima wants to merge 3 commits into
masterfrom
nojima/HOTPOT-as-01-uiscene

Conversation

@chrisnojima

@chrisnojima chrisnojima commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Stack 1/9, splitting #29637. Base: master.

Why

iOS 27 requires an app to adopt the UIScene life cycle. Without it, a build made with Xcode 27 traps at launch inside _UIApplicationEvaluateRuntimeIssueForNoSceneLifecycleAdoption — the app does not start at all. That makes this a prerequisite for building the app with the current toolchain, independent of everything else in the stack.

What master does today

  • No UIApplicationSceneManifest, no scene delegate: the app is still on the pre-scene UIApplication life cycle and cannot launch under Xcode 27.
  • AppDelegate.didFinishLaunching builds a KeyboardWindow and starts React Native itself, so all root-view setup is tied to launch rather than to a scene connecting.
  • Hardware key handling lives on that KeyboardWindow subclass.

What this changes and why each piece

  • Info.plist gains a UIApplicationSceneManifest naming SceneDelegate; SceneDelegate subclasses Expo's ExpoAppSceneDelegate. This is the adoption itself.
  • Root-view work moves out of didFinishLaunching into a new didStartReactNative(in:), because under scenes the window does not exist until a scene connects. BGTaskScheduler.register deliberately stays in didFinishLaunching — iOS requires it to run before that returns.
  • KeyboardWindow is deleted and hardware enter / shift-enter move to pressesBegan on the delegate. Expo's scene delegate owns window creation now, so a custom UIWindow subclass has nowhere to be installed; the responder chain gives the same behaviour.
  • The two RCTLinkingManager passthrough overrides are deleted because Expo's SceneEventForwarder already forwards them; keeping both would double-deliver. The keybase://incoming-share path re-adds an explicit call, since it does not go through the forwarder.
  • applicationDidBecomeActive reports foreground directly. sceneDidBecomeActive is forwarded while UIApplication.applicationState still reads .inactive, so the old state-derived path would have concluded "not foreground" and stopped the local http server — which shows up as blank images.

Scope

No Go, JS or protocol change. The Swift symbol set is identical to master's, so no rebuilt Keybasego.xcframework is required. That is what lets this land first and alone.

Verification

iOS builds under Xcode 27 (BUILD SUCCEEDED, no warnings in our code).

Worth a manual pass, because these are behaviours the compiler cannot check: cold launch, images loading, hardware enter / shift-enter in the chat input, and a universal link plus a keybase:// link both cold and warm — the linking overrides were removed rather than relocated.

Ordering

Under scenes RN's AppState can stick at inactive (RCTAppState dedupes on last-known state), so mobileAppState stays wrong until #29651 takes the app state from the service. Merging this alone leaves that window open on master.

Apps built with the iOS 27 SDK trap at launch in
_UIApplicationEvaluateRuntimeIssueForNoSceneLifecycleAdoption unless they
use scenes. SceneDelegate builds on Expo's ExpoAppSceneDelegate, which
forwards lifecycle, URL and user-activity events to the AppDelegate
overrides, and moves React Native's root view controller into a
KeyboardWindow so hardware enter/shift-enter keep working.
…s under scenes

sceneDidBecomeActive is forwarded to applicationDidBecomeActive while
applicationState still reads .inactive, so notifyAppState told Go INACTIVE
and the local http server stayed stopped, leaving images blank.
Use the stock @objc(SceneDelegate) ExpoAppSceneDelegate with only a
post-connect hook for root view setup. Drop KeyboardWindow in favor of
handling hardware enter/shift-enter on the app delegate at the end of the
responder chain, and drop the manual RCTLinkingManager overrides that
Expo's scene forwarder already covers.
@chrisnojima
chrisnojima added this pull request to stack #29658 September 21, 2026 18:56
@chrisnojima chrisnojima changed the title nojima/HOTPOT as 01 uiscene fix(ios): adopt the UIScene life cycle so Xcode 27 builds launch Sep 21, 2026
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.

1 participant