-
Notifications
You must be signed in to change notification settings - Fork 25.2k
feat: migrate to SceneDelegate #53744
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
917f539
8b4adc0
2863c4c
d27e491
0ac5467
0232d98
a81e935
6a0dce2
641a29c
d961af6
dc7763c
c0bc778
c77f68a
1e7a30e
5448f10
7eebb2f
1f25576
d436cd8
75d2c66
5045ad8
cb9a5b4
697758b
b37bcd9
de29fca
2ed5739
4ede7e8
7bc2b30
d3c2cd4
7db2384
a4503ab
8771ff1
4268e4c
785f869
e332eb7
c69f974
3b0416b
543c296
6f5935a
3f96863
a72e1e3
f661e41
5aac7cd
1c0c5ad
2d64c04
ea3c55e
693b31d
b822bec
d751f4c
041c9bc
a75ed5b
37cfa12
dbad6af
d874dda
a0b8944
0820933
d5a5a6d
0979697
78f298c
49816a3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| /* | ||
| * Copyright (c) Meta Platforms, Inc. and affiliates. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE file in the root directory of this source tree. | ||
| */ | ||
|
|
||
| #import <UIKit/UIKit.h> | ||
| #import "RCTDefaultReactNativeFactoryDelegate.h" | ||
| #import "RCTReactNativeFactory.h" | ||
| #import "RCTRootViewFactory.h" | ||
|
|
||
| @protocol RCTBridgeDelegate; | ||
| @protocol RCTComponentViewProtocol; | ||
| @class RCTRootView; | ||
| @class RCTSurfacePresenterBridgeAdapter; | ||
| @protocol RCTDependencyProvider; | ||
|
|
||
| NS_ASSUME_NONNULL_BEGIN | ||
|
|
||
| /** | ||
| * Optional utility base class for SceneDelegate-based React Native apps using the UIScene lifecycle. | ||
| * | ||
| * For AppDelegate-only apps, use `RCTAppDelegate` or `RCTReactNativeFactory` directly. | ||
| * | ||
| * Usage: | ||
| * 1. Declare `UIApplicationSceneManifest` in Info.plist with your SceneDelegate class. | ||
| * 2. Subclass `RCTSceneDelegate` and configure it before calling `[super ...]` in | ||
| * `scene:willConnectToSession:options:`. | ||
| * | ||
| * ```objc | ||
| * #import <React_RCTAppDelegate/RCTSceneDelegate.h> | ||
| * | ||
| * @implementation SceneDelegate | ||
| * | ||
| * - (void)scene:(UIScene *)scene | ||
| * willConnectToSession:(UISceneSession *)session | ||
| * options:(UISceneConnectionOptions *)connectionOptions | ||
| * { | ||
| * self.moduleName = @"MyApp"; // required: JS module name registered in AppRegistry | ||
| * self.initialProps = @{ | ||
| * // optional root props | ||
| * }; | ||
| * self.dependencyProvider = [[RCTAppDependencyProvider alloc] init]; // if using codegen | ||
| * [super scene:scene willConnectToSession:session options:connectionOptions]; | ||
| * } | ||
| * | ||
| * - (NSURL *)bundleURL | ||
| * { | ||
| * return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; | ||
| * } | ||
| * | ||
| * @end | ||
| * ``` | ||
| * | ||
| * Required configuration (set before `[super scene:willConnectToSession:options:]`): | ||
| * - `moduleName` — the AppRegistry component name to mount. | ||
| * - `bundleURL` — override to return the JS bundle URL (raises if not implemented). | ||
| * | ||
| * Optional configuration: | ||
| * - `initialProps` — props passed to the root component. | ||
| * - `dependencyProvider` — codegen module/component provider. | ||
| * - `automaticallyLoadReactNativeWindow` — defaults to `YES`; set to `NO` to call | ||
| * `loadReactNativeWindow:` yourself after custom setup. | ||
| * | ||
| * Linking is forwarded automatically via `RCTLinkingManager`. Push notifications and other | ||
| * `UIApplicationDelegate` callbacks should remain on your AppDelegate. | ||
| * | ||
| * Overridable methods (inherited from `RCTDefaultReactNativeFactoryDelegate`): | ||
| * - (UIViewController *)createRootViewController; | ||
| * - (void)setRootView:(UIView *)rootView toRootViewController:(UIViewController *)rootViewController; | ||
| * - (void)customizeRootView:(RCTRootView *)rootView; | ||
| * - (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents; | ||
| * - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name | ||
| * jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker; | ||
| */ | ||
| @interface RCTSceneDelegate : RCTDefaultReactNativeFactoryDelegate <UIWindowSceneDelegate> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not a fan of the subclassing. Can't we move away from the subclassing approach and show the users how to use the RCTReactNativeFactory directly? What are they going to miss if they don't use this subclass?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's purely a utility to reduce the amount of code the users would need to write. I don't strongly suggest this approach, we can instead document what they need to add and drop this helper. |
||
|
|
||
| /// The window object used to render UIViewControllers for this scene. | ||
| @property (nonatomic, strong, nullable) UIWindow *window; | ||
|
|
||
| #if !defined(RCT_REMOVE_LEGACY_ARCH) | ||
| @property (nonatomic, nullable) RCTBridge *bridge | ||
| __attribute__((deprecated("The bridge is deprecated and will be removed when removing the legacy architecture."))); | ||
| @property (nonatomic, nullable) RCTSurfacePresenterBridgeAdapter *bridgeAdapter __attribute__(( | ||
| deprecated("The bridge adapter is deprecated and will be removed when removing the legacy architecture."))); | ||
| #endif | ||
|
Comment on lines
+82
to
+87
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be removed. It is not possible to disable New Arch |
||
|
|
||
| @property (nonatomic, strong, nullable) NSString *moduleName; | ||
| @property (nonatomic, strong, nullable) NSDictionary *initialProps; | ||
| @property (nonatomic, strong, nullable) RCTReactNativeFactory *reactNativeFactory; | ||
|
|
||
| /// If `automaticallyLoadReactNativeWindow` is set to `true`, the React Native window is loaded in | ||
| /// `scene:willConnectToSession:options:`. | ||
| @property (nonatomic, assign) BOOL automaticallyLoadReactNativeWindow; | ||
|
|
||
| - (RCTRootViewFactory *)rootViewFactory; | ||
|
|
||
| /// Loads the React Native root view into `self.window` using UIScene connection options. | ||
| - (void)loadReactNativeWindow:(UISceneConnectionOptions *_Nullable)connectionOptions; | ||
|
|
||
| @end | ||
|
|
||
| NS_ASSUME_NONNULL_END | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| /* | ||
| * Copyright (c) Meta Platforms, Inc. and affiliates. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE file in the root directory of this source tree. | ||
| */ | ||
|
|
||
| #import "RCTSceneDelegate.h" | ||
|
|
||
| #import <React/RCTLinkingManager.h> | ||
| #import <React/RCTLog.h> | ||
| #import <React/RCTRootView.h> | ||
| #import <React/RCTUtils.h> | ||
|
|
||
| @implementation RCTSceneDelegate | ||
|
|
||
| - (instancetype)init | ||
| { | ||
| if (self = [super init]) { | ||
| _automaticallyLoadReactNativeWindow = YES; | ||
| } | ||
| return self; | ||
| } | ||
|
|
||
| - (void)scene:(UIScene *)scene | ||
| willConnectToSession:(UISceneSession *)session | ||
| options:(UISceneConnectionOptions *)connectionOptions | ||
| { | ||
| if (![scene isKindOfClass:[UIWindowScene class]]) { | ||
| return; | ||
| } | ||
|
|
||
| self.reactNativeFactory = [[RCTReactNativeFactory alloc] initWithDelegate:self]; | ||
|
|
||
| UIWindowScene *windowScene = (UIWindowScene *)scene; | ||
| self.window = [[UIWindow alloc] initWithWindowScene:windowScene]; | ||
|
|
||
| if (self.automaticallyLoadReactNativeWindow) { | ||
| [self loadReactNativeWindow:connectionOptions]; | ||
| } | ||
| } | ||
|
|
||
| - (void)loadReactNativeWindow:(UISceneConnectionOptions *)connectionOptions | ||
| { | ||
| [self.reactNativeFactory startReactNativeWithModuleName:self.moduleName | ||
| inWindow:self.window | ||
| initialProperties:self.initialProps | ||
| connectionOptions:connectionOptions]; | ||
| } | ||
|
|
||
| - (RCTRootViewFactory *)rootViewFactory | ||
| { | ||
| return self.reactNativeFactory.rootViewFactory; | ||
| } | ||
|
|
||
| #pragma mark - Linking (SceneDelegate lifecycle) | ||
|
|
||
| - (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts | ||
| { | ||
| [RCTLinkingManager scene:scene openURLContexts:URLContexts]; | ||
| } | ||
|
|
||
| - (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity | ||
| { | ||
| [RCTLinkingManager scene:scene continueUserActivity:userActivity]; | ||
| } | ||
|
Comment on lines
+58
to
+66
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We probably need to update the website with these. I would not add the RCTSceneDelegate at all. |
||
|
|
||
| @end | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if it is not supported, can't we crash? I think we should raise an exception. A warning in console is very easy to miss. Perhaps we can add a compiler flag that transforms the crash in a warning, but at least users will have to do it themselves and hopefully they'll know what they are doing.
WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, we can make this an assertion. I think the option to opt out with a compiler flag is sensible. I do not foresee use cases when users would enable this, because even if they would do so, internal React Native components that use
NSNotificationCenterwould cross-talk across scenes.