Create RCTAppDelegate to simplify New Architecture Setup (#34384)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/34384

This Diff aims to create a RCTAppDelegate library to offer a subclass which automates some operations required to set up the new architecture.

## Changelog
[iOS][Added] - Added the RCTAppDelegate library

Reviewed By: cortinico

Differential Revision: D38580424

fbshipit-source-id: 38f6c4b8ff2790a2ce9e23d385b36307701cffb7
This commit is contained in:
Riccardo Cipolleschi 2022-08-15 01:30:55 -07:00 коммит произвёл Facebook GitHub Bot
Родитель 0e316ec671
Коммит 7cc2d1a249
10 изменённых файлов: 269 добавлений и 133 удалений

Просмотреть файл

@ -6,11 +6,16 @@
*/
#import <UIKit/UIKit.h>
#import <React/RCTBridge.h>
#import <React/RCTBridgeDelegate.h>
#if RCT_NEW_ARCH_ENABLED
// When the new architecture is enabled, the RCTAppDelegate imports some additional headers
#import <React/RCTCxxBridgeDelegate.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
#import <ReactCommon/RCTTurboModuleManager.h>
#endif
/**
@ -31,6 +36,8 @@
object.
*
* Overridable methods (New Architecture):
* - (BOOL)concurrentRootEnabled
* - (NSDictionary *)prepareInitialProps
* - (Class)getModuleClassFromName:(const char *)name
* - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
@ -39,19 +46,29 @@
(const facebook::react::ObjCTurboModule::InitParams &)params
* - (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
*/
@interface RCTAppDelegate : UIResponder <UIApplicationDelegate>
@interface RCTAppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate>
/// The window object, used to render the UViewControllers
@property (nonatomic, strong) UIWindow *window;
@property (nonatomic, strong) RCTBridge *bridge;
@property (nonatomic, strong) NSString *moduleName;
@end
#if RCT_NEW_ARCH_ENABLED
/// Extension that makes the RCTAppDelegate conform to New Architecture delegates
@interface RCTAppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate>
@interface RCTAppDelegate () <RCTTurboModuleManagerDelegate, RCTCxxBridgeDelegate>
/// The TurboModule manager
@property (nonatomic, strong) RCTTurboModuleManager *turboModuleManager;
@property (nonatomic, strong) RCTSurfacePresenterBridgeAdapter *bridgeAdapter;
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
///
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
/// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`.
- (BOOL)concurrentRootEnabled;
@end
#endif

Просмотреть файл

@ -0,0 +1,129 @@
/*
* 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 "RCTAppDelegate.h"
#import <React/RCTAppSetupUtils.h>
#import <React/RCTRootView.h>
#if RCT_NEW_ARCH_ENABLED
#import <React/CoreModulesPlugins.h>
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
#import <React/RCTSurfacePresenter.h>
#import <react/config/ReactNativeConfig.h>
static NSString *const kRNConcurrentRoot = @"concurrentRoot";
@interface RCTAppDelegate () {
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
facebook::react::ContextContainer::Shared _contextContainer;
}
@end
#endif
@implementation RCTAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RCTAppSetupPrepareApp(application);
if (!self.bridge) {
self.bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
}
#if RCT_NEW_ARCH_ENABLED
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
self.bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:self.bridge contextContainer:_contextContainer];
self.bridge.surfacePresenter = self.bridgeAdapter.surfacePresenter;
#endif
NSDictionary *initProps = [self prepareInitialProps];
UIView *rootView = RCTAppSetupDefaultRootView(self.bridge, self.moduleName, initProps);
if (@available(iOS 13.0, *)) {
rootView.backgroundColor = [UIColor systemBackgroundColor];
} else {
rootView.backgroundColor = [UIColor whiteColor];
}
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
[NSException
raise:@"RCTBridgeDelegate::sourceURLForBridge not implemented"
format:@"Subclasses must implement a valid sourceURLForBridge method"
];
return nil;
}
- (BOOL)concurrentRootEnabled
{
[NSException
raise:@"concurrentRootEnabled not implemented"
format:@"Subclasses must implement a valid concurrentRootEnabled method"
];
return true;
}
- (NSDictionary *)prepareInitialProps
{
NSMutableDictionary *initProps = [NSMutableDictionary new];
#ifdef RCT_NEW_ARCH_ENABLED
initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]);
#endif
return initProps;
}
#if RCT_NEW_ARCH_ENABLED
#pragma mark - RCTCxxBridgeDelegate
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
{
self.turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
delegate:self
jsInvoker:bridge.jsCallInvoker];
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
}
#pragma mark RCTTurboModuleManagerDelegate
- (Class)getModuleClassFromName:(const char *)name
{
return RCTCoreModulesClassProvider(name);
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
{
return nullptr;
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
initParams:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return nullptr;
}
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
{
return RCTAppSetupDefaultModuleFromClass(moduleClass);
}
#endif
@end

Просмотреть файл

@ -0,0 +1,57 @@
# 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.
require "json"
package = JSON.parse(File.read(File.join(__dir__, "..", "..", "package.json")))
version = package['version']
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1")
else
source[:tag] = "v#{version}"
end
folly_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1'
folly_compiler_flags = folly_flags + ' ' + '-Wno-comma -Wno-shorten-64-to-32'
new_arch_enabled_flag="RCT_NEW_ARCH_ENABLED"
is_new_arch_enabled = ENV[new_arch_enabled_flag] == "1"
other_cflags = "$(inherited) -DRN_FABRIC_ENABLED " + folly_flags + (is_new_arch_enabled ? " -D"+"RCT_NEW_ARCH_ENABLED" : "")
Pod::Spec.new do |s|
s.name = "React-RCTAppDelegate"
s.version = version
s.summary = "An utility library to simplify common operations for the New Architecture"
s.homepage = "https://reactnative.dev/"
s.documentation_url = "https://reactnative.dev/docs/actionsheetios"
s.license = package["license"]
s.author = "Facebook, Inc. and its affiliates"
s.platforms = { :ios => "12.4" }
s.source = source
s.source_files = "**/*.{c,h,m,mm,S,cpp}"
# This guard prevent to install the dependencies when we run `pod install` in the old architecture.
s.compiler_flags = other_cflags
s.pod_target_xcconfig = {
"HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Headers/Private/React-Core\" \"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/RCT-Folly\" \"${PODS_ROOT}/Headers/Public/React-hermes\" \"${PODS_ROOT}/Headers/Public/hermes-engine\" \"${PODS_ROOT}/Headers/Public/FlipperKit\" \"$(PODS_ROOT)/Headers/Public/ReactCommon\" \"$(PODS_ROOT)/Headers/Public/React-RCTFabric\"",
"OTHER_CPLUSPLUSFLAGS" => other_cflags,
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
}
s.user_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/Headers/Private/React-Core\""}
s.dependency "React-Core"
s.dependency "RCT-Folly"
s.dependency "RCTRequired"
s.dependency "RCTTypeSafety"
s.dependency "ReactCommon/turbomodule/core"
if is_new_arch_enabled
s.dependency "React-RCTFabric"
s.dependency "React-graphics"
end
end

Просмотреть файл

@ -1,55 +0,0 @@
/*
* 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 "RCTAppDelegate.h"
#import <React/RCTAppSetupUtils.h>
#if RCT_NEW_ARCH_ENABLED
#import <React/CoreModulesPlugins.h>
#endif
@implementation RCTAppDelegate
#if RCT_NEW_ARCH_ENABLED
#pragma mark - RCTCxxBridgeDelegate
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
{
self.turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
delegate:self
jsInvoker:bridge.jsCallInvoker];
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
}
#pragma mark RCTTurboModuleManagerDelegate
- (Class)getModuleClassFromName:(const char *)name
{
return RCTCoreModulesClassProvider(name);
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
{
return nullptr;
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
initParams:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return nullptr;
}
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
{
return RCTAppSetupDefaultModuleFromClass(moduleClass);
}
#endif
@end

Просмотреть файл

@ -17,7 +17,7 @@ if USE_FRAMEWORKS
use_frameworks!
end
def pods(options = {}, use_flipper: false)
def pods(options = {}, use_flipper: !IN_CI && !USE_FRAMEWORKS)
project 'RNTesterPods.xcodeproj'
fabric_enabled = true
@ -53,7 +53,7 @@ def pods(options = {}, use_flipper: false)
end
target 'RNTester' do
pods({}, :use_flipper => !IN_CI && !USE_FRAMEWORKS)
pods()
end
target 'RNTesterUnitTests' do

Просмотреть файл

@ -626,6 +626,12 @@ PODS:
- React-Core/RCTAnimationHeaders (= 1000.0.0)
- React-jsi (= 1000.0.0)
- ReactCommon/turbomodule/core (= 1000.0.0)
- React-RCTAppDelegate (1000.0.0):
- RCT-Folly
- RCTRequired
- RCTTypeSafety
- React-Core
- ReactCommon/turbomodule/core
- React-RCTBlob (1000.0.0):
- RCT-Folly (= 2021.07.22.00)
- React-Codegen (= 1000.0.0)
@ -771,6 +777,7 @@ DEPENDENCIES:
- React-perflogger (from `../../ReactCommon/reactperflogger`)
- React-RCTActionSheet (from `../../Libraries/ActionSheetIOS`)
- React-RCTAnimation (from `../../Libraries/NativeAnimation`)
- React-RCTAppDelegate (from `../../Libraries/AppDelegate`)
- React-RCTBlob (from `../../Libraries/Blob`)
- React-RCTFabric (from `../../React`)
- React-RCTImage (from `../../Libraries/Image`)
@ -855,6 +862,8 @@ EXTERNAL SOURCES:
:path: "../../Libraries/ActionSheetIOS"
React-RCTAnimation:
:path: "../../Libraries/NativeAnimation"
React-RCTAppDelegate:
:path: "../../Libraries/AppDelegate"
React-RCTBlob:
:path: "../../Libraries/Blob"
React-RCTFabric:
@ -924,6 +933,7 @@ SPEC CHECKSUMS:
React-perflogger: c4fdd48988c2d3047186fc1bc1772d634cfca2ea
React-RCTActionSheet: 166fd1df85ac10219466b45d12a5884d3eaceac1
React-RCTAnimation: d6127046c6bb44bd3e67b7503c4ad7f91131b58e
React-RCTAppDelegate: 475ca9b80e26c1c4aed93ce04363092fa78cf788
React-RCTBlob: 68675c89ebe6edf310dddd0774ba07b685f090a9
React-RCTFabric: a98a6effece6719669b8c6b4d2c33fb0edddc613
React-RCTImage: 6de9f0f4402af859849e97cc73a56a52f400f4c9
@ -942,6 +952,6 @@ SPEC CHECKSUMS:
Yoga: 1b1a12ff3d86a10565ea7cbe057d42f5e5fb2a07
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
PODFILE CHECKSUM: 54d9bd86f3c8151531bd4da1d3ba2e2e1f9a6ca9
PODFILE CHECKSUM: c3d82494e47e0adce02921f2ed9dba2427f0a9ff
COCOAPODS: 1.11.3

Просмотреть файл

@ -431,6 +431,7 @@
E7DB209C22B2BA84005AC45F /* Frameworks */,
E7DB209D22B2BA84005AC45F /* Resources */,
9A596313B3964A4DEB794409 /* [CP] Copy Pods Resources */,
284FCEB787673BF7B938D5E0 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -451,6 +452,7 @@
E7DB215022B2F332005AC45F /* Frameworks */,
E7DB215122B2F332005AC45F /* Resources */,
98E057AC8860597818FB485A /* [CP] Copy Pods Resources */,
5814D608FC5071AB8F979BB0 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -551,6 +553,23 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
284FCEB787673BF7B938D5E0 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
2FCDFB64B37634EC8EC3139B /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -590,6 +609,23 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
5814D608FC5071AB8F979BB0 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-RNTesterIntegrationTests/Pods-RNTesterIntegrationTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-RNTesterIntegrationTests/Pods-RNTesterIntegrationTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RNTesterIntegrationTests/Pods-RNTesterIntegrationTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
5CF0FD27207FC6EC00C13D65 /* Start Metro */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;

Просмотреть файл

@ -60,6 +60,7 @@ def use_react_native! (
pod 'React', :path => "#{prefix}/"
pod 'React-Core', :path => "#{prefix}/"
pod 'React-CoreModules', :path => "#{prefix}/React/CoreModules"
pod 'React-RCTAppDelegate', :path => "#{prefix}/Libraries/AppDelegate"
pod 'React-RCTActionSheet', :path => "#{prefix}/Libraries/ActionSheetIOS"
pod 'React-RCTAnimation', :path => "#{prefix}/Libraries/NativeAnimation"
pod 'React-RCTBlob', :path => "#{prefix}/Libraries/Blob"

Просмотреть файл

@ -1,7 +1,6 @@
#import <React/RCTAppDelegate.h>
#import <React/RCTBridgeDelegate.h>
#import <React-RCTAppDelegate/RCTAppDelegate.h>
#import <UIKit/UIKit.h>
@interface AppDelegate : RCTAppDelegate <RCTBridgeDelegate>
@interface AppDelegate : RCTAppDelegate
@end

Просмотреть файл

@ -1,81 +1,13 @@
#import "AppDelegate.h"
#import <React/RCTAppSetupUtils.h>
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#if RCT_NEW_ARCH_ENABLED
#import <React/CoreModulesPlugins.h>
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
#import <React/RCTSurfacePresenter.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
#import <react/config/ReactNativeConfig.h>
static NSString *const kRNConcurrentRoot = @"concurrentRoot";
@interface AppDelegate () {
RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
facebook::react::ContextContainer::Shared _contextContainer;
}
@end
#endif
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RCTAppSetupPrepareApp(application);
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
#if RCT_NEW_ARCH_ENABLED
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
#endif
NSDictionary *initProps = [self prepareInitialProps];
UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"HelloWorld", initProps);
if (@available(iOS 13.0, *)) {
rootView.backgroundColor = [UIColor systemBackgroundColor];
} else {
rootView.backgroundColor = [UIColor whiteColor];
}
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
///
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
/// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`.
- (BOOL)concurrentRootEnabled
{
// Switch this bool to turn on and off the concurrent root
return true;
}
- (NSDictionary *)prepareInitialProps
{
NSMutableDictionary *initProps = [NSMutableDictionary new];
#ifdef RCT_NEW_ARCH_ENABLED
initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]);
#endif
return initProps;
self.moduleName = @"HelloWorld";
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
@ -87,4 +19,14 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
#endif
}
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
///
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
/// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`.
- (BOOL)concurrentRootEnabled
{
return true;
}
@end