Skip to content

Commit ec928d7

Browse files
okwasniewskifacebook-github-bot
authored andcommitted
feat(RCTAppDelegate): Implement RCTRootViewFactory (#42263)
Summary: This PR implements `RCTRootViewFactory` a utility class (suggested by cipolleschi) that returns proper RCTRootView based on the current environment state (new arch/old arch/bridgeless). This class aims to preserve background compatibility by implementing a configuration class forwarding necessary class to RCTAppDelegate. ### Brownfield use case This PR leverages the `RCTRootViewFactory` in `RCTAppDelegate` for the default initialization of React Native (greenfield). Here is an example of creating a Brownfield integration (without RCTAppDelegate) using this class (can be later added to docs): 1. Store reference to `rootViewFactory` and to `UIWindow` `AppDelegate.h`: ```objc interface AppDelegate : UIResponder <UIApplicationDelegate> property(nonatomic, strong) UIWindow* window; property(nonatomic, strong) RCTRootViewFactory* rootViewFactory; end ``` 2. Create an initial configuration using `RCTRootViewFactoryConfiguration` and initialize `RCTRootViewFactory` using it. Then you can use the factory to create a new `RCTRootView` without worrying about old arch/new arch/bridgeless. `AppDelegate.mm` ```objc implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey,id> *)launchOptions { // Create configuration RCTRootViewFactoryConfiguration *configuration = [[RCTRootViewFactoryConfiguration alloc] initWithBundleURL:self.bundleURL newArchEnabled:self.fabricEnabled turboModuleEnabled:self.turboModuleEnabled bridgelessEnabled:self.bridgelessEnabled]; // Initialize RCTRootViewFactory self.rootViewFactory = [[RCTRootViewFactory alloc] initWithConfiguration:configuration]; // Create main root view UIView *rootView = [self.rootViewFactory viewWithModuleName:@"RNTesterApp" initialProperties:@{} launchOptions:launchOptions]; // Set main window as you prefer for your Brownfield integration. self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIViewController *rootViewController = [UIViewController new]; rootViewController.view = rootView; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; // Later in the codebase you can initialize more rootView's using rootViewFactory. return YES; } end ``` bypass-github-export-checks ## Changelog: [INTERNAL] [ADDED] - Implement RCTRootViewFactory Pull Request resolved: #42263 Test Plan: Check if root view is properly created on app initialization Reviewed By: dmytrorykun Differential Revision: D53179625 Pulled By: cipolleschi fbshipit-source-id: 9bc850965ba30d84ad3e67d91dd888f0547c2136
1 parent 9f85a24 commit ec928d7

File tree

6 files changed

+441
-141
lines changed

6 files changed

+441
-141
lines changed

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

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#import <React/RCTBridgeDelegate.h>
99
#import <React/RCTConvert.h>
1010
#import <UIKit/UIKit.h>
11+
#import "RCTRootViewFactory.h"
1112

1213
@class RCTBridge;
1314
@protocol RCTBridgeDelegate;
@@ -58,9 +59,12 @@ NS_ASSUME_NONNULL_BEGIN
5859

5960
/// The window object, used to render the UViewControllers
6061
@property (nonatomic, strong, nonnull) UIWindow *window;
61-
@property (nonatomic, strong, nullable) RCTBridge *bridge;
62+
@property (nonatomic, nullable) RCTBridge *bridge;
6263
@property (nonatomic, strong, nullable) NSString *moduleName;
6364
@property (nonatomic, strong, nullable) NSDictionary *initialProps;
65+
@property (nonatomic, strong, nonnull) RCTRootViewFactory *rootViewFactory;
66+
67+
@property (nonatomic, nullable) RCTSurfacePresenterBridgeAdapter *bridgeAdapter;
6468

6569
/**
6670
* It creates a `RCTBridge` using a delegate and some launch options.
@@ -127,18 +131,11 @@ NS_ASSUME_NONNULL_BEGIN
127131
*/
128132
- (void)setRootView:(UIView *)rootView toRootViewController:(UIViewController *)rootViewController;
129133

130-
/// This method controls whether the App will use RuntimeScheduler. Only applicable in the legacy architecture.
131-
///
132-
/// @return: `YES` to use RuntimeScheduler, `NO` to use JavaScript scheduler. The default value is `YES`.
133-
- (BOOL)runtimeSchedulerEnabled;
134-
135134
/**
136135
* The default `RCTColorSpace` for the app. It defaults to `RCTColorSpaceSRGB`.
137136
*/
138137
@property (nonatomic, readonly) RCTColorSpace defaultColorSpace;
139138

140-
@property (nonatomic, strong) RCTSurfacePresenterBridgeAdapter *bridgeAdapter;
141-
142139
/// This method returns a map of Component Descriptors and Components classes that needs to be registered in the
143140
/// new renderer. The Component Descriptor is a string which represent the name used in JS to refer to the native
144141
/// component. The default implementation returns an empty dictionary. Subclasses can override this method to register

packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm

Lines changed: 56 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -43,83 +43,29 @@
4343
#import <react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.h>
4444
#import <react/runtime/JSRuntimeFactory.h>
4545

46-
@interface RCTAppDelegate () <RCTComponentViewFactoryComponentProvider, RCTContextContainerHandling> {
47-
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
48-
facebook::react::ContextContainer::Shared _contextContainer;
49-
}
46+
@interface RCTAppDelegate () <RCTComponentViewFactoryComponentProvider>
5047
@end
5148

52-
static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabricEnabled)
53-
{
54-
NSMutableDictionary *mutableProps = [initialProps mutableCopy] ?: [NSMutableDictionary new];
55-
return mutableProps;
56-
}
57-
58-
@interface RCTAppDelegate () <RCTCxxBridgeDelegate> {
59-
std::shared_ptr<facebook::react::RuntimeScheduler> _runtimeScheduler;
60-
}
61-
@end
62-
63-
@implementation RCTAppDelegate {
64-
RCTHost *_reactHost;
65-
}
66-
67-
- (instancetype)init
68-
{
69-
if (self = [super init]) {
70-
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
71-
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
72-
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
73-
}
74-
return self;
75-
}
49+
@implementation RCTAppDelegate
7650

7751
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
7852
{
7953
RCTSetNewArchEnabled([self newArchEnabled]);
8054
[RCTColorSpaceUtils applyDefaultColorSpace:self.defaultColorSpace];
81-
BOOL enableTM = self.turboModuleEnabled;
82-
BOOL fabricEnabled = self.fabricEnabled;
83-
BOOL enableBridgeless = self.bridgelessEnabled;
84-
85-
NSDictionary *initProps = updateInitialProps([self prepareInitialProps], fabricEnabled);
55+
RCTAppSetupPrepareApp(application, self.turboModuleEnabled);
8656

87-
RCTAppSetupPrepareApp(application, enableTM);
57+
self.rootViewFactory = [self createRCTRootViewFactory];
8858

89-
UIView *rootView;
90-
if (enableBridgeless) {
91-
// Enable native view config interop only if both bridgeless mode and Fabric is enabled.
92-
RCTSetUseNativeViewConfigsInBridgelessMode(fabricEnabled);
59+
UIView *rootView = [self.rootViewFactory viewWithModuleName:self.moduleName
60+
initialProperties:self.initialProps
61+
launchOptions:launchOptions];
9362

94-
// Enable TurboModule interop by default in Bridgeless mode
95-
RCTEnableTurboModuleInterop(YES);
96-
RCTEnableTurboModuleInteropBridgeProxy(YES);
97-
98-
[self createReactHost];
63+
if (self.newArchEnabled || self.fabricEnabled) {
9964
[RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self;
100-
RCTFabricSurface *surface = [_reactHost createSurfaceWithModuleName:self.moduleName initialProperties:initProps];
101-
102-
RCTSurfaceHostingProxyRootView *surfaceHostingProxyRootView = [[RCTSurfaceHostingProxyRootView alloc]
103-
initWithSurface:surface
104-
sizeMeasureMode:RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact];
105-
106-
rootView = (RCTRootView *)surfaceHostingProxyRootView;
107-
rootView.backgroundColor = [UIColor systemBackgroundColor];
108-
} else {
109-
if (!self.bridge) {
110-
self.bridge = [self createBridgeWithDelegate:self launchOptions:launchOptions];
111-
}
112-
if ([self newArchEnabled]) {
113-
self.bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:self.bridge
114-
contextContainer:_contextContainer];
115-
self.bridge.surfacePresenter = self.bridgeAdapter.surfacePresenter;
116-
117-
[RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self;
118-
}
119-
rootView = [self createRootViewWithBridge:self.bridge moduleName:self.moduleName initProps:initProps];
12065
}
12166
[self _logWarnIfCreateRootViewWithBridgeIsOverridden];
12267
[self customizeRootView:(RCTRootView *)rootView];
68+
12369
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
12470
UIViewController *rootViewController = [self createRootViewController];
12571
[self setRootView:rootView toRootViewController:rootViewController];
@@ -142,21 +88,11 @@ - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
14288
return nil;
14389
}
14490

145-
- (NSDictionary *)prepareInitialProps
146-
{
147-
return self.initialProps;
148-
}
149-
15091
- (RCTBridge *)createBridgeWithDelegate:(id<RCTBridgeDelegate>)delegate launchOptions:(NSDictionary *)launchOptions
15192
{
15293
return [[RCTBridge alloc] initWithDelegate:delegate launchOptions:launchOptions];
15394
}
15495

155-
- (void)customizeRootView:(RCTRootView *)rootView
156-
{
157-
// Override point for customization after application launch.
158-
}
159-
16096
- (UIView *)createRootViewWithBridge:(RCTBridge *)bridge
16197
moduleName:(NSString *)moduleName
16298
initProps:(NSDictionary *)initProps
@@ -194,9 +130,9 @@ - (void)setRootView:(UIView *)rootView toRootViewController:(UIViewController *)
194130
rootViewController.view = rootView;
195131
}
196132

197-
- (BOOL)runtimeSchedulerEnabled
133+
- (void)customizeRootView:(RCTRootView *)rootView
198134
{
199-
return YES;
135+
// Override point for customization after application launch.
200136
}
201137

202138
#pragma mark - UISceneDelegate
@@ -209,25 +145,6 @@ - (void)windowScene:(UIWindowScene *)windowScene
209145
[[NSNotificationCenter defaultCenter] postNotificationName:RCTWindowFrameDidChangeNotification object:self];
210146
}
211147

212-
#pragma mark - RCTCxxBridgeDelegate
213-
214-
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
215-
{
216-
_runtimeScheduler = std::make_shared<facebook::react::RuntimeScheduler>(RCTRuntimeExecutorFromBridge(bridge));
217-
if ([self newArchEnabled]) {
218-
std::shared_ptr<facebook::react::CallInvoker> callInvoker =
219-
std::make_shared<facebook::react::RuntimeSchedulerCallInvoker>(_runtimeScheduler);
220-
RCTTurboModuleManager *turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
221-
delegate:self
222-
jsInvoker:callInvoker];
223-
_contextContainer->erase("RuntimeScheduler");
224-
_contextContainer->insert("RuntimeScheduler", _runtimeScheduler);
225-
return RCTAppSetupDefaultJsExecutorFactory(bridge, turboModuleManager, _runtimeScheduler);
226-
} else {
227-
return RCTAppSetupJsExecutorFactoryForOldArch(bridge, _runtimeScheduler);
228-
}
229-
}
230-
231148
- (RCTColorSpace)defaultColorSpace
232149
{
233150
return RCTColorSpaceSRGB;
@@ -259,11 +176,33 @@ - (BOOL)bridgelessEnabled
259176
return [self newArchEnabled];
260177
}
261178

262-
#pragma mark - RCTComponentViewFactoryComponentProvider
179+
- (NSURL *)bundleURL
180+
{
181+
[NSException raise:@"RCTAppDelegate::bundleURL not implemented"
182+
format:@"Subclasses must implement a valid getBundleURL method"];
183+
return nullptr;
184+
}
263185

264-
- (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents
186+
#pragma mark - Bridge and Bridge Adapter properties
187+
188+
- (RCTBridge *)bridge
265189
{
266-
return @{};
190+
return self.rootViewFactory.bridge;
191+
}
192+
193+
- (RCTSurfacePresenterBridgeAdapter *)bridgeAdapter
194+
{
195+
return self.rootViewFactory.bridgeAdapter;
196+
}
197+
198+
- (void)setBridge:(RCTBridge *)bridge
199+
{
200+
self.rootViewFactory.bridge = bridge;
201+
}
202+
203+
- (void)setBridgeAdapter:(RCTSurfacePresenterBridgeAdapter *)bridgeAdapter
204+
{
205+
self.rootViewFactory.bridgeAdapter = bridgeAdapter;
267206
}
268207

269208
#pragma mark - RCTTurboModuleManagerDelegate
@@ -299,43 +238,33 @@ - (Class)getModuleClassFromName:(const char *)name
299238
return RCTAppSetupDefaultModuleFromClass(moduleClass);
300239
}
301240

302-
#pragma mark - New Arch Utilities
241+
#pragma mark - RCTComponentViewFactoryComponentProvider
303242

304-
- (void)createReactHost
243+
- (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents
305244
{
306-
__weak __typeof(self) weakSelf = self;
307-
_reactHost = [[RCTHost alloc] initWithBundleURL:[self bundleURL]
308-
hostDelegate:nil
309-
turboModuleManagerDelegate:self
310-
jsEngineProvider:^std::shared_ptr<facebook::react::JSRuntimeFactory>() {
311-
return [weakSelf createJSRuntimeFactory];
312-
}];
313-
[_reactHost setBundleURLProvider:^NSURL *() {
314-
return [weakSelf bundleURL];
315-
}];
316-
[_reactHost setContextContainerHandler:self];
317-
[_reactHost start];
245+
return @{};
318246
}
319247

320-
- (std::shared_ptr<facebook::react::JSRuntimeFactory>)createJSRuntimeFactory
248+
- (RCTRootViewFactory *)createRCTRootViewFactory
321249
{
322-
#if USE_HERMES
323-
return std::make_shared<facebook::react::RCTHermesInstance>(_reactNativeConfig, nullptr);
324-
#else
325-
return std::make_shared<facebook::react::RCTJscInstance>();
326-
#endif
327-
}
250+
RCTRootViewFactoryConfiguration *configuration =
251+
[[RCTRootViewFactoryConfiguration alloc] initWithBundleURL:self.bundleURL
252+
newArchEnabled:self.fabricEnabled
253+
turboModuleEnabled:self.turboModuleEnabled
254+
bridgelessEnabled:self.bridgelessEnabled];
328255

329-
- (void)didCreateContextContainer:(std::shared_ptr<facebook::react::ContextContainer>)contextContainer
330-
{
331-
contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
332-
}
256+
__weak __typeof(self) weakSelf = self;
257+
configuration.createRootViewWithBridge = ^UIView *(RCTBridge *bridge, NSString *moduleName, NSDictionary *initProps)
258+
{
259+
return [weakSelf createRootViewWithBridge:bridge moduleName:moduleName initProps:initProps];
260+
};
333261

334-
- (NSURL *)bundleURL
335-
{
336-
[NSException raise:@"RCTAppDelegate::bundleURL not implemented"
337-
format:@"Subclasses must implement a valid getBundleURL method"];
338-
return nullptr;
262+
configuration.createBridgeWithDelegate = ^RCTBridge *(id<RCTBridgeDelegate> delegate, NSDictionary *launchOptions)
263+
{
264+
return [weakSelf createBridgeWithDelegate:delegate launchOptions:launchOptions];
265+
};
266+
267+
return [[RCTRootViewFactory alloc] initWithConfiguration:configuration andTurboModuleManagerDelegate:self];
339268
}
340269

341270
@end

0 commit comments

Comments
 (0)