From 7487a2c27713a0129ef2db70efc2fa543a8c185e Mon Sep 17 00:00:00 2001 From: Alan Hughes Date: Thu, 3 Oct 2024 07:12:43 -0700 Subject: [PATCH] Allow taking control of bundle loading on new arch (#46731) Summary: On the old architecture you could take control of loading the bundle by implementing ```objc - (void)loadSourceForBridge:(RCTBridge *)bridge onProgress:(RCTSourceLoadProgressBlock)onProgress onComplete:(RCTSourceLoadBlock)loadCallback; ``` in your `RCTBridgeDelegate`. This is not currently possible in the new architecture. I've added this using a pretty much identical api by adding a function to both the `RCTInstanceDelegate` and `RCTHostDelegate` protocols. This will be called on the `RCTRootViewFactory`. I've added two properties to the `RCTRootViewFactoryConfiguration`, `loadSourceForHost` and `loadSourceWithProgressForHost`. If one is present, we call it, otherwise we fallback to the normal loading process ## Changelog: [iOS] [Breaking] - Add ability to control bundle loading on the new architecture similar to `loadSourceForBridge`. Removed some properties from the `RCTRootViewFactory`. Pull Request resolved: https://github.com/facebook/react-native/pull/46731 Test Plan: Rn-tester works as normal and it is working for our use case in expo go. Reviewed By: blakef Differential Revision: D63755188 Pulled By: cipolleschi fbshipit-source-id: f1f26b2775b9e547ce7a23028665797c19bfdd9b --- .../Libraries/AppDelegate/RCTAppDelegate.mm | 15 +------ .../AppDelegate/RCTRootViewFactory.h | 28 +++--------- .../AppDelegate/RCTRootViewFactory.mm | 44 ++++++++----------- .../platform/ios/ReactCommon/RCTHost.h | 5 +++ .../platform/ios/ReactCommon/RCTHost.mm | 11 +++++ .../platform/ios/ReactCommon/RCTInstance.h | 5 +++ .../platform/ios/ReactCommon/RCTInstance.mm | 3 +- 7 files changed, 47 insertions(+), 64 deletions(-) diff --git a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm index ca5be139ffb2de..28e339d0e00c51 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm @@ -275,19 +275,6 @@ - (RCTRootViewFactory *)createRCTRootViewFactory return [weakSelf sourceURLForBridge:bridge]; }; - configuration.hostDidStartBlock = ^(RCTHost *_Nonnull host) { - [weakSelf hostDidStart:host]; - }; - - configuration.hostDidReceiveJSErrorStackBlock = - ^(RCTHost *_Nonnull host, - NSArray *> *_Nonnull stack, - NSString *_Nonnull message, - NSUInteger exceptionId, - BOOL isFatal) { - [weakSelf host:host didReceiveJSErrorStack:stack message:message exceptionId:exceptionId isFatal:isFatal]; - }; - if ([self respondsToSelector:@selector(extraModulesForBridge:)]) { configuration.extraModulesForBridge = ^NSArray> *_Nonnull(RCTBridge *_Nonnull bridge) { @@ -309,7 +296,7 @@ - (RCTRootViewFactory *)createRCTRootViewFactory }; } - return [[RCTRootViewFactory alloc] initWithConfiguration:configuration andTurboModuleManagerDelegate:self]; + return [[RCTRootViewFactory alloc] initWithTurboModuleDelegate:self hostDelegate:self configuration:configuration]; } #pragma mark - Feature Flags diff --git a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h index d4220f6102fd05..27e55f56645ac1 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h +++ b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h @@ -12,6 +12,7 @@ @protocol RCTCxxBridgeDelegate; @protocol RCTComponentViewFactoryComponentProvider; @protocol RCTTurboModuleManagerDelegate; +@protocol RCTHostDelegate; @class RCTBridge; @class RCTHost; @class RCTRootView; @@ -30,13 +31,6 @@ typedef NSURL *_Nullable (^RCTBundleURLBlock)(void); typedef NSArray> *_Nonnull (^RCTExtraModulesForBridgeBlock)(RCTBridge *bridge); typedef NSDictionary *_Nonnull (^RCTExtraLazyModuleClassesForBridge)(RCTBridge *bridge); typedef BOOL (^RCTBridgeDidNotFindModuleBlock)(RCTBridge *bridge, NSString *moduleName); -typedef void (^RCTHostDidStartBlock)(RCTHost *host); -typedef void (^RCTHostDidReceiveJSErrorStackBlock)( - RCTHost *host, - NSArray *> *stack, - NSString *message, - NSUInteger exceptionId, - BOOL isFatal); #pragma mark - RCTRootViewFactory Configuration @interface RCTRootViewFactoryConfiguration : NSObject @@ -147,22 +141,6 @@ typedef void (^RCTHostDidReceiveJSErrorStackBlock)( */ @property (nonatomic, nullable) RCTBridgeDidNotFindModuleBlock bridgeDidNotFindModule; -/** - * Called when `RCTHost` started. - * @parameter: host - The started `RCTHost`. - */ -@property (nonatomic, nullable) RCTHostDidStartBlock hostDidStartBlock; - -/** - * Called when `RCTHost` received JS error. - * @parameter: host - `RCTHost` which received js error. - * @parameter: stack - JS error stack. - * @parameter: message - Error message. - * @parameter: exceptionId - Exception ID. - * @parameter: isFatal - YES if JS error is fatal. - */ -@property (nonatomic, nullable) RCTHostDidReceiveJSErrorStackBlock hostDidReceiveJSErrorStackBlock; - @end #pragma mark - RCTRootViewFactory @@ -187,6 +165,10 @@ typedef void (^RCTHostDidReceiveJSErrorStackBlock)( - (instancetype)initWithConfiguration:(RCTRootViewFactoryConfiguration *)configuration; +- (instancetype)initWithTurboModuleDelegate:(id)turboModuleManagerDelegate + hostDelegate:(id)hostdelegate + configuration:(RCTRootViewFactoryConfiguration *)configuration; + /** * This method can be used to create new RCTRootViews on demand. * diff --git a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm index e365cf76abac5e..80e9fabd1879c9 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm @@ -83,7 +83,7 @@ - (instancetype)initWithBundleURLBlock:(RCTBundleURLBlock)bundleURLBlock @end -@interface RCTRootViewFactory () { +@interface RCTRootViewFactory () { std::shared_ptr _reactNativeConfig; facebook::react::ContextContainer::Shared _contextContainer; } @@ -95,15 +95,18 @@ @interface RCTRootViewFactory () { @end @implementation RCTRootViewFactory { - RCTRootViewFactoryConfiguration *_configuration; __weak id _turboModuleManagerDelegate; + __weak id _hostDelegate; + RCTRootViewFactoryConfiguration *_configuration; } -- (instancetype)initWithConfiguration:(RCTRootViewFactoryConfiguration *)configuration - andTurboModuleManagerDelegate:(id)turboModuleManagerDelegate +- (instancetype)initWithTurboModuleDelegate:(id)turboModuleManagerDelegate + hostDelegate:(id)hostdelegate + configuration:(RCTRootViewFactoryConfiguration *)configuration { if (self = [super init]) { _configuration = configuration; + _hostDelegate = hostdelegate; _contextContainer = std::make_shared(); _reactNativeConfig = std::make_shared(); _contextContainer->insert("ReactNativeConfig", _reactNativeConfig); @@ -112,6 +115,17 @@ - (instancetype)initWithConfiguration:(RCTRootViewFactoryConfiguration *)configu return self; } +- (instancetype)initWithConfiguration:(RCTRootViewFactoryConfiguration *)configuration + andTurboModuleManagerDelegate:(id)turboModuleManagerDelegate +{ + id hostDelegate = [turboModuleManagerDelegate conformsToProtocol:@protocol(RCTHostDelegate)] + ? (id)turboModuleManagerDelegate + : nil; + return [self initWithTurboModuleDelegate:turboModuleManagerDelegate + hostDelegate:hostDelegate + configuration:configuration]; +} + - (instancetype)initWithConfiguration:(RCTRootViewFactoryConfiguration *)configuration { return [self initWithConfiguration:configuration andTurboModuleManagerDelegate:nil]; @@ -188,26 +202,6 @@ - (UIView *)createRootViewWithBridge:(RCTBridge *)bridge return rootView; } -#pragma mark - RCTHostDelegate - -- (void)hostDidStart:(RCTHost *)host -{ - if (self->_configuration.hostDidStartBlock) { - self->_configuration.hostDidStartBlock(host); - } -} - -- (void)host:(RCTHost *)host - didReceiveJSErrorStack:(NSArray *> *)stack - message:(NSString *)message - exceptionId:(NSUInteger)exceptionId - isFatal:(BOOL)isFatal -{ - if (self->_configuration.hostDidReceiveJSErrorStackBlock) { - self->_configuration.hostDidReceiveJSErrorStackBlock(host, stack, message, exceptionId, isFatal); - } -} - #pragma mark - RCTCxxBridgeDelegate - (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge { @@ -266,7 +260,7 @@ - (RCTHost *)createReactHost:(NSDictionary *)launchOptions __weak __typeof(self) weakSelf = self; RCTHost *reactHost = [[RCTHost alloc] initWithBundleURLProvider:self->_configuration.bundleURLBlock - hostDelegate:self + hostDelegate:_hostDelegate turboModuleManagerDelegate:_turboModuleManagerDelegate jsEngineProvider:^std::shared_ptr() { return [weakSelf createJSRuntimeFactory]; diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h index 8f32300607290f..fa16f997266346 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h @@ -35,6 +35,11 @@ typedef NSURL *_Nullable (^RCTHostBundleURLProvider)(void); - (void)hostDidStart:(RCTHost *)host; +@optional +- (void)loadBundleAtURL:(NSURL *)sourceURL + onProgress:(RCTSourceLoadProgressBlock)onProgress + onComplete:(RCTSourceLoadBlock)loadCallback; + @end @protocol RCTHostRuntimeDelegate diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm index 48511d3cc71c26..5fb599a8595ca0 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm @@ -329,6 +329,17 @@ - (void)instance:(RCTInstance *)instance didInitializeRuntime:(facebook::jsi::Ru [self.runtimeDelegate host:self didInitializeRuntime:runtime]; } +- (void)loadBundleAtURL:(NSURL *)sourceURL + onProgress:(RCTSourceLoadProgressBlock)onProgress + onComplete:(RCTSourceLoadBlock)loadCallback +{ + if ([_hostDelegate respondsToSelector:@selector(loadBundleAtURL:onProgress:onComplete:)]) { + [_hostDelegate loadBundleAtURL:sourceURL onProgress:onProgress onComplete:loadCallback]; + } else { + [RCTJavaScriptLoader loadBundleAtURL:sourceURL onProgress:onProgress onComplete:loadCallback]; + } +} + #pragma mark - RCTContextContainerHandling - (void)didCreateContextContainer:(std::shared_ptr)contextContainer diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h index 7df2693943c539..8d26c642de3e57 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h @@ -8,6 +8,7 @@ #import #import +#import #import #import #import @@ -44,6 +45,10 @@ RCT_EXTERN void RCTInstanceSetRuntimeDiagnosticFlags(NSString *_Nullable flags); - (void)instance:(RCTInstance *)instance didInitializeRuntime:(facebook::jsi::Runtime &)runtime; +- (void)loadBundleAtURL:(NSURL *)sourceURL + onProgress:(RCTSourceLoadProgressBlock)onProgress + onComplete:(RCTSourceLoadBlock)loadCallback; + @end /** diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm index 6e3c8c4b468277..89aeb2e19ca764 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm @@ -25,7 +25,6 @@ #import #import #import -#import #import #import #import @@ -416,7 +415,7 @@ - (void)_loadJSBundle:(NSURL *)sourceURL #endif __weak __typeof(self) weakSelf = self; - [RCTJavaScriptLoader loadBundleAtURL:sourceURL + [_delegate loadBundleAtURL:sourceURL onProgress:^(RCTLoadingProgress *progressData) { __typeof(self) strongSelf = weakSelf; if (!strongSelf) {