diff --git a/packages/react-native/React/Base/RCTBundleManager.h b/packages/react-native/React/Base/RCTBundleManager.h index 79dd4b0762ff..268c86898631 100644 --- a/packages/react-native/React/Base/RCTBundleManager.h +++ b/packages/react-native/React/Base/RCTBundleManager.h @@ -54,9 +54,9 @@ typedef NSMutableArray *_Nullable (^RCTPackagerOptionsUpdater) - (nullable NSURL *)getBundleURL; -- (nullable NSString *)getPackagerServerScheme; +- (nonnull NSString *)getPackagerServerScheme; -- (nullable NSString *)getPackagerServerHost; +- (nonnull NSString *)getPackagerServerHost; @end diff --git a/packages/react-native/React/Base/RCTBundleManager.m b/packages/react-native/React/Base/RCTBundleManager.m index bb81de59c83d..88d2217efcf1 100644 --- a/packages/react-native/React/Base/RCTBundleManager.m +++ b/packages/react-native/React/Base/RCTBundleManager.m @@ -7,6 +7,7 @@ #import "RCTBundleManager.h" #import +#import #import "RCTAssert.h" #import "RCTBridge+Private.h" #import "RCTBridge.h" @@ -48,6 +49,12 @@ - (instancetype)initWithBundleFilePath:(NSURL *)bundleFilePath { return options; }; + + // When the bundleFilePath is set in the RCTBundleConfiguration the Metro connection + // shouldn't be suggested/required. + if (_bundleFilePath != nil) { + RCTDevLoadingViewSetEnabled(false); + } } return self; @@ -160,7 +167,13 @@ - (NSURL *)bundleURL _bridgelessBundleURLGetter != nil, @"RCTBundleManager: In bridgeless mode, RCTBridgelessBundleURLGetter must not be nil."); - return _bridgelessBundleURLGetter(); + NSURL *bundleURL = [_bundleConfig getBundleURL]; + + if (bundleURL == nil) { + return _bridgelessBundleURLGetter(); + } + + return bundleURL; } - (void)resetBundleURL diff --git a/packages/react-native/React/CoreModules/RCTDevSettings.h b/packages/react-native/React/CoreModules/RCTDevSettings.h index a4d9c8f9e303..cffe380b9355 100644 --- a/packages/react-native/React/CoreModules/RCTDevSettings.h +++ b/packages/react-native/React/CoreModules/RCTDevSettings.h @@ -11,6 +11,14 @@ #import #import +@class RCTPackagerClientResponder; +typedef uint32_t RCTHandlerToken; +typedef void (^RCTNotificationHandler)(NSDictionary *); +typedef void (^RCTRequestHandler)(NSDictionary *, RCTPackagerClientResponder *); +typedef void (^RCTConnectedHandler)(void); + +@class RCTPackagerConnection; + @protocol RCTPackagerClientMethod; /** @@ -85,6 +93,10 @@ */ @property (nonatomic, assign) BOOL isPerfMonitorShown; +#if RCT_DEV +@property (nonatomic, readonly) RCTPackagerConnection *packagerConnection; +#endif + /** * Toggle the element inspector. */ @@ -102,7 +114,30 @@ #if RCT_DEV_MENU - (void)addHandler:(id)handler - forPackagerMethod:(NSString *)name __deprecated_msg("Use RCTPackagerConnection directly instead"); + forPackagerMethod:(NSString *)name __deprecated_msg("Use addRequestHandler or addNotificationHandler instead"); +#endif + +#if RCT_DEV +/** + * Registers a handler for a notification broadcast from the packager. An + * example is "reload" - an instruction to reload from the packager. + * If multiple notification handlers are registered for the same method, they + * will all be invoked sequentially. + */ +- (RCTHandlerToken)addNotificationHandler:(RCTNotificationHandler)handler + queue:(dispatch_queue_t)queue + forMethod:(NSString *)method; + +/** + * Registers a handler for a request from the packager. An example is + * pokeSamplingProfiler; it asks for profile data from the client. + * Only one handler can be registered for a given method; calling this + * displaces any previous request handler registered for that method. + */ +- (RCTHandlerToken)addRequestHandler:(RCTRequestHandler)handler + queue:(dispatch_queue_t)queue + forMethod:(NSString *)method; + #endif @end diff --git a/packages/react-native/React/CoreModules/RCTDevSettings.mm b/packages/react-native/React/CoreModules/RCTDevSettings.mm index fbbeec0e703d..72b2d1371c85 100644 --- a/packages/react-native/React/CoreModules/RCTDevSettings.mm +++ b/packages/react-native/React/CoreModules/RCTDevSettings.mm @@ -32,9 +32,12 @@ static NSString *const kRCTDevSettingsUserDefaultsKey = @"RCTDevMenu"; +#if RCT_DEV +#import +#endif + #if RCT_DEV_SETTINGS_ENABLE_PACKAGER_CONNECTION #import -#import #endif #if RCT_ENABLE_INSPECTOR @@ -52,7 +55,7 @@ void RCTDevSettingsSetEnabled(BOOL enabled) devSettingsMenuEnabled = enabled; } -#if RCT_DEV_MENU || RCT_REMOTE_PROFILE +#if RCT_DEV || RCT_REMOTE_PROFILE @interface RCTDevSettingsUserDefaultsDataSource : NSObject @@ -145,6 +148,9 @@ - (instancetype)init }; RCTDevSettingsUserDefaultsDataSource *dataSource = [[RCTDevSettingsUserDefaultsDataSource alloc] initWithDefaultValues:defaultValues]; +#if RCT_DEV + _packagerConnection = [RCTPackagerConnection new]; +#endif _isShakeGestureEnabled = true; return [self initWithDataSource:dataSource]; } @@ -178,18 +184,27 @@ - (instancetype)initWithDataSource:(id)dataSource - (void)initialize { +#if RCT_DEV + [_packagerConnection startWithBundleManager:_bundleManager]; +#endif + #if RCT_DEV_SETTINGS_ENABLE_PACKAGER_CONNECTION if (numInitializedModules++ == 0) { - reloadToken = [[RCTPackagerConnection sharedPackagerConnection] + reloadToken = [self addNotificationHandler:^(id params) { RCTTriggerReloadCommandListeners(@"Global hotkey"); } queue:dispatch_get_main_queue() forMethod:@"reload"]; #if RCT_DEV_MENU - devMenuToken = [[RCTPackagerConnection sharedPackagerConnection] + __weak __typeof(self) weakSelf = self; + devMenuToken = [self addNotificationHandler:^(id params) { - [[self.moduleRegistry moduleForName:"DevMenu"] show]; + __typeof(self) strongSelf = weakSelf; + if (strongSelf == nullptr) { + return; + } + [[strongSelf.moduleRegistry moduleForName:"DevMenu"] show]; } queue:dispatch_get_main_queue() forMethod:@"devMenu"]; @@ -240,9 +255,9 @@ - (void)invalidate [super invalidate]; #if RCT_DEV_SETTINGS_ENABLE_PACKAGER_CONNECTION if (--numInitializedModules == 0) { - [[RCTPackagerConnection sharedPackagerConnection] removeHandler:reloadToken]; + [_packagerConnection removeHandler:reloadToken]; #if RCT_DEV_MENU - [[RCTPackagerConnection sharedPackagerConnection] removeHandler:devMenuToken]; + [_packagerConnection removeHandler:devMenuToken]; #endif } #endif @@ -422,10 +437,27 @@ - (void)setExecutorClass:(Class)executorClass } } +#if RCT_DEV +- (RCTHandlerToken)addNotificationHandler:(RCTNotificationHandler)handler + queue:(dispatch_queue_t)queue + forMethod:(NSString *)method +{ + return [_packagerConnection addNotificationHandler:handler queue:queue forMethod:method]; +} + +- (RCTHandlerToken)addRequestHandler:(RCTRequestHandler)handler + queue:(dispatch_queue_t)queue + forMethod:(NSString *)method +{ + return [_packagerConnection addRequestHandler:handler queue:queue forMethod:method]; +} + +#endif + - (void)addHandler:(id)handler forPackagerMethod:(NSString *)name { #if RCT_DEV_SETTINGS_ENABLE_PACKAGER_CONNECTION - [[RCTPackagerConnection sharedPackagerConnection] addHandler:handler forMethod:name]; + [_packagerConnection addHandler:handler forMethod:name]; #endif } @@ -512,7 +544,7 @@ - (void)jsLoaded:(NSNotification *)notification @end -#else // #if RCT_DEV_MENU +#else // #if RCT_DEV || RCT_REMOTE_PROFILE @interface RCTDevSettings () @end @@ -578,6 +610,10 @@ - (void)setIsShakeToShowDevMenuEnabled:(BOOL)enabled return std::make_shared(params); } +- (void)addHandler:(id)handler forPackagerMethod:(NSString *)name +{ +} + @end #endif // #if RCT_DEV_MENU diff --git a/packages/react-native/React/DevSupport/RCTPackagerConnection.h b/packages/react-native/React/DevSupport/RCTPackagerConnection.h index cdd7c745a895..68310757c9c7 100644 --- a/packages/react-native/React/DevSupport/RCTPackagerConnection.h +++ b/packages/react-native/React/DevSupport/RCTPackagerConnection.h @@ -7,6 +7,7 @@ #import +#import #import #if RCT_DEV @@ -24,8 +25,6 @@ typedef void (^RCTConnectedHandler)(void); /** Encapsulates singleton connection to React Native packager. */ @interface RCTPackagerConnection : NSObject -+ (instancetype)sharedPackagerConnection; - /** * Registers a handler for a notification broadcast from the packager. An * example is "reload" - an instruction to reload from the packager. @@ -62,6 +61,9 @@ typedef void (^RCTConnectedHandler)(void); /** Reconnect with given packager server, if packagerServerHostPort has changed. */ - (void)reconnect:(NSString *)packagerServerHostPort; +/** starts packager connection with configuration from the bundle manager */ +- (void)startWithBundleManager:(RCTBundleManager *)bundleManager; + /** * Historically no distinction was made between notification and request * handlers. If you use this method, it will be registered as *both* a diff --git a/packages/react-native/React/DevSupport/RCTPackagerConnection.mm b/packages/react-native/React/DevSupport/RCTPackagerConnection.mm index 95dbf82437c9..1c17945855dc 100644 --- a/packages/react-native/React/DevSupport/RCTPackagerConnection.mm +++ b/packages/react-native/React/DevSupport/RCTPackagerConnection.mm @@ -13,6 +13,7 @@ #import #import +#import #import #import #import @@ -50,43 +51,13 @@ @implementation RCTPackagerConnection { std::vector> _notificationRegistrations; std::vector> _requestRegistrations; std::vector> _connectedRegistrations; -} - -+ (instancetype)sharedPackagerConnection -{ - static RCTPackagerConnection *connection; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - connection = [RCTPackagerConnection new]; - }); - return connection; + RCTBundleManager *_bundleManager; } - (instancetype)init { if (self = [super init]) { _nextToken = 1; // Prevent randomly erasing a handler if you pass a bogus 0 token - _serverHostPortForSocket = [[RCTBundleURLProvider sharedSettings] packagerServerHostPort]; - _serverSchemeForSocket = [[RCTBundleURLProvider sharedSettings] packagerScheme]; - _socket = socketForLocation(_serverHostPortForSocket, _serverSchemeForSocket); - _socket.delegate = self; - [_socket start]; - - RCTPackagerConnection *const __weak weakSelf = self; - _bundleURLChangeObserver = - [[NSNotificationCenter defaultCenter] addObserverForName:RCTBundleURLProviderUpdatedNotification - object:nil - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification *_Nonnull __unused note) { - [weakSelf bundleURLSettingsChanged]; - }]; - _reloadWithPotentiallyNewURLObserver = - [[NSNotificationCenter defaultCenter] addObserverForName:RCTTriggerReloadCommandNotification - object:nil - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification *_Nonnull __unused note) { - [weakSelf bundleURLSettingsChanged]; - }]; } return self; } @@ -119,6 +90,32 @@ - (instancetype)init return [[RCTReconnectingWebSocket alloc] initWithURL:components.URL queue:queue]; } +- (void)startWithBundleManager:(RCTBundleManager *)bundleManager +{ + _serverHostPortForSocket = [bundleManager.bundleConfig getPackagerServerHost]; + _serverSchemeForSocket = [bundleManager.bundleConfig getPackagerServerScheme]; + _socket = socketForLocation(_serverHostPortForSocket, _serverSchemeForSocket); + _socket.delegate = self; + [_socket start]; + _bundleManager = bundleManager; + + RCTPackagerConnection *const __weak weakSelf = self; + _bundleURLChangeObserver = + [[NSNotificationCenter defaultCenter] addObserverForName:RCTBundleURLProviderUpdatedNotification + object:nil + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification *_Nonnull __unused note) { + [weakSelf bundleURLSettingsChanged]; + }]; + _reloadWithPotentiallyNewURLObserver = + [[NSNotificationCenter defaultCenter] addObserverForName:RCTTriggerReloadCommandNotification + object:nil + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification *_Nonnull __unused note) { + [weakSelf bundleURLSettingsChanged]; + }]; +} + - (void)stop { std::lock_guard l(_mutex); @@ -144,7 +141,7 @@ - (void)reconnect:(NSString *)packagerServerHostPort return; // already stopped } - NSString *const serverScheme = [[RCTBundleURLProvider sharedSettings] packagerScheme]; + NSString *const serverScheme = [_bundleManager.bundleConfig getPackagerServerScheme]; if ([packagerServerHostPort isEqual:_serverHostPortForSocket] && [serverScheme isEqual:_serverSchemeForSocket]) { return; // unchanged } @@ -161,7 +158,7 @@ - (void)reconnect:(NSString *)packagerServerHostPort - (void)bundleURLSettingsChanged { // Will only reconnect if `packagerServerHostPort` has actually changed - [self reconnect:[[RCTBundleURLProvider sharedSettings] packagerServerHostPort]]; + [self reconnect:[_bundleManager.bundleConfig getPackagerServerHost]]; } - (RCTHandlerToken)addNotificationHandler:(RCTNotificationHandler)handler