Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions packages/react-native-host/cocoa/RNXHostConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
#import <React/RCTBridgeDelegate.h>
#import <React/RCTLog.h>

#ifdef __cplusplus
#include <jsi/jsi.h>
#endif

@class ReactNativeHost;

NS_ASSUME_NONNULL_BEGIN

/// Configuration object for ``ReactNativeHost``.
Expand Down Expand Up @@ -30,6 +36,20 @@ NS_ASSUME_NONNULL_BEGIN
/// Handles a fatal error.
- (void)onFatalError:(NSError *)error;

/// Called after the JS instance has finished loading. ``error`` is ``nil``
/// on success.
- (void)host:(ReactNativeHost *)host didLoadInstanceWithError:(nullable NSError *)error
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little iffy with the optional error parameter.. the signature makes it feel like there is always an error despite the comment. Alternative is to also expose failedtoLoadInstanceWithError. Thoughts?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be - (void)didLoadInstanceWithHost:(ReactNativeHost *)host error: (nullable NSError **)error, where the user passes a ref to an NSError if they're interested.

__attribute__((__swift_name__("host(_:didLoadInstanceWithError:)")));

/// Called when the instance is about to be unloaded.
- (void)hostWillUnloadInstance:(ReactNativeHost *)host;

#ifdef __cplusplus
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this allowed? Won't we end up with two definitions of this protocol?

/// Called after host bindings install but before the user JS bundle loads.
/// Use to evaluate pre-user JS on the runtime. Bridgeless mode only.
- (void)host:(ReactNativeHost *)host didInitializeRuntime:(facebook::jsi::Runtime &)runtime;
#endif // __cplusplus

// MARK: - RCTBridgeDelegate deprecated details (for backwards compatibility) [>=0.84]

- (NSURL *__nullable)sourceURLForBridge:(RCTBridge *)bridge;
Expand Down
98 changes: 96 additions & 2 deletions packages/react-native-host/cocoa/ReactNativeHost.mm
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,41 @@ @interface ReactNativeHost () <RCTComponentViewFactoryComponentProvider>
@end
#endif // USE_CODEGEN_PROVIDER

#if USE_BRIDGELESS
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this block be in a private header instead?


// Forwards host:didInitializeRuntime: from RCTHost to the consumer's RNXHostConfig.
@interface _RNXForwardingRCTHostDelegate : NSObject <RCTHostDelegate>
- (instancetype)initWithHost:(ReactNativeHost *)host config:(id<RNXHostConfig>)config;
@end

@implementation _RNXForwardingRCTHostDelegate {
__weak ReactNativeHost *_host;
__weak id<RNXHostConfig> _config;
}

- (instancetype)initWithHost:(ReactNativeHost *)host config:(id<RNXHostConfig>)config
{
if (self = [super init]) {
_host = host;
_config = config;
}
return self;
}

- (void)host:(RCTHost *)host didInitializeRuntime:(facebook::jsi::Runtime &)runtime
{
id<RNXHostConfig> config = _config;
ReactNativeHost *forwardedHost = _host;
if (forwardedHost != nil &&
[config respondsToSelector:@selector(host:didInitializeRuntime:)]) {
[config host:forwardedHost didInitializeRuntime:runtime];
}
}

@end

#endif // USE_BRIDGELESS

@implementation ReactNativeHost {
__weak id<RNXHostConfig> _config;
NSDictionary *_launchOptions;
Expand All @@ -44,6 +79,9 @@ @implementation ReactNativeHost {
RCTHost *_reactHost;
NSLock *_isShuttingDown;
RNXHostReleaser *_hostReleaser;
#if USE_BRIDGELESS
_RNXForwardingRCTHostDelegate *_hostDelegateProxy;
#endif // USE_BRIDGELESS
#ifdef USE_REACT_NATIVE_CONFIG
std::shared_ptr<ReactNativeConfig> _reactNativeConfig;
#endif // USE_REACT_NATIVE_CONFIG
Expand Down Expand Up @@ -99,11 +137,63 @@ - (instancetype)initWithConfig:(id<RNXHostConfig>)config launchOptions:(NSDictio
});
}

if ([config respondsToSelector:@selector(host:didLoadInstanceWithError:)]) {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(_rnxInstanceDidLoad:)
name:RCTJavaScriptDidLoadNotification
object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(_rnxInstanceDidFailToLoad:)
name:RCTJavaScriptDidFailToLoadNotification
object:nil];
}
if ([config respondsToSelector:@selector(hostWillUnloadInstance:)]) {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(_rnxInstanceWillUnload:)
name:RCTBridgeWillBeInvalidatedNotification
object:nil];
}
Comment on lines +140 to +158
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't all of this be contained within _RNXForwardingRCTHostDelegate?


[self initializeReactHost];
}
return self;
}

- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)_rnxInstanceDidLoad:(NSNotification *)__unused notification
{
id<RNXHostConfig> config = _config;
if ([config respondsToSelector:@selector(host:didLoadInstanceWithError:)]) {
[config host:self didLoadInstanceWithError:nil];
}
}

- (void)_rnxInstanceDidFailToLoad:(NSNotification *)notification
{
id<RNXHostConfig> config = _config;
if ([config respondsToSelector:@selector(host:didLoadInstanceWithError:)]) {
NSError *error = notification.userInfo[@"error"];
[config host:self didLoadInstanceWithError:error ?: [NSError errorWithDomain:@"ReactNativeHost"
code:0
userInfo:nil]];
}
}

- (void)_rnxInstanceWillUnload:(NSNotification *)__unused notification
{
id<RNXHostConfig> config = _config;
if ([config respondsToSelector:@selector(hostWillUnloadInstance:)]) {
[config hostWillUnloadInstance:self];
}
}

- (RCTBridge *)bridge
{
if (self.isBridgelessEnabled) {
Expand Down Expand Up @@ -303,6 +393,10 @@ - (void)initializeReactHost
#endif
};

// Retained as an ivar because RCTHost stores host delegates weakly.
_hostDelegateProxy = [[_RNXForwardingRCTHostDelegate alloc] initWithHost:self
config:_config];

__weak __typeof(self) weakSelf = self;
if ([RCTHost instancesRespondToSelector:@selector
(initWithBundleURLProvider:
Expand All @@ -312,13 +406,13 @@ - (void)initializeReactHost
initWithBundleURLProvider:^{
return [weakSelf sourceURLForBridge:nil];
}
hostDelegate:nil
hostDelegate:_hostDelegateProxy
turboModuleManagerDelegate:_turboModuleAdapter
jsEngineProvider:jsEngineProvider
launchOptions:_launchOptions];
} else {
_reactHost = [[RCTHost alloc] initWithBundleURL:[self sourceURLForBridge:nil]
hostDelegate:nil
hostDelegate:_hostDelegateProxy
turboModuleManagerDelegate:_turboModuleAdapter
jsEngineProvider:jsEngineProvider];
}
Expand Down
Loading