mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
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: https://github.com/facebook/react-native/pull/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
This commit is contained in:
committed by
Facebook GitHub Bot
parent
9f85a249fa
commit
ec928d7a66
@@ -8,6 +8,7 @@
|
||||
#import <React/RCTBridgeDelegate.h>
|
||||
#import <React/RCTConvert.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RCTRootViewFactory.h"
|
||||
|
||||
@class RCTBridge;
|
||||
@protocol RCTBridgeDelegate;
|
||||
@@ -58,9 +59,12 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// The window object, used to render the UViewControllers
|
||||
@property (nonatomic, strong, nonnull) UIWindow *window;
|
||||
@property (nonatomic, strong, nullable) RCTBridge *bridge;
|
||||
@property (nonatomic, nullable) RCTBridge *bridge;
|
||||
@property (nonatomic, strong, nullable) NSString *moduleName;
|
||||
@property (nonatomic, strong, nullable) NSDictionary *initialProps;
|
||||
@property (nonatomic, strong, nonnull) RCTRootViewFactory *rootViewFactory;
|
||||
|
||||
@property (nonatomic, nullable) RCTSurfacePresenterBridgeAdapter *bridgeAdapter;
|
||||
|
||||
/**
|
||||
* It creates a `RCTBridge` using a delegate and some launch options.
|
||||
@@ -127,18 +131,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (void)setRootView:(UIView *)rootView toRootViewController:(UIViewController *)rootViewController;
|
||||
|
||||
/// This method controls whether the App will use RuntimeScheduler. Only applicable in the legacy architecture.
|
||||
///
|
||||
/// @return: `YES` to use RuntimeScheduler, `NO` to use JavaScript scheduler. The default value is `YES`.
|
||||
- (BOOL)runtimeSchedulerEnabled;
|
||||
|
||||
/**
|
||||
* The default `RCTColorSpace` for the app. It defaults to `RCTColorSpaceSRGB`.
|
||||
*/
|
||||
@property (nonatomic, readonly) RCTColorSpace defaultColorSpace;
|
||||
|
||||
@property (nonatomic, strong) RCTSurfacePresenterBridgeAdapter *bridgeAdapter;
|
||||
|
||||
/// This method returns a map of Component Descriptors and Components classes that needs to be registered in the
|
||||
/// new renderer. The Component Descriptor is a string which represent the name used in JS to refer to the native
|
||||
/// component. The default implementation returns an empty dictionary. Subclasses can override this method to register
|
||||
|
||||
@@ -43,83 +43,29 @@
|
||||
#import <react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.h>
|
||||
#import <react/runtime/JSRuntimeFactory.h>
|
||||
|
||||
@interface RCTAppDelegate () <RCTComponentViewFactoryComponentProvider, RCTContextContainerHandling> {
|
||||
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
|
||||
facebook::react::ContextContainer::Shared _contextContainer;
|
||||
}
|
||||
@interface RCTAppDelegate () <RCTComponentViewFactoryComponentProvider>
|
||||
@end
|
||||
|
||||
static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabricEnabled)
|
||||
{
|
||||
NSMutableDictionary *mutableProps = [initialProps mutableCopy] ?: [NSMutableDictionary new];
|
||||
return mutableProps;
|
||||
}
|
||||
|
||||
@interface RCTAppDelegate () <RCTCxxBridgeDelegate> {
|
||||
std::shared_ptr<facebook::react::RuntimeScheduler> _runtimeScheduler;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation RCTAppDelegate {
|
||||
RCTHost *_reactHost;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
|
||||
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
|
||||
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@implementation RCTAppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
RCTSetNewArchEnabled([self newArchEnabled]);
|
||||
[RCTColorSpaceUtils applyDefaultColorSpace:self.defaultColorSpace];
|
||||
BOOL enableTM = self.turboModuleEnabled;
|
||||
BOOL fabricEnabled = self.fabricEnabled;
|
||||
BOOL enableBridgeless = self.bridgelessEnabled;
|
||||
RCTAppSetupPrepareApp(application, self.turboModuleEnabled);
|
||||
|
||||
NSDictionary *initProps = updateInitialProps([self prepareInitialProps], fabricEnabled);
|
||||
self.rootViewFactory = [self createRCTRootViewFactory];
|
||||
|
||||
RCTAppSetupPrepareApp(application, enableTM);
|
||||
UIView *rootView = [self.rootViewFactory viewWithModuleName:self.moduleName
|
||||
initialProperties:self.initialProps
|
||||
launchOptions:launchOptions];
|
||||
|
||||
UIView *rootView;
|
||||
if (enableBridgeless) {
|
||||
// Enable native view config interop only if both bridgeless mode and Fabric is enabled.
|
||||
RCTSetUseNativeViewConfigsInBridgelessMode(fabricEnabled);
|
||||
|
||||
// Enable TurboModule interop by default in Bridgeless mode
|
||||
RCTEnableTurboModuleInterop(YES);
|
||||
RCTEnableTurboModuleInteropBridgeProxy(YES);
|
||||
|
||||
[self createReactHost];
|
||||
if (self.newArchEnabled || self.fabricEnabled) {
|
||||
[RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self;
|
||||
RCTFabricSurface *surface = [_reactHost createSurfaceWithModuleName:self.moduleName initialProperties:initProps];
|
||||
|
||||
RCTSurfaceHostingProxyRootView *surfaceHostingProxyRootView = [[RCTSurfaceHostingProxyRootView alloc]
|
||||
initWithSurface:surface
|
||||
sizeMeasureMode:RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact];
|
||||
|
||||
rootView = (RCTRootView *)surfaceHostingProxyRootView;
|
||||
rootView.backgroundColor = [UIColor systemBackgroundColor];
|
||||
} else {
|
||||
if (!self.bridge) {
|
||||
self.bridge = [self createBridgeWithDelegate:self launchOptions:launchOptions];
|
||||
}
|
||||
if ([self newArchEnabled]) {
|
||||
self.bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:self.bridge
|
||||
contextContainer:_contextContainer];
|
||||
self.bridge.surfacePresenter = self.bridgeAdapter.surfacePresenter;
|
||||
|
||||
[RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self;
|
||||
}
|
||||
rootView = [self createRootViewWithBridge:self.bridge moduleName:self.moduleName initProps:initProps];
|
||||
}
|
||||
[self _logWarnIfCreateRootViewWithBridgeIsOverridden];
|
||||
[self customizeRootView:(RCTRootView *)rootView];
|
||||
|
||||
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
UIViewController *rootViewController = [self createRootViewController];
|
||||
[self setRootView:rootView toRootViewController:rootViewController];
|
||||
@@ -142,21 +88,11 @@ static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabri
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSDictionary *)prepareInitialProps
|
||||
{
|
||||
return self.initialProps;
|
||||
}
|
||||
|
||||
- (RCTBridge *)createBridgeWithDelegate:(id<RCTBridgeDelegate>)delegate launchOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
return [[RCTBridge alloc] initWithDelegate:delegate launchOptions:launchOptions];
|
||||
}
|
||||
|
||||
- (void)customizeRootView:(RCTRootView *)rootView
|
||||
{
|
||||
// Override point for customization after application launch.
|
||||
}
|
||||
|
||||
- (UIView *)createRootViewWithBridge:(RCTBridge *)bridge
|
||||
moduleName:(NSString *)moduleName
|
||||
initProps:(NSDictionary *)initProps
|
||||
@@ -194,9 +130,9 @@ static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabri
|
||||
rootViewController.view = rootView;
|
||||
}
|
||||
|
||||
- (BOOL)runtimeSchedulerEnabled
|
||||
- (void)customizeRootView:(RCTRootView *)rootView
|
||||
{
|
||||
return YES;
|
||||
// Override point for customization after application launch.
|
||||
}
|
||||
|
||||
#pragma mark - UISceneDelegate
|
||||
@@ -209,25 +145,6 @@ static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabri
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTWindowFrameDidChangeNotification object:self];
|
||||
}
|
||||
|
||||
#pragma mark - RCTCxxBridgeDelegate
|
||||
|
||||
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
|
||||
{
|
||||
_runtimeScheduler = std::make_shared<facebook::react::RuntimeScheduler>(RCTRuntimeExecutorFromBridge(bridge));
|
||||
if ([self newArchEnabled]) {
|
||||
std::shared_ptr<facebook::react::CallInvoker> callInvoker =
|
||||
std::make_shared<facebook::react::RuntimeSchedulerCallInvoker>(_runtimeScheduler);
|
||||
RCTTurboModuleManager *turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
|
||||
delegate:self
|
||||
jsInvoker:callInvoker];
|
||||
_contextContainer->erase("RuntimeScheduler");
|
||||
_contextContainer->insert("RuntimeScheduler", _runtimeScheduler);
|
||||
return RCTAppSetupDefaultJsExecutorFactory(bridge, turboModuleManager, _runtimeScheduler);
|
||||
} else {
|
||||
return RCTAppSetupJsExecutorFactoryForOldArch(bridge, _runtimeScheduler);
|
||||
}
|
||||
}
|
||||
|
||||
- (RCTColorSpace)defaultColorSpace
|
||||
{
|
||||
return RCTColorSpaceSRGB;
|
||||
@@ -259,11 +176,33 @@ static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabri
|
||||
return [self newArchEnabled];
|
||||
}
|
||||
|
||||
#pragma mark - RCTComponentViewFactoryComponentProvider
|
||||
|
||||
- (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents
|
||||
- (NSURL *)bundleURL
|
||||
{
|
||||
return @{};
|
||||
[NSException raise:@"RCTAppDelegate::bundleURL not implemented"
|
||||
format:@"Subclasses must implement a valid getBundleURL method"];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#pragma mark - Bridge and Bridge Adapter properties
|
||||
|
||||
- (RCTBridge *)bridge
|
||||
{
|
||||
return self.rootViewFactory.bridge;
|
||||
}
|
||||
|
||||
- (RCTSurfacePresenterBridgeAdapter *)bridgeAdapter
|
||||
{
|
||||
return self.rootViewFactory.bridgeAdapter;
|
||||
}
|
||||
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
{
|
||||
self.rootViewFactory.bridge = bridge;
|
||||
}
|
||||
|
||||
- (void)setBridgeAdapter:(RCTSurfacePresenterBridgeAdapter *)bridgeAdapter
|
||||
{
|
||||
self.rootViewFactory.bridgeAdapter = bridgeAdapter;
|
||||
}
|
||||
|
||||
#pragma mark - RCTTurboModuleManagerDelegate
|
||||
@@ -299,43 +238,33 @@ static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabri
|
||||
return RCTAppSetupDefaultModuleFromClass(moduleClass);
|
||||
}
|
||||
|
||||
#pragma mark - New Arch Utilities
|
||||
#pragma mark - RCTComponentViewFactoryComponentProvider
|
||||
|
||||
- (void)createReactHost
|
||||
- (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents
|
||||
{
|
||||
return @{};
|
||||
}
|
||||
|
||||
- (RCTRootViewFactory *)createRCTRootViewFactory
|
||||
{
|
||||
RCTRootViewFactoryConfiguration *configuration =
|
||||
[[RCTRootViewFactoryConfiguration alloc] initWithBundleURL:self.bundleURL
|
||||
newArchEnabled:self.fabricEnabled
|
||||
turboModuleEnabled:self.turboModuleEnabled
|
||||
bridgelessEnabled:self.bridgelessEnabled];
|
||||
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
_reactHost = [[RCTHost alloc] initWithBundleURL:[self bundleURL]
|
||||
hostDelegate:nil
|
||||
turboModuleManagerDelegate:self
|
||||
jsEngineProvider:^std::shared_ptr<facebook::react::JSRuntimeFactory>() {
|
||||
return [weakSelf createJSRuntimeFactory];
|
||||
}];
|
||||
[_reactHost setBundleURLProvider:^NSURL *() {
|
||||
return [weakSelf bundleURL];
|
||||
}];
|
||||
[_reactHost setContextContainerHandler:self];
|
||||
[_reactHost start];
|
||||
}
|
||||
configuration.createRootViewWithBridge = ^UIView *(RCTBridge *bridge, NSString *moduleName, NSDictionary *initProps)
|
||||
{
|
||||
return [weakSelf createRootViewWithBridge:bridge moduleName:moduleName initProps:initProps];
|
||||
};
|
||||
|
||||
- (std::shared_ptr<facebook::react::JSRuntimeFactory>)createJSRuntimeFactory
|
||||
{
|
||||
#if USE_HERMES
|
||||
return std::make_shared<facebook::react::RCTHermesInstance>(_reactNativeConfig, nullptr);
|
||||
#else
|
||||
return std::make_shared<facebook::react::RCTJscInstance>();
|
||||
#endif
|
||||
}
|
||||
configuration.createBridgeWithDelegate = ^RCTBridge *(id<RCTBridgeDelegate> delegate, NSDictionary *launchOptions)
|
||||
{
|
||||
return [weakSelf createBridgeWithDelegate:delegate launchOptions:launchOptions];
|
||||
};
|
||||
|
||||
- (void)didCreateContextContainer:(std::shared_ptr<facebook::react::ContextContainer>)contextContainer
|
||||
{
|
||||
contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
|
||||
}
|
||||
|
||||
- (NSURL *)bundleURL
|
||||
{
|
||||
[NSException raise:@"RCTAppDelegate::bundleURL not implemented"
|
||||
format:@"Subclasses must implement a valid getBundleURL method"];
|
||||
return nullptr;
|
||||
return [[RCTRootViewFactory alloc] initWithConfiguration:configuration andTurboModuleManagerDelegate:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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 <React/RCTBridge.h>
|
||||
#import <React/RCTRootView.h>
|
||||
#import <React/RCTUtils.h>
|
||||
|
||||
@protocol RCTCxxBridgeDelegate;
|
||||
@protocol RCTComponentViewFactoryComponentProvider;
|
||||
@protocol RCTTurboModuleManagerDelegate;
|
||||
@class RCTBridge;
|
||||
@class RCTRootView;
|
||||
@class RCTSurfacePresenterBridgeAdapter;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#pragma mark - Blocks' definitions
|
||||
typedef UIView *_Nonnull (
|
||||
^RCTCreateRootViewWithBridgeBlock)(RCTBridge *bridge, NSString *moduleName, NSDictionary *initProps);
|
||||
typedef RCTBridge *_Nonnull (
|
||||
^RCTCreateBridgeWithDelegateBlock)(id<RCTBridgeDelegate> delegate, NSDictionary *launchOptions);
|
||||
|
||||
#pragma mark - RCTRootViewFactory Configuration
|
||||
@interface RCTRootViewFactoryConfiguration : NSObject
|
||||
|
||||
/// This property controls whether the App will use the Fabric renderer of the New Architecture or not.
|
||||
@property (nonatomic, assign, readonly) BOOL fabricEnabled;
|
||||
|
||||
/// This property controls whether React Native's new initialization layer is enabled.
|
||||
@property (nonatomic, assign, readonly) BOOL bridgelessEnabled;
|
||||
|
||||
/// This method controls whether the `turboModules` feature of the New Architecture is turned on or off
|
||||
@property (nonatomic, assign, readonly) BOOL turboModuleEnabled;
|
||||
|
||||
/// Return the bundle URL for the main bundle.
|
||||
@property (nonatomic) NSURL *bundleURL;
|
||||
|
||||
/**
|
||||
* Use this method to initialize a new instance of `RCTRootViewFactoryConfiguration` by passing a `bundleURL`
|
||||
*
|
||||
* Which is the location of the JavaScript source file. When running from the packager
|
||||
* this should be an absolute URL, e.g. `http://localhost:8081/index.ios.bundle`.
|
||||
* When running from a locally bundled JS file, this should be a `file://` url
|
||||
* pointing to a path inside the app resources, e.g. `file://.../main.jsbundle`.
|
||||
*
|
||||
*/
|
||||
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
|
||||
newArchEnabled:(BOOL)newArchEnabled
|
||||
turboModuleEnabled:(BOOL)turboModuleEnabled
|
||||
bridgelessEnabled:(BOOL)bridgelessEnabled;
|
||||
|
||||
/**
|
||||
* Block that allows to override logic of creating root view instance.
|
||||
* It creates a `UIView` starting from a bridge, a module name and a set of initial properties.
|
||||
* By default, it is invoked using the bridge created by `RCTCreateBridgeWithDelegateBlock` (or the default
|
||||
* implementation) and the `moduleName` variable comes from `viewWithModuleName:initialProperties:launchOptions` of
|
||||
* `RCTRootViewFactory`.
|
||||
*
|
||||
* @parameter: bridge - an instance of the `RCTBridge` object.
|
||||
* @parameter: moduleName - the name of the app, used by Metro to resolve the module.
|
||||
* @parameter: initProps - a set of initial properties.
|
||||
*
|
||||
* @returns: a UIView properly configured with a bridge for React Native.
|
||||
*/
|
||||
@property (nonatomic, nullable) RCTCreateRootViewWithBridgeBlock createRootViewWithBridge;
|
||||
|
||||
/**
|
||||
* Block that allows to override default behavior of creating bridge.
|
||||
* It should return `RCTBridge` using a delegate and some launch options.
|
||||
*
|
||||
* By default, it is invoked passing `self` as a delegate.
|
||||
*
|
||||
* @parameter: delegate - an object that implements the `RCTBridgeDelegate` protocol.
|
||||
* @parameter: launchOptions - a dictionary with a set of options.
|
||||
*
|
||||
* @returns: a newly created instance of RCTBridge.
|
||||
*/
|
||||
@property (nonatomic, nullable) RCTCreateBridgeWithDelegateBlock createBridgeWithDelegate;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - RCTRootViewFactory
|
||||
/**
|
||||
* The RCTRootViewFactory is an utility class that encapsulates the logic of creating a new RCTRootView based on the
|
||||
* current state of the environment. It allows you to initialize your app root view for old architecture, new
|
||||
* architecture and bridgless mode.
|
||||
*
|
||||
* This class is used to initalize rootView in RCTAppDelegate, but you can also use it separately.
|
||||
*
|
||||
* Create a new instance of this class (make sure to retain it) and call the
|
||||
* `viewWithModuleName:initialProperties:launchOptions` method to create new RCTRootView.
|
||||
*/
|
||||
@interface RCTRootViewFactory : NSObject
|
||||
|
||||
@property (nonatomic, strong, nullable) RCTBridge *bridge;
|
||||
@property (nonatomic, strong, nullable) RCTSurfacePresenterBridgeAdapter *bridgeAdapter;
|
||||
|
||||
- (instancetype)initWithConfiguration:(RCTRootViewFactoryConfiguration *)configuration
|
||||
andTurboModuleManagerDelegate:(id<RCTTurboModuleManagerDelegate>)turboModuleManagerDelegate;
|
||||
|
||||
/**
|
||||
* This method can be used to create new RCTRootViews on demand.
|
||||
*
|
||||
* @parameter: moduleName - the name of the app, used by Metro to resolve the module.
|
||||
* @parameter: initialProperties - a set of initial properties.
|
||||
* @parameter: moduleName - a dictionary with a set of options.
|
||||
*/
|
||||
- (UIView *_Nonnull)viewWithModuleName:(NSString *)moduleName
|
||||
initialProperties:(NSDictionary *__nullable)initialProperties
|
||||
launchOptions:(NSDictionary *__nullable)launchOptions;
|
||||
|
||||
- (UIView *_Nonnull)viewWithModuleName:(NSString *)moduleName
|
||||
initialProperties:(NSDictionary *__nullable)initialProperties;
|
||||
|
||||
- (UIView *_Nonnull)viewWithModuleName:(NSString *)moduleName;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* 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 "RCTRootViewFactory.h"
|
||||
#import <React/RCTCxxBridgeDelegate.h>
|
||||
#import <React/RCTLog.h>
|
||||
#import <React/RCTRootView.h>
|
||||
#import <React/RCTSurfacePresenterBridgeAdapter.h>
|
||||
#import <React/RCTUtils.h>
|
||||
#import <react/renderer/runtimescheduler/RuntimeScheduler.h>
|
||||
#import "RCTAppDelegate.h"
|
||||
#import "RCTAppSetupUtils.h"
|
||||
|
||||
#if RN_DISABLE_OSS_PLUGIN_HEADER
|
||||
#import <RCTTurboModulePlugin/RCTTurboModulePlugin.h>
|
||||
#else
|
||||
#import <React/CoreModulesPlugins.h>
|
||||
#endif
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
#import <React/RCTComponentViewFactory.h>
|
||||
#import <React/RCTComponentViewProtocol.h>
|
||||
#import <React/RCTFabricSurface.h>
|
||||
#import <React/RCTSurfaceHostingProxyRootView.h>
|
||||
#import <React/RCTSurfacePresenter.h>
|
||||
#import <ReactCommon/RCTContextContainerHandling.h>
|
||||
#if USE_HERMES
|
||||
#import <ReactCommon/RCTHermesInstance.h>
|
||||
#else
|
||||
#import <ReactCommon/RCTJscInstance.h>
|
||||
#endif
|
||||
#import <ReactCommon/RCTHost+Internal.h>
|
||||
#import <ReactCommon/RCTHost.h>
|
||||
#import <ReactCommon/RCTTurboModuleManager.h>
|
||||
#import <react/config/ReactNativeConfig.h>
|
||||
#import <react/renderer/runtimescheduler/RuntimeScheduler.h>
|
||||
#import <react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.h>
|
||||
#import <react/runtime/JSRuntimeFactory.h>
|
||||
|
||||
static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
||||
|
||||
static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabricEnabled)
|
||||
{
|
||||
NSMutableDictionary *mutableProps = initialProps != NULL ? [initialProps mutableCopy] : [NSMutableDictionary new];
|
||||
// Hardcoding the Concurrent Root as it it not recommended to
|
||||
// have the concurrentRoot turned off when Fabric is enabled.
|
||||
mutableProps[kRNConcurrentRoot] = @(isFabricEnabled);
|
||||
return mutableProps;
|
||||
}
|
||||
|
||||
@implementation RCTRootViewFactoryConfiguration
|
||||
|
||||
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
|
||||
newArchEnabled:(BOOL)newArchEnabled
|
||||
turboModuleEnabled:(BOOL)turboModuleEnabled
|
||||
bridgelessEnabled:(BOOL)bridgelessEnabled
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_bundleURL = bundleURL;
|
||||
_fabricEnabled = newArchEnabled;
|
||||
_turboModuleEnabled = turboModuleEnabled;
|
||||
_bridgelessEnabled = bridgelessEnabled;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTRootViewFactory () <RCTContextContainerHandling> {
|
||||
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
|
||||
facebook::react::ContextContainer::Shared _contextContainer;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface RCTRootViewFactory () <RCTCxxBridgeDelegate> {
|
||||
std::shared_ptr<facebook::react::RuntimeScheduler> _runtimeScheduler;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation RCTRootViewFactory {
|
||||
RCTHost *_reactHost;
|
||||
RCTRootViewFactoryConfiguration *_configuration;
|
||||
__weak id<RCTTurboModuleManagerDelegate> _turboModuleManagerDelegate;
|
||||
}
|
||||
|
||||
- (instancetype)initWithConfiguration:(RCTRootViewFactoryConfiguration *)configuration
|
||||
andTurboModuleManagerDelegate:(id<RCTTurboModuleManagerDelegate>)turboModuleManagerDelegate
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_configuration = configuration;
|
||||
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
|
||||
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
|
||||
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
|
||||
_turboModuleManagerDelegate = turboModuleManagerDelegate;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (UIView *)viewWithModuleName:(NSString *)moduleName initialProperties:(NSDictionary *)initialProperties
|
||||
{
|
||||
return [self viewWithModuleName:moduleName initialProperties:initialProperties launchOptions:nil];
|
||||
}
|
||||
|
||||
- (UIView *)viewWithModuleName:(NSString *)moduleName
|
||||
{
|
||||
return [self viewWithModuleName:moduleName initialProperties:nil launchOptions:nil];
|
||||
}
|
||||
|
||||
- (UIView *)viewWithModuleName:(NSString *)moduleName
|
||||
initialProperties:(NSDictionary *)initialProperties
|
||||
launchOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
NSDictionary *initProps = updateInitialProps(initialProperties, self->_configuration.fabricEnabled);
|
||||
|
||||
if (self->_configuration.bridgelessEnabled) {
|
||||
// Enable native view config interop only if both bridgeless mode and Fabric is enabled.
|
||||
RCTSetUseNativeViewConfigsInBridgelessMode(self->_configuration.fabricEnabled);
|
||||
|
||||
// Enable TurboModule interop by default in Bridgeless mode
|
||||
RCTEnableTurboModuleInterop(YES);
|
||||
RCTEnableTurboModuleInteropBridgeProxy(YES);
|
||||
|
||||
[self createReactHostIfNeeded];
|
||||
|
||||
RCTFabricSurface *surface = [_reactHost createSurfaceWithModuleName:moduleName initialProperties:initProps];
|
||||
|
||||
RCTSurfaceHostingProxyRootView *surfaceHostingProxyRootView = [[RCTSurfaceHostingProxyRootView alloc]
|
||||
initWithSurface:surface
|
||||
sizeMeasureMode:RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact];
|
||||
|
||||
return surfaceHostingProxyRootView;
|
||||
}
|
||||
|
||||
[self createBridgeIfNeeded:launchOptions];
|
||||
[self createBridgeAdapterIfNeeded];
|
||||
|
||||
if (self->_configuration.createRootViewWithBridge != nil) {
|
||||
return self->_configuration.createRootViewWithBridge(self.bridge, moduleName, initProps);
|
||||
}
|
||||
|
||||
return [self createRootViewWithBridge:self.bridge moduleName:moduleName initProps:initProps];
|
||||
}
|
||||
|
||||
- (RCTBridge *)createBridgeWithDelegate:(id<RCTBridgeDelegate>)delegate launchOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
return [[RCTBridge alloc] initWithDelegate:delegate launchOptions:launchOptions];
|
||||
}
|
||||
|
||||
- (UIView *)createRootViewWithBridge:(RCTBridge *)bridge
|
||||
moduleName:(NSString *)moduleName
|
||||
initProps:(NSDictionary *)initProps
|
||||
{
|
||||
BOOL enableFabric = self->_configuration.fabricEnabled;
|
||||
UIView *rootView = RCTAppSetupDefaultRootView(bridge, moduleName, initProps, enableFabric);
|
||||
|
||||
rootView.backgroundColor = [UIColor systemBackgroundColor];
|
||||
|
||||
return rootView;
|
||||
}
|
||||
|
||||
#pragma mark - RCTCxxBridgeDelegate
|
||||
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
|
||||
{
|
||||
_runtimeScheduler = std::make_shared<facebook::react::RuntimeScheduler>(RCTRuntimeExecutorFromBridge(bridge));
|
||||
if (RCTIsNewArchEnabled()) {
|
||||
std::shared_ptr<facebook::react::CallInvoker> callInvoker =
|
||||
std::make_shared<facebook::react::RuntimeSchedulerCallInvoker>(_runtimeScheduler);
|
||||
RCTTurboModuleManager *turboModuleManager =
|
||||
[[RCTTurboModuleManager alloc] initWithBridge:bridge
|
||||
delegate:_turboModuleManagerDelegate
|
||||
jsInvoker:callInvoker];
|
||||
_contextContainer->erase("RuntimeScheduler");
|
||||
_contextContainer->insert("RuntimeScheduler", _runtimeScheduler);
|
||||
return RCTAppSetupDefaultJsExecutorFactory(bridge, turboModuleManager, _runtimeScheduler);
|
||||
} else {
|
||||
return RCTAppSetupJsExecutorFactoryForOldArch(bridge, _runtimeScheduler);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)createBridgeIfNeeded:(NSDictionary *)launchOptions
|
||||
{
|
||||
if (self.bridge != nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->_configuration.createBridgeWithDelegate != nil) {
|
||||
self.bridge = self->_configuration.createBridgeWithDelegate(self, launchOptions);
|
||||
} else {
|
||||
self.bridge = [self createBridgeWithDelegate:self launchOptions:launchOptions];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)createBridgeAdapterIfNeeded
|
||||
{
|
||||
if (!self->_configuration.fabricEnabled || self.bridgeAdapter) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:self.bridge
|
||||
contextContainer:_contextContainer];
|
||||
self.bridge.surfacePresenter = self.bridgeAdapter.surfacePresenter;
|
||||
}
|
||||
|
||||
#pragma mark - New Arch Utilities
|
||||
|
||||
- (void)createReactHostIfNeeded
|
||||
{
|
||||
if (_reactHost) {
|
||||
return;
|
||||
}
|
||||
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
_reactHost = [[RCTHost alloc] initWithBundleURL:[self bundleURL]
|
||||
hostDelegate:nil
|
||||
turboModuleManagerDelegate:_turboModuleManagerDelegate
|
||||
jsEngineProvider:^std::shared_ptr<facebook::react::JSRuntimeFactory>() {
|
||||
return [weakSelf createJSRuntimeFactory];
|
||||
}];
|
||||
[_reactHost setBundleURLProvider:^NSURL *() {
|
||||
return [weakSelf bundleURL];
|
||||
}];
|
||||
[_reactHost setContextContainerHandler:self];
|
||||
[_reactHost start];
|
||||
}
|
||||
|
||||
- (std::shared_ptr<facebook::react::JSRuntimeFactory>)createJSRuntimeFactory
|
||||
{
|
||||
#if USE_HERMES
|
||||
return std::make_shared<facebook::react::RCTHermesInstance>(_reactNativeConfig, nullptr);
|
||||
#else
|
||||
return std::make_shared<facebook::react::RCTJscInstance>();
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)didCreateContextContainer:(std::shared_ptr<facebook::react::ContextContainer>)contextContainer
|
||||
{
|
||||
contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
|
||||
}
|
||||
|
||||
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
|
||||
{
|
||||
return [self bundleURL];
|
||||
}
|
||||
|
||||
- (NSURL *)bundleURL
|
||||
{
|
||||
return self->_configuration.bundleURL;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -46,9 +46,8 @@ RCT_EXPORT_MODULE();
|
||||
|
||||
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
|
||||
|
||||
_resizableRootView = [[RCTRootView alloc] initWithBridge:appDelegate.bridge
|
||||
moduleName:@"RootViewSizeFlexibilityExampleApp"
|
||||
initialProperties:@{}];
|
||||
_resizableRootView =
|
||||
(RCTRootView *)[appDelegate.rootViewFactory viewWithModuleName:@"RootViewSizeFlexibilityExampleApp"];
|
||||
|
||||
[_resizableRootView setSizeFlexibility:RCTRootViewSizeFlexibilityHeight];
|
||||
|
||||
|
||||
@@ -41,9 +41,8 @@ RCT_EXPORT_MODULE();
|
||||
|
||||
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
|
||||
|
||||
_rootView = [[RCTRootView alloc] initWithBridge:appDelegate.bridge
|
||||
moduleName:@"SetPropertiesExampleApp"
|
||||
initialProperties:@{@"color" : @"beige"}];
|
||||
_rootView = (RCTRootView *)[appDelegate.rootViewFactory viewWithModuleName:@"SetPropertiesExampleApp"
|
||||
initialProperties:@{@"color" : @"beige"}];
|
||||
|
||||
_button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||
[_button setTitle:@"Native Button" forState:UIControlStateNormal];
|
||||
|
||||
Reference in New Issue
Block a user