Skip to content

Commit a0b8944

Browse files
committed
feat: migrate RNTester and HelloWorld to RCTSceneDelegate
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.
1 parent d874dda commit a0b8944

14 files changed

Lines changed: 343 additions & 152 deletions

File tree

packages/react-native/Libraries/AppDelegate/RCTSceneDelegate.h

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,56 @@ NS_ASSUME_NONNULL_BEGIN
2323
*
2424
* For AppDelegate-only apps, use `RCTAppDelegate` or `RCTReactNativeFactory` directly.
2525
*
26-
* To use it, make your SceneDelegate a subclass of RCTSceneDelegate:
26+
* Usage:
27+
* 1. Declare `UIApplicationSceneManifest` in Info.plist with your SceneDelegate class.
28+
* 2. Subclass `RCTSceneDelegate` and configure it before calling `[super ...]` in
29+
* `scene:willConnectToSession:options:`.
2730
*
2831
* ```objc
2932
* #import <React_RCTAppDelegate/RCTSceneDelegate.h>
30-
* @interface SceneDelegate : RCTSceneDelegate
33+
*
34+
* @implementation SceneDelegate
35+
*
36+
* - (void)scene:(UIScene *)scene
37+
* willConnectToSession:(UISceneSession *)session
38+
* options:(UISceneConnectionOptions *)connectionOptions
39+
* {
40+
* self.moduleName = @"MyApp"; // required: JS module name registered in AppRegistry
41+
* self.initialProps = @{
42+
* // optional root props
43+
* };
44+
* self.dependencyProvider = [[RCTAppDependencyProvider alloc] init]; // if using codegen
45+
* [super scene:scene willConnectToSession:session options:connectionOptions];
46+
* }
47+
*
48+
* - (NSURL *)bundleURL
49+
* {
50+
* return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
51+
* }
52+
*
3153
* @end
3254
* ```
3355
*
34-
* Requires `UIApplicationSceneManifest` in Info.plist with your SceneDelegate class configured.
56+
* Required configuration (set before `[super scene:willConnectToSession:options:]`):
57+
* - `moduleName` — the AppRegistry component name to mount.
58+
* - `bundleURL` — override to return the JS bundle URL (raises if not implemented).
59+
*
60+
* Optional configuration:
61+
* - `initialProps` — props passed to the root component.
62+
* - `dependencyProvider` — codegen module/component provider.
63+
* - `automaticallyLoadReactNativeWindow` — defaults to `YES`; set to `NO` to call
64+
* `loadReactNativeWindow:` yourself after custom setup.
65+
*
66+
* Linking is forwarded automatically via `RCTLinkingManager`. Push notifications and other
67+
* `UIApplicationDelegate` callbacks should remain on your AppDelegate.
3568
*
36-
* All methods implemented by RCTSceneDelegate can be overridden. Call `[super ...]` to use the default
37-
* implementation.
69+
* Overridable methods (inherited from `RCTDefaultReactNativeFactoryDelegate`):
70+
* - (UIViewController *)createRootViewController;
71+
* - (void)setRootView:(UIView *)rootView toRootViewController:(UIViewController *)rootViewController;
72+
* - (void)customizeRootView:(RCTRootView *)rootView;
73+
* - (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents;
74+
* - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
75+
* jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker;
3876
*/
3977
@interface RCTSceneDelegate : RCTDefaultReactNativeFactoryDelegate <UIWindowSceneDelegate>
4078

packages/rn-tester/RNTester/AppDelegate.mm

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,62 @@ - (NSURL *)bundleURL
184184

185185
@end
186186
#else
187+
188+
#if !TARGET_OS_TV
189+
#import <UserNotifications/UserNotifications.h>
190+
#import <React/RCTPushNotificationManager.h>
191+
#endif
192+
193+
#if !TARGET_OS_TV
194+
@interface AppDelegate () <UNUserNotificationCenterDelegate>
195+
@end
196+
#endif
197+
187198
@implementation AppDelegate
188199

200+
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
201+
{
202+
#if !TARGET_OS_TV
203+
[[UNUserNotificationCenter currentNotificationCenter] setDelegate:self];
204+
#endif
205+
return YES;
206+
}
207+
208+
#if !TARGET_OS_TV
209+
- (void)application:(__unused UIApplication *)application
210+
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
211+
{
212+
[RCTPushNotificationManager didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
213+
}
214+
215+
- (void)application:(__unused UIApplication *)application
216+
didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
217+
{
218+
[RCTPushNotificationManager didFailToRegisterForRemoteNotificationsWithError:error];
219+
}
220+
221+
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
222+
willPresentNotification:(UNNotification *)notification
223+
withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
224+
{
225+
[RCTPushNotificationManager didReceiveNotification:notification];
226+
completionHandler(UNNotificationPresentationOptionNone);
227+
}
228+
229+
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
230+
didReceiveNotificationResponse:(UNNotificationResponse *)response
231+
withCompletionHandler:(void (^)(void))completionHandler
232+
{
233+
UNNotification *notification = response.notification;
234+
235+
if ([response.actionIdentifier isEqualToString:UNNotificationDefaultActionIdentifier]) {
236+
[RCTPushNotificationManager setInitialNotification:notification];
237+
}
238+
239+
[RCTPushNotificationManager didReceiveNotification:notification];
240+
completionHandler();
241+
}
242+
#endif
243+
189244
@end
190245
#endif

packages/rn-tester/RNTester/Info.plist

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,6 @@
4848
<string>You need to add NSPhotoLibraryUsageDescription key in Info.plist to enable photo library usage, otherwise it is going to *fail silently*!</string>
4949
<key>RCTNewArchEnabled</key>
5050
<true/>
51-
<key>UILaunchStoryboardName</key>
52-
<string>LaunchScreen</string>
53-
<key>UIRequiredDeviceCapabilities</key>
54-
<array>
55-
<string>arm64</string>
56-
</array>
57-
<key>UISupportedInterfaceOrientations</key>
58-
<array>
59-
<string>UIInterfaceOrientationLandscapeLeft</string>
60-
<string>UIInterfaceOrientationLandscapeRight</string>
61-
<string>UIInterfaceOrientationPortrait</string>
62-
<string>UIInterfaceOrientationPortraitUpsideDown</string>
63-
</array>
6451
<key>UIApplicationSceneManifest</key>
6552
<dict>
6653
<key>UIApplicationSupportsMultipleScenes</key>
@@ -70,14 +57,27 @@
7057
<key>UIWindowSceneSessionRoleApplication</key>
7158
<array>
7259
<dict>
73-
<key>UISceneDelegateClassName</key>
74-
<string>SceneDelegate</string>
7560
<key>UISceneConfigurationName</key>
7661
<string>Default Configuration</string>
62+
<key>UISceneDelegateClassName</key>
63+
<string>SceneDelegate</string>
7764
</dict>
7865
</array>
7966
</dict>
8067
</dict>
68+
<key>UILaunchStoryboardName</key>
69+
<string>LaunchScreen</string>
70+
<key>UIRequiredDeviceCapabilities</key>
71+
<array>
72+
<string>arm64</string>
73+
</array>
74+
<key>UISupportedInterfaceOrientations</key>
75+
<array>
76+
<string>UIInterfaceOrientationLandscapeLeft</string>
77+
<string>UIInterfaceOrientationLandscapeRight</string>
78+
<string>UIInterfaceOrientationPortrait</string>
79+
<string>UIInterfaceOrientationPortraitUpsideDown</string>
80+
</array>
8181
<key>UIViewControllerBasedStatusBarAppearance</key>
8282
<false/>
8383
</dict>

packages/rn-tester/RNTester/NativeExampleViews/FlexibleSizeExampleView.mm

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@
1010
#import <React/RCTBridge.h>
1111
#import <React/RCTRootView.h>
1212
#import <React/RCTRootViewDelegate.h>
13+
#import <React/RCTUtils.h>
1314
#import <React/RCTViewManager.h>
14-
15-
#import "AppDelegate.h"
16-
#import "SceneDelegate.h"
15+
#import <RCTReactNativeFactory.h>
1716

1817
@interface FlexibleSizeExampleViewManager : RCTViewManager
1918

@@ -45,13 +44,7 @@ - (instancetype)initWithFrame:(CGRect)frame
4544
if ((self = [super initWithFrame:frame])) {
4645
_sizeUpdated = NO;
4746

48-
#if RNTESTER_USE_APPDELEGATE
49-
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
50-
RCTReactNativeFactory* reactNativeFactory = appDelegate.reactNativeFactory;
51-
#else
52-
SceneDelegate *sceneDelegate = (SceneDelegate*)self.window.windowScene.delegate;
53-
RCTReactNativeFactory* reactNativeFactory = sceneDelegate.reactNativeFactory;
54-
#endif
47+
RCTReactNativeFactory *reactNativeFactory = RCTGetActiveReactNativeFactory();
5548

5649
_resizableRootView = (RCTRootView *)[reactNativeFactory.rootViewFactory
5750
viewWithModuleName:@"RootViewSizeFlexibilityExampleApp"];

packages/rn-tester/RNTester/NativeExampleViews/UpdatePropertiesExampleView.mm

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@
88
#import "UpdatePropertiesExampleView.h"
99

1010
#import <React/RCTRootView.h>
11+
#import <React/RCTUtils.h>
1112
#import <React/RCTViewManager.h>
12-
13-
#import "AppDelegate.h"
14-
#import "SceneDelegate.h"
13+
#import <RCTReactNativeFactory.h>
1514

1615
@interface UpdatePropertiesExampleViewManager : RCTViewManager
1716

@@ -40,13 +39,7 @@ - (instancetype)initWithFrame:(CGRect)frame
4039
if (self) {
4140
_beige = YES;
4241

43-
#if RNTESTER_USE_APPDELEGATE
44-
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
45-
RCTReactNativeFactory* reactNativeFactory = appDelegate.reactNativeFactory;
46-
#else
47-
SceneDelegate *sceneDelegate = (SceneDelegate*)self.window.windowScene.delegate;
48-
RCTReactNativeFactory* reactNativeFactory = sceneDelegate.reactNativeFactory;
49-
#endif
42+
RCTReactNativeFactory *reactNativeFactory = RCTGetActiveReactNativeFactory();
5043

5144
_rootView =
5245
(RCTRootView *)[reactNativeFactory.rootViewFactory viewWithModuleName:@"SetPropertiesExampleApp"

packages/rn-tester/RNTester/SceneDelegate.h

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,11 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
#import <RCTDefaultReactNativeFactoryDelegate.h>
9-
#import <RCTReactNativeFactory.h>
8+
#import <RCTSceneDelegate.h>
109
#import <UIKit/UIKit.h>
1110

12-
@class ReactNativeDelegate;
13-
@class RCTReactNativeFactory;
14-
15-
@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>
16-
17-
@property (strong, nonatomic) UIWindow *window;
18-
@property (strong, nonatomic) ReactNativeDelegate *reactNativeDelegate;
19-
@property (strong, nonatomic) RCTReactNativeFactory *reactNativeFactory;
11+
@interface SceneDelegate : RCTSceneDelegate
2012

2113
- (NSDictionary *)prepareInitialProps;
2214

2315
@end
24-
25-
@interface ReactNativeDelegate : RCTDefaultReactNativeFactoryDelegate
26-
27-
@end

packages/rn-tester/RNTester/SceneDelegate.mm

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,11 @@
77

88
#import "SceneDelegate.h"
99

10-
#import <UserNotifications/UserNotifications.h>
11-
1210
#import <React/RCTBundleURLProvider.h>
1311
#import <React/RCTDefines.h>
14-
#import <React/RCTLinkingManager.h>
1512
#import <ReactCommon/RCTSampleTurboModule.h>
1613
#import <ReactCommon/RCTTurboModuleManager.h>
1714

18-
#import <React/RCTPushNotificationManager.h>
19-
2015
#import <NativeCxxModuleExample/NativeCxxModuleExample.h>
2116
#ifndef RN_DISABLE_OSS_PLUGIN_HEADER
2217
#import <RNTMyNativeViewComponentView.h>
@@ -29,8 +24,11 @@
2924
#define USE_OSS_CODEGEN 0
3025
#endif
3126

32-
@interface SceneDelegate ()
33-
@end
27+
#if RCT_DEV_MENU
28+
#import <React/RCTDevMenu.h>
29+
#endif
30+
31+
static NSString *kBundlePath = @"js/RNTesterApp.ios";
3432

3533
@implementation SceneDelegate
3634

@@ -49,54 +47,54 @@ - (void)scene:(UIScene *)scene
4947
willConnectToSession:(UISceneSession *)session
5048
options:(UISceneConnectionOptions *)connectionOptions
5149
{
52-
if (![scene isKindOfClass:[UIWindowScene class]])
53-
return;
54-
55-
UIWindowScene *windowScene = (UIWindowScene *)scene;
56-
self.window = [[UIWindow alloc] initWithWindowScene:windowScene];
57-
58-
ReactNativeDelegate *delegate = [[ReactNativeDelegate alloc] init];
59-
RCTReactNativeFactory *factory = [[RCTReactNativeFactory alloc] initWithDelegate:delegate];
60-
50+
self.moduleName = @"RNTesterApp";
51+
self.initialProps = [self prepareInitialProps];
6152
#if USE_OSS_CODEGEN
62-
delegate.dependencyProvider = [[RCTAppDependencyProvider alloc] init];
53+
self.dependencyProvider = [[RCTAppDependencyProvider alloc] init];
6354
#endif
6455

65-
self.reactNativeDelegate = delegate;
66-
self.reactNativeFactory = factory;
56+
[super scene:scene willConnectToSession:session options:connectionOptions];
6757

68-
[factory startReactNativeWithModuleName:@"RNTesterApp"
69-
inWindow:self.window
70-
initialProperties:[self prepareInitialProps]
71-
connectionOptions:connectionOptions];
58+
#if RCT_DEV_MENU
59+
RCTDevMenuConfiguration *devMenuConfiguration = [[RCTDevMenuConfiguration alloc] initWithDevMenuEnabled:true
60+
shakeGestureEnabled:true
61+
keyboardShortcutsEnabled:true];
62+
[self.reactNativeFactory setDevMenuConfiguration:devMenuConfiguration];
63+
#endif
7264
}
7365

74-
- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts
66+
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
7567
{
76-
[RCTLinkingManager scene:scene openURLContexts:URLContexts];
68+
return [self bundleURL];
7769
}
7870

79-
- (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity
71+
- (NSURL *)bundleURL
8072
{
81-
[RCTLinkingManager scene:scene continueUserActivity:userActivity];
73+
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:kBundlePath];
8274
}
8375

84-
@end
85-
86-
@implementation ReactNativeDelegate
87-
88-
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
76+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
77+
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
8978
{
90-
return [self bundleURL];
79+
if (name == facebook::react::NativeCxxModuleExample::kModuleName) {
80+
return std::make_shared<facebook::react::NativeCxxModuleExample>(jsInvoker);
81+
}
82+
83+
return [super getTurboModule:name jsInvoker:jsInvoker];
9184
}
9285

93-
- (NSURL *)bundleURL
86+
#ifndef RN_DISABLE_OSS_PLUGIN_HEADER
87+
- (nonnull NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents
9488
{
95-
#if DEBUG
96-
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"js/RNTesterApp.ios"];
97-
#else
98-
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
99-
#endif
89+
NSMutableDictionary *dict = [super thirdPartyFabricComponents].mutableCopy;
90+
if (!dict[@"RNTMyNativeView"]) {
91+
dict[@"RNTMyNativeView"] = NSClassFromString(@"RNTMyNativeViewComponentView");
92+
}
93+
if (!dict[@"SampleNativeComponent"]) {
94+
dict[@"SampleNativeComponent"] = NSClassFromString(@"RCTSampleNativeComponentComponentView");
95+
}
96+
return dict;
10097
}
98+
#endif
10199

102100
@end

packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,7 @@
11931193
"-ObjC",
11941194
"-lc++",
11951195
);
1196+
PODFILE_DIR = "$(SRCROOT)";
11961197
REACT_NATIVE_PATH = "${PODS_ROOT}/../../react-native";
11971198
SDKROOT = iphoneos;
11981199
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
@@ -1290,6 +1291,7 @@
12901291
"-ObjC",
12911292
"-lc++",
12921293
);
1294+
PODFILE_DIR = "$(SRCROOT)";
12931295
REACT_NATIVE_PATH = "${PODS_ROOT}/../../react-native";
12941296
SDKROOT = iphoneos;
12951297
USE_HERMES = true;

0 commit comments

Comments
 (0)