diff --git a/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.h b/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.h index 455d23e42cb525..1d9bed69666541 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.h +++ b/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.h @@ -24,6 +24,7 @@ @class RCTBridge; @protocol RCTComponentViewProtocol; @class RCTSurfacePresenterBridgeAdapter; +@class RCTCustomBundleConfiguration; @class RCTDevMenuConfiguration; NS_ASSUME_NONNULL_BEGIN @@ -117,6 +118,8 @@ typedef NS_ENUM(NSInteger, RCTReleaseLevel) { Canary, Experimental, Stable }; @property (nonatomic, weak) id delegate; +@property (nonatomic, nullable) RCTCustomBundleConfiguration *customBundleConfiguration; + @property (nonatomic, nullable) RCTDevMenuConfiguration *devMenuConfiguration; @end diff --git a/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm b/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm index 5851645cf91f4b..8d26ab4d19b4c3 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm @@ -6,6 +6,7 @@ */ #import "RCTReactNativeFactory.h" +#import #import #import #import @@ -59,6 +60,8 @@ - (instancetype)initWithDelegate:(id)delegate rel self.rootViewFactory = [self createRCTRootViewFactory]; [RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self; + + self.customBundleConfiguration = [[RCTCustomBundleConfiguration alloc] init]; } return self; @@ -84,6 +87,7 @@ - (void)startReactNativeWithModuleName:(NSString *)moduleName UIView *rootView = [self.rootViewFactory viewWithModuleName:moduleName initialProperties:initialProperties launchOptions:launchOptions + customBundleConfiguration:self.customBundleConfiguration devMenuConfiguration:self.devMenuConfiguration]; UIViewController *rootViewController = [_delegate createRootViewController]; [_delegate setRootView:rootView toRootViewController:rootViewController]; diff --git a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h index 119d91a9ee29fc..1ec55db5c570e1 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h +++ b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h @@ -18,6 +18,7 @@ @class RCTHost; @class RCTRootView; @class RCTSurfacePresenterBridgeAdapter; +@class RCTCustomBundleConfiguration; @class RCTDevMenuConfiguration; NS_ASSUME_NONNULL_BEGIN @@ -202,11 +203,13 @@ typedef void (^RCTLoadSourceForBridgeBlock)(RCTBridge *bridge, RCTSourceLoadBloc * @parameter: moduleName - the name of the app, used by Metro to resolve the module. * @parameter: initialProperties - a set of initial properties. * @parameter: launchOptions - a dictionary with a set of options. + * @parameter: customBundleConfiguration - a configuration for custom bundle source URL. * @parameter: devMenuConfiguration - a configuration for enabling/disabling dev menu. */ - (UIView *_Nonnull)viewWithModuleName:(NSString *)moduleName initialProperties:(NSDictionary *__nullable)initialProperties launchOptions:(NSDictionary *__nullable)launchOptions + customBundleConfiguration:(RCTCustomBundleConfiguration *__nullable)customBundleConfiguration devMenuConfiguration:(RCTDevMenuConfiguration *__nullable)devMenuConfiguration; - (UIView *_Nonnull)viewWithModuleName:(NSString *)moduleName @@ -226,15 +229,18 @@ typedef void (^RCTLoadSourceForBridgeBlock)(RCTBridge *bridge, RCTSourceLoadBloc * Use it to speed up later viewWithModuleName: calls. * * @parameter: launchOptions - a dictionary with a set of options. + * @parameter: customBundleConfiguration - a configuration for custom bundle source URL. * @parameter: devMenuConfiguration - a configuration for enabling/disabling dev menu. */ - (void)initializeReactHostWithLaunchOptions:(NSDictionary *__nullable)launchOptions + customBundleConfiguration:(RCTCustomBundleConfiguration *__nullable)customBundleConfiguration devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration; -- (RCTHost *)createReactHost:(NSDictionary *__nullable)launchOptions; - - (RCTHost *)createReactHost:(NSDictionary *__nullable)launchOptions - devMenuConfiguration:(RCTDevMenuConfiguration *__nullable)devMenuConfiguration; + customBundleConfiguration:(RCTCustomBundleConfiguration *__nullable)customBundleConfiguration + devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration; + +- (RCTHost *)createReactHost:(NSDictionary *__nullable)launchOptions; @end diff --git a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm index 77201b675ec53b..2fd7ba8b2bc182 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm @@ -21,7 +21,6 @@ #else #import #endif -#import #import #import #import @@ -137,6 +136,7 @@ - (UIView *)viewWithModuleName:(NSString *)moduleName initialProperties:(NSDicti return [self viewWithModuleName:moduleName initialProperties:initialProperties launchOptions:nil + customBundleConfiguration:nil devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]]; } @@ -145,17 +145,21 @@ - (UIView *)viewWithModuleName:(NSString *)moduleName return [self viewWithModuleName:moduleName initialProperties:nil launchOptions:nil + customBundleConfiguration:nil devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]]; } - (void)initializeReactHostWithLaunchOptions:(NSDictionary *)launchOptions + customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration { // Enable TurboModule interop by default in Bridgeless mode RCTEnableTurboModuleInterop(YES); RCTEnableTurboModuleInteropBridgeProxy(YES); - [self createReactHostIfNeeded:launchOptions devMenuConfiguration:devMenuConfiguration]; + [self createReactHostIfNeeded:launchOptions + customBundleConfiguration:customBundleConfiguration + devMenuConfiguration:devMenuConfiguration]; return; } @@ -166,15 +170,19 @@ - (UIView *)viewWithModuleName:(NSString *)moduleName return [self viewWithModuleName:moduleName initialProperties:initialProperties launchOptions:launchOptions + customBundleConfiguration:nil devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]]; } - (UIView *)viewWithModuleName:(NSString *)moduleName initialProperties:(NSDictionary *)initProps launchOptions:(NSDictionary *)launchOptions + customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration { - [self initializeReactHostWithLaunchOptions:launchOptions devMenuConfiguration:devMenuConfiguration]; + [self initializeReactHostWithLaunchOptions:launchOptions + customBundleConfiguration:customBundleConfiguration + devMenuConfiguration:devMenuConfiguration]; RCTFabricSurface *surface = [self.reactHost createSurfaceWithModuleName:moduleName initialProperties:initProps ? initProps : @{}]; @@ -245,21 +253,28 @@ - (void)createBridgeAdapterIfNeeded #pragma mark - New Arch Utilities - (void)createReactHostIfNeeded:(NSDictionary *)launchOptions + customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration { if (self.reactHost) { return; } - self.reactHost = [self createReactHost:launchOptions devMenuConfiguration:devMenuConfiguration]; + + self.reactHost = [self createReactHost:launchOptions + customBundleConfiguration:customBundleConfiguration + devMenuConfiguration:devMenuConfiguration]; } - (RCTHost *)createReactHost:(NSDictionary *)launchOptions { - return [self createReactHost:launchOptions devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]]; + return [self createReactHost:launchOptions + customBundleConfiguration:nil + devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]]; } - (RCTHost *)createReactHost:(NSDictionary *)launchOptions - devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration + customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration + devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration { __weak __typeof(self) weakSelf = self; RCTHost *reactHost = @@ -270,6 +285,7 @@ - (RCTHost *)createReactHost:(NSDictionary *)launchOptions return [weakSelf createJSRuntimeFactory]; } launchOptions:launchOptions + customBundleConfiguration:customBundleConfiguration devMenuConfiguration:devMenuConfiguration]; [reactHost setBundleURLProvider:^NSURL *() { return [weakSelf bundleURL]; diff --git a/packages/react-native/React/Base/RCTBundleManager.h b/packages/react-native/React/Base/RCTBundleManager.h index fa0559ba50cd22..21153bc36d6be4 100644 --- a/packages/react-native/React/Base/RCTBundleManager.h +++ b/packages/react-native/React/Base/RCTBundleManager.h @@ -12,6 +12,47 @@ typedef NSURL * (^RCTBridgelessBundleURLGetter)(void); typedef void (^RCTBridgelessBundleURLSetter)(NSURL *bundleURL); +/** + * Configuration class for setting up custom bundle locations + */ +@interface RCTCustomBundleConfiguration : NSObject + +/** + * The URL of the bundle to load from the file system + */ +@property (nonatomic, readonly, nullable) NSURL *bundleFilePath; + +/** + * The server scheme (e.g. http or https) to use when loading from the packager + */ +@property (nonatomic, readonly, nullable) NSString *packagerServerScheme; + +/** + * The server host (e.g. localhost) to use when loading from the packager + */ +@property (nonatomic, readonly, nullable) NSString *packagerServerHost; + +/** + * The relative path to the bundle. + */ +@property (nonatomic, readonly, nullable) NSString *bundlePath; + +- (instancetype)initWithBundleFilePath:(NSURL *)bundleFilePath; + +- (instancetype)initWithPackagerServerScheme:(NSString *)packagerServerScheme + packagerServerHost:(NSString *)packagerServerHost + bundlePath:(NSString *)bundlePath; + +- (NSURL *)getBundleURL:(NSURL *__nullable (^)(void))fallbackURLProvider; + +- (NSString *)getPackagerServerScheme; + +- (NSString *)getPackagerServerHost; + +- (void)clean; + +@end + /** * A class that allows NativeModules/TurboModules to read/write the bundleURL, with or without the bridge. */ @@ -24,4 +65,5 @@ typedef void (^RCTBridgelessBundleURLSetter)(NSURL *bundleURL); andDefaultGetter:(RCTBridgelessBundleURLGetter)defaultGetter; - (void)resetBundleURL; @property NSURL *bundleURL; +@property (nonatomic, nullable) RCTCustomBundleConfiguration *customBundleConfig; @end diff --git a/packages/react-native/React/Base/RCTBundleManager.m b/packages/react-native/React/Base/RCTBundleManager.m index baa23d9123653a..29afbd3ead14df 100644 --- a/packages/react-native/React/Base/RCTBundleManager.m +++ b/packages/react-native/React/Base/RCTBundleManager.m @@ -6,9 +6,88 @@ */ #import "RCTBundleManager.h" +#import #import "RCTAssert.h" #import "RCTBridge+Private.h" #import "RCTBridge.h" +#import "RCTLog.h" + +@implementation RCTCustomBundleConfiguration + +- (instancetype)initWithBundleFilePath:(NSURL *)bundleFilePath +{ + if (self = [super init]) { + _bundleFilePath = bundleFilePath; + } + + return self; +} + +- (instancetype)initWithPackagerServerScheme:(NSString *)packagerServerScheme + packagerServerHost:(NSString *)packagerServerHost + bundlePath:(NSString *)bundlePath +{ + if (self = [super init]) { + _packagerServerScheme = packagerServerScheme; + _packagerServerHost = packagerServerHost; + _bundlePath = bundlePath; + } + + return self; +} + +- (NSString *)getPackagerServerScheme +{ + if (!_packagerServerScheme) { + return [[RCTBundleURLProvider sharedSettings] packagerScheme]; + } + + return _packagerServerScheme; +} + +- (NSString *)getPackagerServerHost +{ + if (!_packagerServerHost) { + return [[RCTBundleURLProvider sharedSettings] packagerServerHostPort]; + } + + return _packagerServerHost; +} + +- (NSURL *)getBundleURL:(NSURL * (^)(void))fallbackURLProvider +{ + if (_packagerServerScheme && _packagerServerHost) { + NSArray *jsBundleURLQuery = + [[RCTBundleURLProvider sharedSettings] createJSBundleURLQuery:_packagerServerHost + packagerScheme:_packagerServerScheme]; + + NSString *path = [NSString stringWithFormat:@"/%@.bundle", _bundlePath]; + return [[RCTBundleURLProvider class] resourceURLForResourcePath:path + packagerHost:_packagerServerHost + scheme:_packagerServerScheme + queryItems:jsBundleURLQuery]; + } + + if (_bundleFilePath) { + if (!_bundleFilePath.fileURL) { + RCTLogError(@"Bundle file path must be a file URL"); + return nil; + } + + return _bundleFilePath; + } + + return fallbackURLProvider(); +} + +- (void)clean +{ + _packagerServerHost = nil; + _packagerServerScheme = nil; + _bundleFilePath = nil; +} + +@end @implementation RCTBundleManager { #ifndef RCT_REMOVE_LEGACY_ARCH @@ -18,6 +97,7 @@ @implementation RCTBundleManager { RCTBridgelessBundleURLSetter _bridgelessBundleURLSetter; RCTBridgelessBundleURLGetter _bridgelessBundleURLDefaultGetter; } +@synthesize customBundleConfig; #ifndef RCT_REMOVE_LEGACY_ARCH - (void)setBridge:(RCTBridge *)bridge diff --git a/packages/react-native/React/Base/RCTBundleURLProvider.h b/packages/react-native/React/Base/RCTBundleURLProvider.h index 3ade62f9c2f2ba..06bca4b3144fbc 100644 --- a/packages/react-native/React/Base/RCTBundleURLProvider.h +++ b/packages/react-native/React/Base/RCTBundleURLProvider.h @@ -97,6 +97,19 @@ NS_ASSUME_NONNULL_BEGIN resourceExtension:(NSString *)extension offlineBundle:(NSBundle *)offlineBundle; +- (NSArray *)createJSBundleURLQuery:(NSString *)packagerHost + packagerScheme:(NSString *__nullable)scheme; + ++ (NSArray *)createJSBundleURLQuery:(NSString *)packagerHost + packagerScheme:(NSString *__nullable)scheme + enableDev:(BOOL)enableDev + enableMinification:(BOOL)enableMinification + inlineSourceMap:(BOOL)inlineSourceMap + modulesOnly:(BOOL)modulesOnly + runModule:(BOOL)runModule + additionalOptions:(NSDictionary + *__nullable)additionalOptions; + /** * The IP address or hostname of the packager. */ diff --git a/packages/react-native/React/Base/RCTBundleURLProvider.mm b/packages/react-native/React/Base/RCTBundleURLProvider.mm index 89ccd0c7ad7a58..2a527688df9ef0 100644 --- a/packages/react-native/React/Base/RCTBundleURLProvider.mm +++ b/packages/react-native/React/Base/RCTBundleURLProvider.mm @@ -313,6 +313,44 @@ + (NSURL *__nullable)jsBundleURLForBundleRoot:(NSString *)bundleRoot additionalOptions:(NSDictionary *__nullable)additionalOptions { NSString *path = [NSString stringWithFormat:@"/%@.bundle", bundleRoot]; + NSArray *queryItems = [self createJSBundleURLQuery:packagerHost + packagerScheme:scheme + enableDev:enableDev + enableMinification:enableMinification + inlineSourceMap:inlineSourceMap + modulesOnly:modulesOnly + runModule:runModule + additionalOptions:additionalOptions]; + + return [[self class] resourceURLForResourcePath:path + packagerHost:packagerHost + scheme:scheme + queryItems:[queryItems copy]]; +} + +- (NSArray *)createJSBundleURLQuery:(NSString *)packagerHost + packagerScheme:(NSString *__nullable)scheme +{ + return [[self class] createJSBundleURLQuery:packagerHost + packagerScheme:scheme + enableDev:[self enableDev] + enableMinification:[self enableMinification] + inlineSourceMap:[self inlineSourceMap] + modulesOnly:NO + runModule:YES + additionalOptions:nil]; +} + ++ (NSArray *)createJSBundleURLQuery:(NSString *)packagerHost + packagerScheme:(NSString *__nullable)scheme + enableDev:(BOOL)enableDev + enableMinification:(BOOL)enableMinification + inlineSourceMap:(BOOL)inlineSourceMap + modulesOnly:(BOOL)modulesOnly + runModule:(BOOL)runModule + additionalOptions:(NSDictionary + *__nullable)additionalOptions +{ BOOL lazy = enableDev; NSMutableArray *queryItems = [[NSMutableArray alloc] initWithArray:@[ [[NSURLQueryItem alloc] initWithName:@"platform" value:RCTPlatformName], @@ -344,11 +382,8 @@ + (NSURL *__nullable)jsBundleURLForBundleRoot:(NSString *)bundleRoot [queryItems addObject:[[NSURLQueryItem alloc] initWithName:key value:value]]; } } - - return [[self class] resourceURLForResourcePath:path - packagerHost:packagerHost - scheme:scheme - queryItems:[queryItems copy]]; + + return queryItems; } + (NSURL *)resourceURLForResourcePath:(NSString *)path diff --git a/packages/react-native/React/CoreModules/RCTDevSettings.h b/packages/react-native/React/CoreModules/RCTDevSettings.h index a4d9c8f9e30328..00069c61b8f5f0 100644 --- a/packages/react-native/React/CoreModules/RCTDevSettings.h +++ b/packages/react-native/React/CoreModules/RCTDevSettings.h @@ -10,6 +10,7 @@ #import #import #import +#import @protocol RCTPackagerClientMethod; @@ -85,6 +86,8 @@ */ @property (nonatomic, assign) BOOL isPerfMonitorShown; +@property (nonatomic, readonly) RCTPackagerConnection* packagerConnection; + /** * Toggle the element inspector. */ diff --git a/packages/react-native/React/CoreModules/RCTDevSettings.mm b/packages/react-native/React/CoreModules/RCTDevSettings.mm index fbbeec0e703d53..4e6d5af60836ff 100644 --- a/packages/react-native/React/CoreModules/RCTDevSettings.mm +++ b/packages/react-native/React/CoreModules/RCTDevSettings.mm @@ -145,6 +145,13 @@ - (instancetype)init }; RCTDevSettingsUserDefaultsDataSource *dataSource = [[RCTDevSettingsUserDefaultsDataSource alloc] initWithDefaultValues:defaultValues]; + +#if RCT_DEV_SETTINGS_ENABLE_PACKAGER_CONNECTION + + _packagerConnection = [[RCTPackagerConnection alloc] init]; + +#endif + _isShakeGestureEnabled = true; return [self initWithDataSource:dataSource]; } @@ -179,15 +186,17 @@ - (instancetype)initWithDataSource:(id)dataSource - (void)initialize { #if RCT_DEV_SETTINGS_ENABLE_PACKAGER_CONNECTION + [_packagerConnection startWithBundleManager:_bundleManager]; + if (numInitializedModules++ == 0) { - reloadToken = [[RCTPackagerConnection sharedPackagerConnection] + reloadToken = [_packagerConnection addNotificationHandler:^(id params) { RCTTriggerReloadCommandListeners(@"Global hotkey"); } queue:dispatch_get_main_queue() forMethod:@"reload"]; #if RCT_DEV_MENU - devMenuToken = [[RCTPackagerConnection sharedPackagerConnection] + devMenuToken = [_packagerConnection addNotificationHandler:^(id params) { [[self.moduleRegistry moduleForName:"DevMenu"] show]; } @@ -240,9 +249,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 @@ -425,7 +434,7 @@ - (void)setExecutorClass:(Class)executorClass - (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 } diff --git a/packages/react-native/React/DevSupport/RCTPackagerConnection.h b/packages/react-native/React/DevSupport/RCTPackagerConnection.h index cdd7c745a895ff..b242b7e9759091 100644 --- a/packages/react-native/React/DevSupport/RCTPackagerConnection.h +++ b/packages/react-native/React/DevSupport/RCTPackagerConnection.h @@ -8,6 +8,7 @@ #import #import +#import #if RCT_DEV @@ -24,7 +25,7 @@ typedef void (^RCTConnectedHandler)(void); /** Encapsulates singleton connection to React Native packager. */ @interface RCTPackagerConnection : NSObject -+ (instancetype)sharedPackagerConnection; +@property (nonatomic, weak, readwrite) RCTBundleManager *bundleManager; /** * Registers a handler for a notification broadcast from the packager. An @@ -62,6 +63,8 @@ typedef void (^RCTConnectedHandler)(void); /** Reconnect with given packager server, if packagerServerHostPort has changed. */ - (void)reconnect:(NSString *)packagerServerHostPort; +- (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 95dbf82437c934..399cc8ffaf0282 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 @@ -52,41 +53,12 @@ @implementation RCTPackagerConnection { std::vector> _connectedRegistrations; } -+ (instancetype)sharedPackagerConnection -{ - static RCTPackagerConnection *connection; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - connection = [RCTPackagerConnection new]; - }); - return connection; -} +@synthesize 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 +91,31 @@ - (instancetype)init return [[RCTReconnectingWebSocket alloc] initWithURL:components.URL queue:queue]; } +- (void)startWithBundleManager:(RCTBundleManager *)bundleManager +{ + _serverHostPortForSocket = [bundleManager.customBundleConfig getPackagerServerHost]; + _serverSchemeForSocket = [bundleManager.customBundleConfig getPackagerServerScheme]; + _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]; + }]; +} + - (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.customBundleConfig 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.customBundleConfig getPackagerServerHost]]; } - (RCTHandlerToken)addNotificationHandler:(RCTNotificationHandler)handler diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm index ac4c5c5c2a0f08..47063034ba5ee5 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm @@ -28,6 +28,7 @@ #import #import #import +#import #import #import #import 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 0aa5845a80f3c1..0330b6af862ae6 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 @@ -18,6 +18,7 @@ NS_ASSUME_NONNULL_BEGIN @class RCTFabricSurface; @class RCTHost; @class RCTModuleRegistry; +@class RCTCustomBundleConfiguration; @class RCTDevMenuConfiguration; @protocol RCTTurboModuleManagerDelegate; @@ -65,7 +66,8 @@ typedef std::shared_ptr (^RCTHostJSEngineProv turboModuleManagerDelegate:(id)turboModuleManagerDelegate jsEngineProvider:(RCTHostJSEngineProvider)jsEngineProvider launchOptions:(nullable NSDictionary *)launchOptions - devMenuConfiguration:(RCTDevMenuConfiguration *__nullable)devMenuConfiguration + customBundleConfiguration:(nullable RCTCustomBundleConfiguration *)customBundleConfiguration + devMenuConfiguration:(nullable RCTDevMenuConfiguration *)devMenuConfiguration NS_DESIGNATED_INITIALIZER; - (instancetype)initWithBundleURLProvider:(RCTHostBundleURLProvider)provider 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 5c7bf6b986a7bd..4cedb9fba3b8b7 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 @@ -152,10 +152,6 @@ - (instancetype)initWithBundleURL:(NSURL *)bundleURL 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)hostDelegate turboModuleManagerDelegate:(id)turboModuleManagerDelegate @@ -167,14 +163,20 @@ - (instancetype)initWithBundleURLProvider:(RCTHostBundleURLProvider)provider turboModuleManagerDelegate:turboModuleManagerDelegate jsEngineProvider:jsEngineProvider launchOptions:launchOptions + customBundleConfiguration:nil devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]]; } +/** + 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)hostDelegate turboModuleManagerDelegate:(id)turboModuleManagerDelegate jsEngineProvider:(RCTHostJSEngineProvider)jsEngineProvider launchOptions:(nullable NSDictionary *)launchOptions + customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration { if (self = [super init]) { @@ -184,6 +186,9 @@ - (instancetype)initWithBundleURLProvider:(RCTHostBundleURLProvider)provider _moduleRegistry = [RCTModuleRegistry new]; _jsEngineProvider = [jsEngineProvider copy]; _launchOptions = [launchOptions copy]; + _bundleManager.customBundleConfig = customBundleConfiguration; + + [self setBundleURLProvider:provider]; __weak RCTHost *weakSelf = self; auto bundleURLGetter = ^NSURL *() { @@ -192,7 +197,9 @@ - (instancetype)initWithBundleURLProvider:(RCTHostBundleURLProvider)provider return nil; } - return strongSelf->_bundleURL; + return [strongSelf->_bundleManager.customBundleConfig getBundleURL:^NSURL * { + return strongSelf->_bundleURL; + }]; }; auto bundleURLSetter = ^(NSURL *bundleURL_) { @@ -205,7 +212,9 @@ - (instancetype)initWithBundleURLProvider:(RCTHostBundleURLProvider)provider return nil; } - return strongSelf->_bundleURLProvider(); + return [strongSelf->_bundleManager.customBundleConfig getBundleURL:^NSURL * { + return strongSelf->_bundleURLProvider(); + }]; }; [_bundleManager setBridgelessBundleURLGetter:bundleURLGetter @@ -450,7 +459,7 @@ - (void)_setBundleURL:(NSURL *)bundleURL // Sanitize the bundle URL _bundleURL = [RCTConvert NSURL:_bundleURL.absoluteString]; - // Update the global bundle URLq + // Update the global bundle URL RCTReloadCommandSetBundleURL(_bundleURL); } diff --git a/packages/rn-tester/RNTester/AppDelegate.mm b/packages/rn-tester/RNTester/AppDelegate.mm index 7217807c12205a..c32f32e079828e 100644 --- a/packages/rn-tester/RNTester/AppDelegate.mm +++ b/packages/rn-tester/RNTester/AppDelegate.mm @@ -9,6 +9,7 @@ #import +#import #import #import #import @@ -47,6 +48,14 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( self.dependencyProvider = [RCTAppDependencyProvider new]; #endif +// RCTCustomBundleConfiguration *customBundleConfiguration = +// [[RCTCustomBundleConfiguration alloc] initWithPackagerServerScheme:@"http" packagerServerHost:@"localhost" bundlePath:@"js/custom-bundle.js.bundle"]; +// + NSURL *bundleURL = [NSURL fileURLWithPath:@"js/custom-bundle.js.bundle"]; + RCTCustomBundleConfiguration *customBundleConfiguration = [[RCTCustomBundleConfiguration alloc] initWithBundleFilePath:bundleURL]; + + self.reactNativeFactory.customBundleConfiguration = customBundleConfiguration; + #if RCT_DEV_MENU RCTDevMenuConfiguration *devMenuConfiguration = [[RCTDevMenuConfiguration alloc] initWithDevMenuEnabled:true