Skip to content

feat: migrate to SceneDelegate - #53744

Open
artus9033 wants to merge 69 commits into
react:mainfrom
artus9033:feat/scene-delegate
Open

feat: migrate to SceneDelegate#53744
artus9033 wants to merge 69 commits into
react:mainfrom
artus9033:feat/scene-delegate

Conversation

@artus9033

@artus9033artus9033 commented Sep 11, 2025

Copy link
Copy Markdown
Contributor

Summary:

This PR introduces a migration from the now-deprecated iOS AppDelegate to SceneDelegate, as well as the changes to React Native code that will migrate to APIs allowing it to work fine with the SceneDelegate approach. There are two primary goals of this API:

  • feature: ability for users to leverage resizable windows feature on iPadOS in RN apps
  • stay ahead of the incoming changes, since AppDelegate is scheduled to be deleted in future releases of iOS

The changes are additive to RN and maintain backwards compatibility, but require user-space modifications to RN apps that want to migrate from AppDelegate to the scene lifecycle.

What changed

Bootstrap (Libraries/AppDelegate)

  • RCTSceneDelegate - introduced fast integration point for subclassing in the user-space SceneDelegate base class (counterpart to RCTAppDelegate)
  • RCTReactNativeFactory - SceneDelegate entrypoints via UISceneConnectionOptions
  • RCTAppDelegate retained; deprecation wording updated to point scene apps to RCTSceneDelegate

Utilities (React/Base)

  • RCTGetActiveReactNativeFactory() - resolve factory from SceneDelegate or AppDelegate
  • RCTKeyWindow() - iOS 13–14 fallback for scene-based apps
  • RCTIsSceneDelegateApp() - dual-path linking detection

Linking (Libraries/LinkingIOS)

  • SceneDelegate class methods on RCTLinkingManager; AppDelegate methods retained for non-scene apps

Core modules

  • RCTDeviceInfo - refresh cached getConstants() on orientation/frame/content-size changes
  • NativeDeviceInfo JS - remove stale constants caching
  • RCTDevLoadingView - layout/dismiss fixes for scene window resizing

Sample apps

  • RNTester (default): RCTSceneDelegate; AppDelegate handles push notifications
  • RNTester (AppDelegate) scheme: legacy path via RNTESTER_USE_APPDELEGATE
  • HelloWorld: SceneDelegate default; legacy bootstrap in AppDelegate.legacy.swift

The RFC documenting the details is available here.

Example usage:

An example entrypoint using the added RCTSceneDelegate (imported from React_RCTAppDelegate) is:

internalimport Expo
import React
import React_RCTAppDelegate
import ReactAppDependencyProvider
import UIKit
classSceneDelegate:RCTSceneDelegate{overridefunc scene(
_ scene:UIScene,
willConnectTo session:UISceneSession,
options connectionOptions:UIScene.ConnectionOptions){
moduleName ="main"
dependencyProvider =RCTAppDependencyProvider()
automaticallyLoadReactNativeWindow =falseguardlet windowScene = scene as?UIWindowSceneelse{return}letdelegate=ReactNativeDelegate()
delegate.dependencyProvider =RCTAppDependencyProvider()
reactNativeFactory =ExpoReactNativeFactory(delegate: delegate)
window =UIWindow(windowScene: windowScene)
reactNativeFactory?.startReactNative(
withModuleName: moduleName ??"main",
in: window,
initialProperties: initialProps,
connectionOptions: connectionOptions
)}}classReactNativeDelegate:ExpoReactNativeFactoryDelegate{overridefunc bundleURL()->URL?{#if DEBUGreturnRCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot:".expo/.virtual-metro-entry")#elsereturnBundle.main.url(forResource:"main", withExtension:"jsbundle")#endif}}

Follow-ups:

After merging this PR, the AppDelegate will be deprecated, but still available. After a future de-integration of AppDelegate, packages/react-native/Libraries/AppDelegate and contained files should be renamed.

Additional information

One capability that can be leveraged with the SceneDelegate on iPadOS is multi-window (multi-instance) support, which would cause all sorts of problems in the current React Native core and 3p libraries' designs. Therefore, enabling this capability is currently unsupported by React Native and to enforce this, a warning message like the one below would be printed provided this capability had been enabled:

image

Changelog:

[IOS] [ADDED] - SceneDelegate support with backwards compatibility to AppDelegate-only approaches
[IOS] [ADDED] - RCTSceneDelegate, RCTGetActiveReactNativeFactory(), and RCTReactNativeFactory SceneDelegate entrypoints
[IOS] [ADDED] - RCTLinkingManager SceneDelegate lifecycle methods (AppDelegate path retained)
[IOS] [CHANGED] - DeviceInfo to update the value returned by getConstants upon frame change
[IOS] [CHANGED] - RCTDevLoadingView layout for scene window resizing
[IOS] [ADDED] - additional scheme: RNTester (AppDelegate), allowing to run RNTester with AppDelegate infrastructure
[IOS] [ADDED] - print warning from RCTReactNativeFactory if UIApplicationSceneManifest.UIApplicationSupportsMultipleScenes in Info.plist is set to true, which is unsupported by RN to prevent bugs due to simultaneously running multiple instances of an app

Test Plan:

  • Evaluate that RNTester works properly (default RNTester SceneDelegate and RNTester (AppDelegate) schemes)
  • Verify HelloWorld SceneDelegate bootstrap; deep links and iPadOS window resize / Dimensions updates
IMG_0015
RNTester
ScreenRecording_07-13-2026.12-26-27_1.mov
HelloWorld
ScreenRecording_07-13-2026.13-15-08_1.mov

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Sep 11, 2025
@artus9033artus9033 changed the title Feat/scene delegatefeat: migrate to SceneDelegateSep 11, 2025
Adopt RCTSceneDelegate in sample apps, wire push notifications on AppDelegate
for the scene path, fix native example views to use RCTGetActiveReactNativeFactory,
and preserve legacy AppDelegate bootstrap behind compile flags.
@artus9033
artus9033force-pushed the feat/scene-delegate branch from e2482e9 to a0b8944CompareJuly 9, 2026 15:29
@artus9033
artus9033 marked this pull request as ready for review July 13, 2026 10:12
@facebook-github-toolsfacebook-github-toolsBot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Jul 13, 2026
@artus9033
artus9033force-pushed the feat/scene-delegate branch from abf677c to d5a5a6dCompareJuly 13, 2026 11:19

@cipolleschicipolleschi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @artus9033, thanks for working on this.
The PR is very detailed and well prepared.

I think we can simplify some assumption to make it smaller and more maintainable.
I left some comments in the PR. Could you address them?

Comment threadpackages/react-native/Libraries/AppDelegate/RCTSceneDelegate.h Outdated
Comment threadpackages/react-native/Libraries/AppDelegate/RCTSceneDelegate.h Outdated
Comment threadpackages/react-native/Libraries/AppDelegate/RCTSceneDelegate.mm Outdated
Comment threadpackages/react-native/Libraries/LinkingIOS/RCTLinkingManager.mm Outdated
Comment threadpackages/rn-tester/IntegrationTests/AccessibilityManagerTest.js Outdated
Comment threadpackages/rn-tester/RNTester/AppDelegate.h Outdated
Comment threadpackages/rn-tester/RNTester/SceneDelegate.h Outdated
Comment threadprivate/helloworld/ios/HelloWorld/AppDelegate.legacy.swift Outdated
@artus9033
artus9033force-pushed the feat/scene-delegate branch from 27e1c80 to 49816a3CompareJuly 13, 2026 15:27

@cipolleschicipolleschi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for fixing this!

@artus9033

Copy link
Copy Markdown
ContributorAuthor

Thank you too!

@meta-codesync

Copy link
Copy Markdown

@cipolleschi has imported this pull request. If you are a Meta employee, you can view this in D113401534.

@ashfurrow

Copy link
Copy Markdown

Hello 👋 Small nitpick: UIApplicationDelegate is not deprecated itself. Rather, there are certain app lifecycle methods in the app delegate that are moving to the scene delegate.

I have migrated an RN app to use UIScene-based lifecycle hooks and, comparing to this implementation, I don't see any red-flags or gotchas here 👍 I hope that helps, and thank you!

@dklt

dklt commented Jul 27, 2026

Copy link
Copy Markdown

Just reporting ::::
indeed, on XCode 27 beta4, without using SceneDelegate, app will crash at runtime, suggesting UIScene-life-cycle only moving forward. ref ::::

failure in void _UIApplicationEvaluateRuntimeIssueForNoSceneLifecycleAdoption(void)_block_invoke (UIApplication_RuntimeIssues.m:106) : Application failed to launch: UIScene life cycle is required for apps built with this SDK. See "Transitioning to the UIKit scene-based life cycle" in the UIKit documentation for more information on migration.

btw, thanks for all the hard work ~!!! 🥇

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.Shared with MetaApplied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@artus9033@react-native-bot@ashfurrow@dklt@cipolleschi