Skip to content

feat: migrate to SceneDelegate#53744

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

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

Conversation

@artus9033

@artus9033 artus9033 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:

internal import Expo
import React
import React_RCTAppDelegate
import ReactAppDependencyProvider
import UIKit

class SceneDelegate: RCTSceneDelegate {
  override func scene(
    _ scene: UIScene,
    willConnectTo session: UISceneSession,
    options connectionOptions: UIScene.ConnectionOptions
  ) {
    moduleName = "main"
    dependencyProvider = RCTAppDependencyProvider()
    automaticallyLoadReactNativeWindow = false

    guard let windowScene = scene as? UIWindowScene else {
      return
    }

    let delegate = ReactNativeDelegate()
    delegate.dependencyProvider = RCTAppDependencyProvider()
    reactNativeFactory = ExpoReactNativeFactory(delegate: delegate)

    window = UIWindow(windowScene: windowScene)

    reactNativeFactory?.startReactNative(
      withModuleName: moduleName ?? "main",
      in: window,
      initialProperties: initialProps,
      connectionOptions: connectionOptions
    )
  }
}

class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {
  override func bundleURL() -> URL? {
#if DEBUG
    return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry")
#else
    return Bundle.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-cla meta-cla Bot 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
@artus9033 artus9033 changed the title Feat/scene delegate feat: migrate to SceneDelegate Sep 11, 2025
@cipolleschi
cipolleschi marked this pull request as ready for review February 4, 2026 11:10
@cipolleschi
cipolleschi marked this pull request as draft February 4, 2026 11:10
Introduces an optional RCTSceneDelegate base class alongside the existing
RCTAppDelegate path, with RCTGetActiveReactNativeFactory and RCTKeyWindow
improvements.
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
artus9033 force-pushed the feat/scene-delegate branch from e2482e9 to a0b8944 Compare July 9, 2026 15:29
@artus9033
artus9033 marked this pull request as ready for review July 13, 2026 10:12
@facebook-github-tools facebook-github-tools Bot 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
artus9033 force-pushed the feat/scene-delegate branch from abf677c to d5a5a6d Compare July 13, 2026 11:19

@cipolleschi cipolleschi 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 thread packages/react-native/Libraries/AppDelegate/RCTSceneDelegate.h Outdated
Comment thread packages/react-native/Libraries/AppDelegate/RCTSceneDelegate.h Outdated
Comment thread packages/react-native/Libraries/AppDelegate/RCTSceneDelegate.mm Outdated
Comment thread packages/react-native/Libraries/LinkingIOS/RCTLinkingManager.mm Outdated
Comment thread packages/rn-tester/IntegrationTests/AccessibilityManagerTest.js Outdated
Comment thread packages/rn-tester/RNTester/AppDelegate.h Outdated
Comment thread packages/rn-tester/RNTester/SceneDelegate.h Outdated
Comment thread private/helloworld/ios/HelloWorld/AppDelegate.legacy.swift Outdated
@artus9033
artus9033 force-pushed the feat/scene-delegate branch from 27e1c80 to 49816a3 Compare July 13, 2026 15:27

@cipolleschi cipolleschi 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!

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

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied 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.

3 participants