Skip to content

Commit

Permalink
[RN][iOS] Cherry Pick #43757 and #43994 (#44007)
Browse files Browse the repository at this point in the history
* Support launchOptions in bridgeless mode (#43757)

Summary:
Support launchOptions in bridgeless mode
bypass-github-export-checks

[IOS] [FIXED] - Support launchOptions in bridgeless mode

Pull Request resolved: #43757

Test Plan:
```
useEffect(() => {
    const processInitialURL = async () => {
      const url = await Linking.getInitialURL();
      if (url !== null) {
        console.log(`Initial url is: ${url}`);
      }
    };

    processInitialURL();
  }, []);
```

Reviewed By: javache

Differential Revision: D55790758

Pulled By: cipolleschi

fbshipit-source-id: 0f6aa6bdcebfc5bc42d632bea9193f122c1eb84f

* Fix Connect to Metro after Reload in Bridgeless mode (#43994)

Summary:
Pull Request resolved: #43994

We received [this issue](#43764) from OSS where an app can't connect to Metro on reloads in the following scenario:

* Start the App when metro does not run.
* Observe the error screen
* Start Metro
* Press Reload
* Observe the error message again

While the desired behavior should be to connect to Metro now that this is running.

The root cause of the problem is that the RCTHost is initialized with a value of the `bundleURL` that is `nil`. Upon reload, the RCTHost is **not** recreated: the instance is restarted, but with the previous `bundleURL`, which is still `nil`.

The solution is to initialize the `RCTHost` with a closure that re-evaluate the `bundleURL` whenever it is invoked and to evaluate it only on `start`, to keep the initialization path light.
This way, when the app is started with Metro not running, the `bundleURL` is `nil`. But when it is reloaded with Metro starting, the `bundleURL` is properly initialized.

Note that the changes in this diff are not breaking as I reimplemented (and deprecated) the old initializer so that they should work in the same way.

[iOS][Fixed] - Let RCTHost be initialized with a function to provide the `bundleURL` so that it can connect to metro on Reload when the url changes.

Reviewed By: dmytrorykun

Differential Revision: D55916135

fbshipit-source-id: 6927b2154870245f28f42d26bd0209b28c9518f2

* Fix badly resolved merge  conflicts

---------

Co-authored-by: zhongwuzw <zhongwuzw@qq.com>
  • Loading branch information
cipolleschi and zhongwuzw committed Apr 10, 2024
1 parent 2d84d83 commit cb2d93e
Show file tree
Hide file tree
Showing 16 changed files with 207 additions and 133 deletions.
15 changes: 10 additions & 5 deletions packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -233,13 +233,18 @@ - (Class)getModuleClassFromName:(const char *)name

- (RCTRootViewFactory *)createRCTRootViewFactory
{
__weak __typeof(self) weakSelf = self;
RCTBundleURLBlock bundleUrlBlock = ^{
RCTAppDelegate *strongSelf = weakSelf;
return strongSelf.bundleURL;
};

RCTRootViewFactoryConfiguration *configuration =
[[RCTRootViewFactoryConfiguration alloc] initWithBundleURL:self.bundleURL
newArchEnabled:self.fabricEnabled
turboModuleEnabled:self.turboModuleEnabled
bridgelessEnabled:self.bridgelessEnabled];
[[RCTRootViewFactoryConfiguration alloc] initWithBundleURLBlock:bundleUrlBlock
newArchEnabled:self.fabricEnabled
turboModuleEnabled:self.turboModuleEnabled
bridgelessEnabled:self.bridgelessEnabled];

__weak __typeof(self) weakSelf = self;
configuration.createRootViewWithBridge = ^UIView *(RCTBridge *bridge, NSString *moduleName, NSDictionary *initProps)
{
return [weakSelf createRootViewWithBridge:bridge moduleName:moduleName initProps:initProps];
Expand Down
14 changes: 12 additions & 2 deletions packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ typedef UIView *_Nonnull (
^RCTCreateRootViewWithBridgeBlock)(RCTBridge *bridge, NSString *moduleName, NSDictionary *initProps);
typedef RCTBridge *_Nonnull (
^RCTCreateBridgeWithDelegateBlock)(id<RCTBridgeDelegate> delegate, NSDictionary *launchOptions);
typedef NSURL *_Nullable (^RCTSourceURLForBridgeBlock)(RCTBridge *bridge);
typedef NSURL *_Nullable (^RCTBundleURLBlock)(void);
typedef NSArray<id<RCTBridgeModule>> *_Nonnull (^RCTExtraModulesForBridgeBlock)(RCTBridge *bridge);
typedef NSDictionary<NSString *, Class> *_Nonnull (^RCTExtraLazyModuleClassesForBridge)(RCTBridge *bridge);
typedef BOOL (^RCTBridgeDidNotFindModuleBlock)(RCTBridge *bridge, NSString *moduleName);

#pragma mark - RCTRootViewFactory Configuration
@interface RCTRootViewFactoryConfiguration : NSObject
Expand All @@ -37,7 +42,7 @@ typedef RCTBridge *_Nonnull (
@property (nonatomic, assign, readonly) BOOL turboModuleEnabled;

/// Return the bundle URL for the main bundle.
@property (nonatomic) NSURL *bundleURL;
@property (nonatomic, nonnull) RCTBundleURLBlock bundleURLBlock;

/**
* Use this method to initialize a new instance of `RCTRootViewFactoryConfiguration` by passing a `bundleURL`
Expand All @@ -48,10 +53,15 @@ typedef RCTBridge *_Nonnull (
* pointing to a path inside the app resources, e.g. `file://.../main.jsbundle`.
*
*/
- (instancetype)initWithBundleURLBlock:(RCTBundleURLBlock)bundleURLBlock
newArchEnabled:(BOOL)newArchEnabled
turboModuleEnabled:(BOOL)turboModuleEnabled
bridgelessEnabled:(BOOL)bridgelessEnabled NS_DESIGNATED_INITIALIZER;

- (instancetype)initWithBundleURL:(NSURL *)bundleURL
newArchEnabled:(BOOL)newArchEnabled
turboModuleEnabled:(BOOL)turboModuleEnabled
bridgelessEnabled:(BOOL)bridgelessEnabled;
bridgelessEnabled:(BOOL)bridgelessEnabled __deprecated;

/**
* Block that allows to override logic of creating root view instance.
Expand Down
35 changes: 25 additions & 10 deletions packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,23 @@ - (instancetype)initWithBundleURL:(NSURL *)bundleURL
newArchEnabled:(BOOL)newArchEnabled
turboModuleEnabled:(BOOL)turboModuleEnabled
bridgelessEnabled:(BOOL)bridgelessEnabled
{
return [self
initWithBundleURLBlock:^{
return bundleURL;
}
newArchEnabled:newArchEnabled
turboModuleEnabled:turboModuleEnabled
bridgelessEnabled:bridgelessEnabled];
}

- (instancetype)initWithBundleURLBlock:(RCTBundleURLBlock)bundleURLBlock
newArchEnabled:(BOOL)newArchEnabled
turboModuleEnabled:(BOOL)turboModuleEnabled
bridgelessEnabled:(BOOL)bridgelessEnabled
{
if (self = [super init]) {
_bundleURL = bundleURL;
_bundleURLBlock = bundleURLBlock;
_fabricEnabled = newArchEnabled;
_turboModuleEnabled = turboModuleEnabled;
_bridgelessEnabled = bridgelessEnabled;
Expand Down Expand Up @@ -123,7 +137,7 @@ - (UIView *)viewWithModuleName:(NSString *)moduleName
RCTEnableTurboModuleInterop(YES);
RCTEnableTurboModuleInteropBridgeProxy(YES);

[self createReactHostIfNeeded];
[self createReactHostIfNeeded:launchOptions];

RCTFabricSurface *surface = [_reactHost createSurfaceWithModuleName:moduleName initialProperties:initProps];

Expand Down Expand Up @@ -206,19 +220,20 @@ - (void)createBridgeAdapterIfNeeded

#pragma mark - New Arch Utilities

- (void)createReactHostIfNeeded
- (void)createReactHostIfNeeded:(NSDictionary *)launchOptions
{
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 = [[RCTHost alloc] initWithBundleURLProvider:self->_configuration.bundleURLBlock
hostDelegate:nil
turboModuleManagerDelegate:_turboModuleManagerDelegate
jsEngineProvider:^std::shared_ptr<facebook::react::JSRuntimeFactory>() {
return [weakSelf createJSRuntimeFactory];
}
launchOptions:launchOptions];
[_reactHost setBundleURLProvider:^NSURL *() {
return [weakSelf bundleURL];
}];
Expand Down Expand Up @@ -247,7 +262,7 @@ - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge

- (NSURL *)bundleURL
{
return self->_configuration.bundleURL;
return self->_configuration.bundleURLBlock();
}

@end
7 changes: 6 additions & 1 deletion packages/react-native/React/Base/RCTBridgeProxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

#import "RCTBridgeModule.h"

NS_ASSUME_NONNULL_BEGIN

@class RCTBundleManager;
@class RCTCallableJSModules;
@class RCTModuleRegistry;
Expand All @@ -22,7 +24,8 @@
callableJSModules:(RCTCallableJSModules *)callableJSModules
dispatchToJSThread:(void (^)(dispatch_block_t))dispatchToJSThread
registerSegmentWithId:(void (^)(NSNumber *, NSString *))registerSegmentWithId
runtime:(void *)runtime NS_DESIGNATED_INITIALIZER;
runtime:(void *)runtime
launchOptions:(nullable NSDictionary *)launchOptions NS_DESIGNATED_INITIALIZER;

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel;
- (void)forwardInvocation:(NSInvocation *)invocation;
Expand All @@ -37,3 +40,5 @@
- (id)moduleForName:(NSString *)moduleName lazilyLoadIfNecessary:(BOOL)lazilyLoad;

@end

NS_ASSUME_NONNULL_END
6 changes: 4 additions & 2 deletions packages/react-native/React/Base/RCTBridgeProxy.mm
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ @implementation RCTBridgeProxy {
RCTModuleRegistry *_moduleRegistry;
RCTBundleManager *_bundleManager;
RCTCallableJSModules *_callableJSModules;
NSDictionary *_launchOptions;
void (^_dispatchToJSThread)(dispatch_block_t);
void (^_registerSegmentWithId)(NSNumber *, NSString *);
void *_runtime;
Expand All @@ -47,6 +48,7 @@ - (instancetype)initWithViewRegistry:(RCTViewRegistry *)viewRegistry
dispatchToJSThread:(void (^)(dispatch_block_t))dispatchToJSThread
registerSegmentWithId:(void (^)(NSNumber *, NSString *))registerSegmentWithId
runtime:(void *)runtime
launchOptions:(nullable NSDictionary *)launchOptions
{
self = [super self];
if (self) {
Expand All @@ -57,6 +59,7 @@ - (instancetype)initWithViewRegistry:(RCTViewRegistry *)viewRegistry
_dispatchToJSThread = dispatchToJSThread;
_registerSegmentWithId = registerSegmentWithId;
_runtime = runtime;
_launchOptions = [launchOptions copy];
}
return self;
}
Expand Down Expand Up @@ -191,8 +194,7 @@ - (void)registerSegmentWithId:(NSUInteger)segmentId path:(NSString *)path

- (NSDictionary *)launchOptions
{
[self logError:@"This method is not supported. Returning nil." cmd:_cmd];
return nil;
return _launchOptions;
}

- (BOOL)loading
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,7 @@ - (BOOL)_shouldCreateObjCModule:(Class)moduleClass
*/
if (_bridge) {
[(id)module setValue:_bridge forKey:@"bridge"];
} else if (_bridgeProxy && [self _isLegacyModuleClass:[module class]]) {
} else if (_bridgeProxy) {
[(id)module setValue:_bridgeProxy forKey:@"bridge"];
}
} @catch (NSException *exception) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ - (void)setUp
turboModuleManagerDelegate:OCMProtocolMock(@protocol(RCTTurboModuleManagerDelegate))
jsEngineProvider:^std::shared_ptr<facebook::react::JSRuntimeFactory>() {
return std::make_shared<facebook::react::RCTHermesInstance>();
}];
}
launchOptions:nil];
}

- (void)tearDown
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@

#import "RCTContextContainerHandling.h"

typedef NSURL * (^RCTHostBundleURLProvider)(void);

@interface RCTHost (Internal)

- (void)registerSegmentWithId:(NSNumber *)segmentId path:(NSString *)path;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ NS_ASSUME_NONNULL_BEGIN

@protocol RCTTurboModuleManagerDelegate;

typedef NSURL *_Nullable (^RCTHostBundleURLProvider)(void);

// Runtime API

@protocol RCTHostDelegate <NSObject>
Expand All @@ -45,10 +47,17 @@ typedef std::shared_ptr<facebook::react::JSRuntimeFactory> (^RCTHostJSEngineProv

@interface RCTHost : NSObject

- (instancetype)initWithBundleURLProvider:(RCTHostBundleURLProvider)provider
hostDelegate:(id<RCTHostDelegate>)hostDelegate
turboModuleManagerDelegate:(id<RCTTurboModuleManagerDelegate>)turboModuleManagerDelegate
jsEngineProvider:(RCTHostJSEngineProvider)jsEngineProvider
launchOptions:(nullable NSDictionary *)launchOptions NS_DESIGNATED_INITIALIZER;

- (instancetype)initWithBundleURL:(NSURL *)bundleURL
hostDelegate:(id<RCTHostDelegate>)hostDelegate
turboModuleManagerDelegate:(id<RCTTurboModuleManagerDelegate>)turboModuleManagerDelegate
jsEngineProvider:(RCTHostJSEngineProvider)jsEngineProvider NS_DESIGNATED_INITIALIZER;
jsEngineProvider:(RCTHostJSEngineProvider)jsEngineProvider
launchOptions:(nullable NSDictionary *)launchOptions __deprecated;

@property (nonatomic, weak, nullable) id<RCTHostRuntimeDelegate> runtimeDelegate;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ @implementation RCTHost {
RCTHostBundleURLProvider _bundleURLProvider;
RCTHostJSEngineProvider _jsEngineProvider;

NSDictionary *_launchOptions;

// All the surfaces that need to be started after main bundle execution
NSMutableArray<RCTFabricSurface *> *_surfaceStartBuffer;
std::mutex _surfaceStartBufferMutex;
Expand All @@ -77,14 +79,31 @@ + (void)initialize
_RCTInitializeJSThreadConstantInternal();
}

/**
Host initialization should not be resource intensive. A host may be created before any intention of using React Native
has been expressed.
*/
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
hostDelegate:(id<RCTHostDelegate>)hostDelegate
turboModuleManagerDelegate:(id<RCTTurboModuleManagerDelegate>)turboModuleManagerDelegate
jsEngineProvider:(RCTHostJSEngineProvider)jsEngineProvider
launchOptions:(nullable NSDictionary *)launchOptions
{
return [self
initWithBundleURLProvider:^{
return bundleURL;
}
hostDelegate:hostDelegate
turboModuleManagerDelegate:turboModuleManagerDelegate
jsEngineProvider:jsEngineProvider
launchOptions:launchOptions];
}

/**
Host initialization should not be resource intensive. A host may be created before any intention of using React Native
has been expressed.
*/
- (instancetype)initWithBundleURLProvider:(RCTHostBundleURLProvider)provider
hostDelegate:(id<RCTHostDelegate>)hostDelegate
turboModuleManagerDelegate:(id<RCTTurboModuleManagerDelegate>)turboModuleManagerDelegate
jsEngineProvider:(RCTHostJSEngineProvider)jsEngineProvider
launchOptions:(nullable NSDictionary *)launchOptions
{
if (self = [super init]) {
_hostDelegate = hostDelegate;
Expand All @@ -93,9 +112,9 @@ - (instancetype)initWithBundleURL:(NSURL *)bundleURL
_bundleManager = [RCTBundleManager new];
_moduleRegistry = [RCTModuleRegistry new];
_jsEngineProvider = [jsEngineProvider copy];
_launchOptions = [launchOptions copy];

__weak RCTHost *weakSelf = self;

auto bundleURLGetter = ^NSURL *()
{
RCTHost *strongSelf = weakSelf;
Expand All @@ -107,7 +126,7 @@ - (instancetype)initWithBundleURL:(NSURL *)bundleURL
};

auto bundleURLSetter = ^(NSURL *bundleURL_) {
[weakSelf _setBundleURL:bundleURL];
[weakSelf _setBundleURL:bundleURL_];
};

auto defaultBundleURLGetter = ^NSURL *()
Expand All @@ -120,7 +139,6 @@ - (instancetype)initWithBundleURL:(NSURL *)bundleURL
return strongSelf->_bundleURLProvider();
};

[self _setBundleURL:bundleURL];
[_bundleManager setBridgelessBundleURLGetter:bundleURLGetter
andSetter:bundleURLSetter
andDefaultGetter:defaultBundleURLGetter];
Expand Down Expand Up @@ -166,6 +184,9 @@ - (instancetype)initWithBundleURL:(NSURL *)bundleURL

- (void)start
{
if (_bundleURLProvider) {
[self _setBundleURL:_bundleURLProvider()];
}
auto &inspectorFlags = jsinspector_modern::InspectorFlags::getInstance();
if (inspectorFlags.getEnableModernCDPRegistry() && !_inspectorPageId.has_value()) {
_inspectorTarget =
Expand Down Expand Up @@ -204,7 +225,8 @@ - (void)start
turboModuleManagerDelegate:_turboModuleManagerDelegate
onInitialBundleLoad:_onInitialBundleLoad
moduleRegistry:_moduleRegistry
parentInspectorTarget:_inspectorTarget.get()];
parentInspectorTarget:_inspectorTarget.get()
launchOptions:_launchOptions];
[_hostDelegate hostDidStart:self];
}

Expand Down Expand Up @@ -284,7 +306,8 @@ - (void)didReceiveReloadCommand
turboModuleManagerDelegate:_turboModuleManagerDelegate
onInitialBundleLoad:_onInitialBundleLoad
moduleRegistry:_moduleRegistry
parentInspectorTarget:_inspectorTarget.get()];
parentInspectorTarget:_inspectorTarget.get()
launchOptions:_launchOptions];
[_hostDelegate hostDidStart:self];

for (RCTFabricSurface *surface in [self _getAttachedSurfaces]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ typedef void (^_Null_unspecified RCTInstanceInitialBundleLoadCompletionBlock)();
turboModuleManagerDelegate:(id<RCTTurboModuleManagerDelegate>)turboModuleManagerDelegate
onInitialBundleLoad:(RCTInstanceInitialBundleLoadCompletionBlock)onInitialBundleLoad
moduleRegistry:(RCTModuleRegistry *)moduleRegistry
parentInspectorTarget:(facebook::react::jsinspector_modern::PageTarget *)parentInspectorTarget;
parentInspectorTarget:(facebook::react::jsinspector_modern::PageTarget *)parentInspectorTarget
launchOptions:(nullable NSDictionary *)launchOptions;

- (void)callFunctionOnJSModule:(NSString *)moduleName method:(NSString *)method args:(NSArray *)args;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ @implementation RCTInstance {
std::mutex _invalidationMutex;
std::atomic<bool> _valid;
RCTJSThreadManager *_jsThreadManager;
NSDictionary *_launchOptions;

// APIs supporting interop with native modules and view managers
RCTBridgeModuleDecorator *_bridgeModuleDecorator;
Expand All @@ -99,6 +100,7 @@ - (instancetype)initWithDelegate:(id<RCTInstanceDelegate>)delegate
onInitialBundleLoad:(RCTInstanceInitialBundleLoadCompletionBlock)onInitialBundleLoad
moduleRegistry:(RCTModuleRegistry *)moduleRegistry
parentInspectorTarget:(jsinspector_modern::PageTarget *)parentInspectorTarget
launchOptions:(nullable NSDictionary *)launchOptions
{
if (self = [super init]) {
_performanceLogger = [RCTPerformanceLogger new];
Expand All @@ -124,6 +126,7 @@ - (instancetype)initWithDelegate:(id<RCTInstanceDelegate>)delegate
[weakSelf callFunctionOnJSModule:moduleName method:methodName args:args];
}];
}
_launchOptions = launchOptions;

NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];

Expand Down Expand Up @@ -270,7 +273,8 @@ - (void)_start
[strongSelf registerSegmentWithId:segmentId path:path];
}
}
runtime:_reactInstance->getJavaScriptContext()];
runtime:_reactInstance->getJavaScriptContext()
launchOptions:_launchOptions];
bridgeProxy.jsCallInvoker = jsCallInvoker;
[RCTBridge setCurrentBridge:(RCTBridge *)bridgeProxy];

Expand Down

0 comments on commit cb2d93e

Please sign in to comment.