From 5f86876a22fbe28a19531d8134d371ff179150e1 Mon Sep 17 00:00:00 2001 From: Dawid Malecki Date: Wed, 24 Sep 2025 11:05:18 +0200 Subject: [PATCH 01/13] RCTCustomBundleConfiguration --- .../AppDelegate/RCTReactNativeFactory.h | 3 +++ .../AppDelegate/RCTReactNativeFactory.mm | 1 + .../React/Base/RCTBundleManager.h | 25 +++++++++++++++++++ .../React/Base/RCTBundleManager.m | 12 +++++++++ 4 files changed, 41 insertions(+) diff --git a/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.h b/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.h index a8b2ab8c6e57b1..92ea6acaa9df0c 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; NS_ASSUME_NONNULL_BEGIN @@ -116,6 +117,8 @@ typedef NS_ENUM(NSInteger, RCTReleaseLevel) { Canary, Experimental, Stable }; @property (nonatomic, weak) id delegate; +@property (nonatomic, nullable, readonly) RCTCustomBundleConfiguration *customBundleConfiguration; + @end NS_ASSUME_NONNULL_END diff --git a/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm b/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm index 9d5a45787af022..997fe8105e2923 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm @@ -11,6 +11,7 @@ #import #import #import +#import #import #import #import diff --git a/packages/react-native/React/Base/RCTBundleManager.h b/packages/react-native/React/Base/RCTBundleManager.h index f7e963ed8b77dd..c2eca001c5ad76 100644 --- a/packages/react-native/React/Base/RCTBundleManager.h +++ b/packages/react-native/React/Base/RCTBundleManager.h @@ -12,6 +12,30 @@ typedef NSURL * (^RCTBridgelessBundleURLGetter)(void); typedef void (^RCTBridgelessBundleURLSetter)(NSURL *bundleURL); +/** + * Configuration class for setting up custom bundle locations + */ +@interface RCTCustomBundleConfiguration + +/** + * 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; + +- (NSURL *)getBundleURL:(NSMutableArray *)query; + +@end + /** * A class that allows NativeModules/TurboModules to read/write the bundleURL, with or without the bridge. */ @@ -24,4 +48,5 @@ typedef void (^RCTBridgelessBundleURLSetter)(NSURL *bundleURL); andDefaultGetter:(RCTBridgelessBundleURLGetter)defaultGetter; - (void)resetBundleURL; @property NSURL *bundleURL; +@property (nonatomic, readonly, nullable) RCTCustomBundleConfiguration *customBundleConfig; @end diff --git a/packages/react-native/React/Base/RCTBundleManager.m b/packages/react-native/React/Base/RCTBundleManager.m index ce06d1d1608a88..988ff107f3b0e0 100644 --- a/packages/react-native/React/Base/RCTBundleManager.m +++ b/packages/react-native/React/Base/RCTBundleManager.m @@ -10,6 +10,17 @@ #import "RCTBridge+Private.h" #import "RCTBridge.h" +@implementation RCTCustomBundleConfiguration +@synthesize bundleFilePath; +@synthesize packagerServerHost; +@synthesize packagerServerScheme; + +- (NSURL *)getBundleURL:(NSMutableArray *)query +{ + return [NSURL alloc]; +} +@end + @implementation RCTBundleManager { #ifndef RCT_FIT_RM_OLD_RUNTIME __weak RCTBridge *_bridge; @@ -18,6 +29,7 @@ @implementation RCTBundleManager { RCTBridgelessBundleURLSetter _bridgelessBundleURLSetter; RCTBridgelessBundleURLGetter _bridgelessBundleURLDefaultGetter; } +@synthesize customBundleConfig; #ifndef RCT_FIT_RM_OLD_RUNTIME - (void)setBridge:(RCTBridge *)bridge From cba9645b3c4fb0377e54ba9d0f69e7c73c8c7d40 Mon Sep 17 00:00:00 2001 From: Dawid Malecki Date: Thu, 25 Sep 2025 16:28:09 +0200 Subject: [PATCH 02/13] pass RCTCustomBundleConfiguration to bundle manager --- .../AppDelegate/RCTReactNativeFactory.h | 2 +- .../AppDelegate/RCTReactNativeFactory.mm | 2 +- .../AppDelegate/RCTRootViewFactory.h | 13 ++++++++++- .../AppDelegate/RCTRootViewFactory.mm | 20 ++++++++++++++-- .../React/Base/RCTBundleManager.h | 9 ++++++-- .../React/Base/RCTBundleManager.m | 23 ++++++++++++++++--- .../platform/ios/ReactCommon/RCTHost.h | 10 +++++++- .../platform/ios/ReactCommon/RCTHost.mm | 17 ++++++++++++++ packages/rn-tester/RNTester/AppDelegate.mm | 5 ++++ 9 files changed, 90 insertions(+), 11 deletions(-) diff --git a/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.h b/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.h index 92ea6acaa9df0c..373d825a69c92b 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.h +++ b/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.h @@ -117,7 +117,7 @@ typedef NS_ENUM(NSInteger, RCTReleaseLevel) { Canary, Experimental, Stable }; @property (nonatomic, weak) id delegate; -@property (nonatomic, nullable, readonly) RCTCustomBundleConfiguration *customBundleConfiguration; +@property (nonatomic, nullable) RCTCustomBundleConfiguration *customBundleConfiguration; @end diff --git a/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm b/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm index 997fe8105e2923..a4d1e1ed088d39 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm @@ -6,12 +6,12 @@ */ #import "RCTReactNativeFactory.h" +#import #import #import #import #import #import -#import #import #import #import diff --git a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h index 35403dac84a72f..184f6a546062e3 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; NS_ASSUME_NONNULL_BEGIN @@ -201,7 +202,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. */ +- (UIView *_Nonnull)viewWithModuleName:(NSString *)moduleName + initialProperties:(NSDictionary *__nullable)initialProperties + launchOptions:(NSDictionary *__nullable)launchOptions + customBundleConfiguration:(RCTCustomBundleConfiguration *__nullable)customBundleConfiguration; + - (UIView *_Nonnull)viewWithModuleName:(NSString *)moduleName initialProperties:(NSDictionary *__nullable)initialProperties launchOptions:(NSDictionary *__nullable)launchOptions; @@ -220,7 +227,11 @@ typedef void (^RCTLoadSourceForBridgeBlock)(RCTBridge *bridge, RCTSourceLoadBloc * * @parameter: launchOptions - a dictionary with a set of options. */ -- (void)initializeReactHostWithLaunchOptions:(NSDictionary *__nullable)launchOptions; +- (void)initializeReactHostWithLaunchOptions:(NSDictionary *__nullable)launchOptions + customBundleConfiguration:(RCTCustomBundleConfiguration *__nullable)customBundleConfiguration; + +- (RCTHost *)createReactHost:(NSDictionary *__nullable)launchOptions + customBundleConfiguration:(RCTCustomBundleConfiguration *__nullable)customBundleConfiguration; - (RCTHost *)createReactHost:(NSDictionary *__nullable)launchOptions; diff --git a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm index a77797a9bddb5a..c7457b7b37b97f 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm @@ -142,6 +142,7 @@ - (UIView *)viewWithModuleName:(NSString *)moduleName } - (void)initializeReactHostWithLaunchOptions:(NSDictionary *)launchOptions + customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration { // Enable TurboModule interop by default in Bridgeless mode RCTEnableTurboModuleInterop(YES); @@ -155,7 +156,15 @@ - (UIView *)viewWithModuleName:(NSString *)moduleName initialProperties:(NSDictionary *)initProps launchOptions:(NSDictionary *)launchOptions { - [self initializeReactHostWithLaunchOptions:launchOptions]; + return [self viewWithModuleName:moduleName initialProperties:initProps launchOptions:launchOptions customBundleConfiguration:nil]; +} + +- (UIView *)viewWithModuleName:(NSString *)moduleName + initialProperties:(NSDictionary *)initProps + launchOptions:(NSDictionary *)launchOptions + customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration +{ + [self initializeReactHostWithLaunchOptions:launchOptions customBundleConfiguration:customBundleConfiguration]; RCTFabricSurface *surface = [self.reactHost createSurfaceWithModuleName:moduleName initialProperties:initProps ? initProps : @{}]; @@ -234,6 +243,12 @@ - (void)createReactHostIfNeeded:(NSDictionary *)launchOptions } - (RCTHost *)createReactHost:(NSDictionary *)launchOptions +{ + return [self createReactHost:launchOptions customBundleConfiguration:nil]; +} + +- (RCTHost *)createReactHost:(NSDictionary *)launchOptions + customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration { __weak __typeof(self) weakSelf = self; RCTHost *reactHost = @@ -243,7 +258,8 @@ - (RCTHost *)createReactHost:(NSDictionary *)launchOptions jsEngineProvider:^std::shared_ptr() { return [weakSelf createJSRuntimeFactory]; } - launchOptions:launchOptions]; + launchOptions:launchOptions + customBundleConfiguration:customBundleConfiguration]; [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 c2eca001c5ad76..facc9304e06292 100644 --- a/packages/react-native/React/Base/RCTBundleManager.h +++ b/packages/react-native/React/Base/RCTBundleManager.h @@ -15,7 +15,7 @@ typedef void (^RCTBridgelessBundleURLSetter)(NSURL *bundleURL); /** * Configuration class for setting up custom bundle locations */ -@interface RCTCustomBundleConfiguration +@interface RCTCustomBundleConfiguration : NSObject /** * The URL of the bundle to load from the file system @@ -32,6 +32,11 @@ typedef void (^RCTBridgelessBundleURLSetter)(NSURL *bundleURL); */ @property (nonatomic, readonly, nullable) NSString *packagerServerHost; +- (instancetype)initWithBundleFilePath:(NSURL *)bundleFilePath; + +- (instancetype)initWithPackagerServerScheme:(NSString *)packagerServerScheme + packagerServerHost:(NSString *)packagerServerHost; + - (NSURL *)getBundleURL:(NSMutableArray *)query; @end @@ -48,5 +53,5 @@ typedef void (^RCTBridgelessBundleURLSetter)(NSURL *bundleURL); andDefaultGetter:(RCTBridgelessBundleURLGetter)defaultGetter; - (void)resetBundleURL; @property NSURL *bundleURL; -@property (nonatomic, readonly, nullable) RCTCustomBundleConfiguration *customBundleConfig; +@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 988ff107f3b0e0..5b67ce08518940 100644 --- a/packages/react-native/React/Base/RCTBundleManager.m +++ b/packages/react-native/React/Base/RCTBundleManager.m @@ -11,14 +11,31 @@ #import "RCTBridge.h" @implementation RCTCustomBundleConfiguration -@synthesize bundleFilePath; -@synthesize packagerServerHost; -@synthesize packagerServerScheme; + +- (instancetype)initWithBundleFilePath:(NSURL *)bundleFilePath +{ + if (self = [super init]) { + _bundleFilePath = bundleFilePath; + } + + return self; +} + +- (instancetype)initWithPackagerServerScheme:(NSString *)packagerServerScheme packagerServerHost:(NSString *)packagerServerHost +{ + if (self = [super init]) { + _packagerServerScheme = packagerServerScheme; + _packagerServerHost = packagerServerHost; + } + + return self; +} - (NSURL *)getBundleURL:(NSMutableArray *)query { return [NSURL alloc]; } + @end @implementation RCTBundleManager { 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 c72cf7cdd6f641..b2906263bff194 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; @protocol RCTTurboModuleManagerDelegate; @@ -63,7 +64,14 @@ typedef std::shared_ptr (^RCTHostJSEngineProv hostDelegate:(id)hostDelegate turboModuleManagerDelegate:(id)turboModuleManagerDelegate jsEngineProvider:(RCTHostJSEngineProvider)jsEngineProvider - launchOptions:(nullable NSDictionary *)launchOptions NS_DESIGNATED_INITIALIZER; + launchOptions:(nullable NSDictionary *)launchOptions + customBundleConfiguration:(nullable RCTCustomBundleConfiguration *)customBundleConfiguration NS_DESIGNATED_INITIALIZER; + +- (instancetype)initWithBundleURLProvider:(RCTHostBundleURLProvider)provider + hostDelegate:(id)hostDelegate + turboModuleManagerDelegate:(id)turboModuleManagerDelegate + jsEngineProvider:(RCTHostJSEngineProvider)jsEngineProvider + launchOptions:(nullable NSDictionary *)launchOptions; - (instancetype)initWithBundleURL:(NSURL *)bundleURL hostDelegate:(id)hostDelegate 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 12631915e88d84..161681872168e9 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 @@ -150,6 +150,20 @@ - (instancetype)initWithBundleURL:(NSURL *)bundleURL launchOptions:launchOptions]; } +- (instancetype)initWithBundleURLProvider:(RCTHostBundleURLProvider)provider + hostDelegate:(id)hostDelegate + turboModuleManagerDelegate:(id)turboModuleManagerDelegate + jsEngineProvider:(RCTHostJSEngineProvider)jsEngineProvider + launchOptions:(nullable NSDictionary *)launchOptions +{ + return [self initWithBundleURLProvider:provider + hostDelegate:hostDelegate + turboModuleManagerDelegate:turboModuleManagerDelegate + jsEngineProvider:jsEngineProvider + launchOptions:launchOptions + customBundleConfiguration:nil]; +} + /** Host initialization should not be resource intensive. A host may be created before any intention of using React Native has been expressed. @@ -159,6 +173,7 @@ - (instancetype)initWithBundleURLProvider:(RCTHostBundleURLProvider)provider turboModuleManagerDelegate:(id)turboModuleManagerDelegate jsEngineProvider:(RCTHostJSEngineProvider)jsEngineProvider launchOptions:(nullable NSDictionary *)launchOptions + customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration { if (self = [super init]) { _hostDelegate = hostDelegate; @@ -167,6 +182,8 @@ - (instancetype)initWithBundleURLProvider:(RCTHostBundleURLProvider)provider _moduleRegistry = [RCTModuleRegistry new]; _jsEngineProvider = [jsEngineProvider copy]; _launchOptions = [launchOptions copy]; + + _bundleManager.customBundleConfig = customBundleConfiguration; __weak RCTHost *weakSelf = self; auto bundleURLGetter = ^NSURL *() { diff --git a/packages/rn-tester/RNTester/AppDelegate.mm b/packages/rn-tester/RNTester/AppDelegate.mm index cf02164f8945b8..2df9ab74d23702 100644 --- a/packages/rn-tester/RNTester/AppDelegate.mm +++ b/packages/rn-tester/RNTester/AppDelegate.mm @@ -11,6 +11,7 @@ #import #import +#import #import #import #import @@ -42,6 +43,10 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( #if USE_OSS_CODEGEN self.dependencyProvider = [RCTAppDependencyProvider new]; #endif + + RCTCustomBundleConfiguration *customBundleConfiguration = [[RCTCustomBundleConfiguration alloc] initWithPackagerServerScheme:@"http" packagerServerHost:@"http"]; + + self.reactNativeFactory.customBundleConfiguration = customBundleConfiguration; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; From 4e706b5ed88772c0430f67c1ddc44e34b84a2e40 Mon Sep 17 00:00:00 2001 From: Dawid Malecki Date: Fri, 26 Sep 2025 16:52:07 +0200 Subject: [PATCH 03/13] starting packager connection --- .../React/CoreModules/RCTDevSettings.h | 5 +++++ .../React/CoreModules/RCTDevSettings.mm | 22 ++++++++++++++----- .../React/DevSupport/RCTPackagerConnection.h | 3 ++- .../React/DevSupport/RCTPackagerConnection.mm | 11 ++-------- .../ios/ReactCommon/RCTTurboModuleManager.mm | 6 +++++ 5 files changed, 32 insertions(+), 15 deletions(-) diff --git a/packages/react-native/React/CoreModules/RCTDevSettings.h b/packages/react-native/React/CoreModules/RCTDevSettings.h index c8db73ec9427fd..38c37a3f0c91cf 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; @@ -80,6 +81,8 @@ */ @property (nonatomic, assign) BOOL isPerfMonitorShown; +@property (nonatomic, readonly) RCTPackagerConnection* packagerConnection; + /** * Toggle the element inspector. */ @@ -95,6 +98,8 @@ */ - (void)setupHMRClientWithAdditionalBundleURL:(NSURL *)bundleURL; +- (void)startPackagerConnection; + #if RCT_DEV_MENU - (void)addHandler:(id)handler forPackagerMethod:(NSString *)name __deprecated_msg("Use RCTPackagerConnection directly instead"); diff --git a/packages/react-native/React/CoreModules/RCTDevSettings.mm b/packages/react-native/React/CoreModules/RCTDevSettings.mm index 4b3e4749496270..19806f34a0cb4f 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 + return [self initWithDataSource:dataSource]; } @@ -179,14 +186,14 @@ - (void)initialize { #if RCT_DEV_SETTINGS_ENABLE_PACKAGER_CONNECTION 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]; } @@ -239,9 +246,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 @@ -262,6 +269,11 @@ - (id)settingForKey:(NSString *)key return [_dataSource settingForKey:key]; } +- (void)startPackagerConnection +{ + NSLog(@"Starting Packager Connection from RCTDevSettings"); +} + - (BOOL)isDeviceDebuggingAvailable { #if RCT_ENABLE_INSPECTOR @@ -418,7 +430,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 014f73f97933f8..a7320c471f854d 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 diff --git a/packages/react-native/React/DevSupport/RCTPackagerConnection.mm b/packages/react-native/React/DevSupport/RCTPackagerConnection.mm index 39419d60e12036..2394fdd3b4fc73 100644 --- a/packages/react-native/React/DevSupport/RCTPackagerConnection.mm +++ b/packages/react-native/React/DevSupport/RCTPackagerConnection.mm @@ -22,6 +22,7 @@ #import #import #import +#import #if RCT_DEV @@ -52,15 +53,7 @@ @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 { 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 5168bbf415d1a7..0f75397d0c870b 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 @@ -27,6 +27,7 @@ #import #import #import +#import #import #import #import @@ -769,6 +770,11 @@ - (BOOL)_shouldCreateObjCModule:(Class)moduleClass if ([module respondsToSelector:@selector(initialize)]) { [(id)module initialize]; } + + if ([module isKindOfClass:[RCTDevSettings class]]) { + RCTDevSettings *devSettings = (RCTDevSettings *)module; + [devSettings startPackagerConnection]; + } /** * Attach method queue to id object. From 6c0696ebdce4c2f86f52bd29b5f0225b61cb0d2a Mon Sep 17 00:00:00 2001 From: Dawid Malecki Date: Tue, 30 Sep 2025 17:15:05 +0200 Subject: [PATCH 04/13] bundle url setup --- .../AppDelegate/RCTReactNativeFactory.mm | 5 +- .../AppDelegate/RCTRootViewFactory.mm | 20 +++---- .../React/Base/RCTBundleManager.h | 8 ++- .../React/Base/RCTBundleManager.m | 43 ++++++++++++++- .../React/Base/RCTBundleURLProvider.h | 13 +++++ .../React/Base/RCTBundleURLProvider.mm | 45 ++++++++++++++-- .../React/CoreModules/RCTDevSettings.h | 2 - .../React/CoreModules/RCTDevSettings.mm | 7 +-- .../React/DevSupport/RCTPackagerConnection.h | 2 + .../React/DevSupport/RCTPackagerConnection.mm | 52 ++++++++++--------- .../ios/ReactCommon/RCTTurboModuleManager.mm | 5 -- .../platform/ios/ReactCommon/RCTHost.mm | 13 +++-- packages/rn-tester/RNTester/AppDelegate.mm | 9 ++-- packages/rn-tester/js/RNTesterAppShared.js | 4 +- 14 files changed, 163 insertions(+), 65 deletions(-) diff --git a/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm b/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm index a4d1e1ed088d39..1017ee6bb5d9fd 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm @@ -59,6 +59,8 @@ - (instancetype)initWithDelegate:(id)delegate rel self.rootViewFactory = [self createRCTRootViewFactory]; [RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self; + + self.customBundleConfiguration = [[RCTCustomBundleConfiguration alloc] init]; } return self; @@ -83,7 +85,8 @@ - (void)startReactNativeWithModuleName:(NSString *)moduleName { UIView *rootView = [self.rootViewFactory viewWithModuleName:moduleName initialProperties:initialProperties - launchOptions:launchOptions]; + launchOptions:launchOptions + customBundleConfiguration:self.customBundleConfiguration]; UIViewController *rootViewController = [_delegate createRootViewController]; [_delegate setRootView:rootView toRootViewController:rootViewController]; window.rootViewController = rootViewController; diff --git a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm index c7457b7b37b97f..fd18b4dc70beb9 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm @@ -20,7 +20,6 @@ #else #import #endif -#import #import #import #import @@ -142,13 +141,13 @@ - (UIView *)viewWithModuleName:(NSString *)moduleName } - (void)initializeReactHostWithLaunchOptions:(NSDictionary *)launchOptions - customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration + customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration { // Enable TurboModule interop by default in Bridgeless mode RCTEnableTurboModuleInterop(YES); RCTEnableTurboModuleInteropBridgeProxy(YES); - [self createReactHostIfNeeded:launchOptions]; + [self createReactHostIfNeeded:launchOptions customBundleConfiguration:customBundleConfiguration]; return; } @@ -156,7 +155,10 @@ - (UIView *)viewWithModuleName:(NSString *)moduleName initialProperties:(NSDictionary *)initProps launchOptions:(NSDictionary *)launchOptions { - return [self viewWithModuleName:moduleName initialProperties:initProps launchOptions:launchOptions customBundleConfiguration:nil]; + return [self viewWithModuleName:moduleName + initialProperties:initProps + launchOptions:launchOptions + customBundleConfiguration:nil]; } - (UIView *)viewWithModuleName:(NSString *)moduleName @@ -235,11 +237,12 @@ - (void)createBridgeAdapterIfNeeded #pragma mark - New Arch Utilities - (void)createReactHostIfNeeded:(NSDictionary *)launchOptions + customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration { if (self.reactHost) { return; } - self.reactHost = [self createReactHost:launchOptions]; + self.reactHost = [self createReactHost:launchOptions customBundleConfiguration:customBundleConfiguration]; } - (RCTHost *)createReactHost:(NSDictionary *)launchOptions @@ -248,7 +251,7 @@ - (RCTHost *)createReactHost:(NSDictionary *)launchOptions } - (RCTHost *)createReactHost:(NSDictionary *)launchOptions - customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration + customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration { __weak __typeof(self) weakSelf = self; RCTHost *reactHost = @@ -259,10 +262,7 @@ - (RCTHost *)createReactHost:(NSDictionary *)launchOptions return [weakSelf createJSRuntimeFactory]; } launchOptions:launchOptions - customBundleConfiguration:customBundleConfiguration]; - [reactHost setBundleURLProvider:^NSURL *() { - return [weakSelf bundleURL]; - }]; + customBundleConfiguration:customBundleConfiguration]; [reactHost start]; return reactHost; } diff --git a/packages/react-native/React/Base/RCTBundleManager.h b/packages/react-native/React/Base/RCTBundleManager.h index facc9304e06292..9783c232fb5624 100644 --- a/packages/react-native/React/Base/RCTBundleManager.h +++ b/packages/react-native/React/Base/RCTBundleManager.h @@ -37,7 +37,13 @@ typedef void (^RCTBridgelessBundleURLSetter)(NSURL *bundleURL); - (instancetype)initWithPackagerServerScheme:(NSString *)packagerServerScheme packagerServerHost:(NSString *)packagerServerHost; -- (NSURL *)getBundleURL:(NSMutableArray *)query; +- (NSURL *)getBundleURL:(NSURL *__nullable (^)(void))fallbackURLProvider; + +- (NSString *)getPackagerServerScheme; + +- (NSString *)getPackagerServerHost; + +- (void)clean; @end diff --git a/packages/react-native/React/Base/RCTBundleManager.m b/packages/react-native/React/Base/RCTBundleManager.m index 5b67ce08518940..781ba509c0ec42 100644 --- a/packages/react-native/React/Base/RCTBundleManager.m +++ b/packages/react-native/React/Base/RCTBundleManager.m @@ -9,6 +9,7 @@ #import "RCTAssert.h" #import "RCTBridge+Private.h" #import "RCTBridge.h" +#import @implementation RCTCustomBundleConfiguration @@ -31,9 +32,47 @@ - (instancetype)initWithPackagerServerScheme:(NSString *)packagerServerScheme pa return self; } -- (NSURL *)getBundleURL:(NSMutableArray *)query +- (NSString *)getPackagerServerScheme { - return [NSURL alloc]; + 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]; + + return [[RCTBundleURLProvider class] resourceURLForResourcePath:@"js/RNTesterApp.ios" + packagerHost:_packagerServerHost + scheme:_packagerServerScheme + queryItems:jsBundleURLQuery]; + } + + if (_bundleFilePath) { + // TODO: modify bundle path + } + + return fallbackURLProvider(); +} + +- (void)clean +{ + _packagerServerHost = nil; + _packagerServerScheme = nil; + _bundleFilePath = nil; } @end 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 fb1d601c075d8d..82a017d8261dff 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 38c37a3f0c91cf..1c4c632a689642 100644 --- a/packages/react-native/React/CoreModules/RCTDevSettings.h +++ b/packages/react-native/React/CoreModules/RCTDevSettings.h @@ -98,8 +98,6 @@ */ - (void)setupHMRClientWithAdditionalBundleURL:(NSURL *)bundleURL; -- (void)startPackagerConnection; - #if RCT_DEV_MENU - (void)addHandler:(id)handler forPackagerMethod:(NSString *)name __deprecated_msg("Use RCTPackagerConnection directly instead"); diff --git a/packages/react-native/React/CoreModules/RCTDevSettings.mm b/packages/react-native/React/CoreModules/RCTDevSettings.mm index 19806f34a0cb4f..e4bce8e6b8f2d5 100644 --- a/packages/react-native/React/CoreModules/RCTDevSettings.mm +++ b/packages/react-native/React/CoreModules/RCTDevSettings.mm @@ -185,6 +185,8 @@ - (instancetype)initWithDataSource:(id)dataSource - (void)initialize { #if RCT_DEV_SETTINGS_ENABLE_PACKAGER_CONNECTION + [_packagerConnection startWithBundleManager:_bundleManager]; + if (numInitializedModules++ == 0) { reloadToken = [_packagerConnection addNotificationHandler:^(id params) { @@ -269,11 +271,6 @@ - (id)settingForKey:(NSString *)key return [_dataSource settingForKey:key]; } -- (void)startPackagerConnection -{ - NSLog(@"Starting Packager Connection from RCTDevSettings"); -} - - (BOOL)isDeviceDebuggingAvailable { #if RCT_ENABLE_INSPECTOR diff --git a/packages/react-native/React/DevSupport/RCTPackagerConnection.h b/packages/react-native/React/DevSupport/RCTPackagerConnection.h index a7320c471f854d..d33eca2c4a6439 100644 --- a/packages/react-native/React/DevSupport/RCTPackagerConnection.h +++ b/packages/react-native/React/DevSupport/RCTPackagerConnection.h @@ -63,6 +63,8 @@ typedef void (^RCTConnectedHandler)(void); /** Reconnect with given packager server. */ - (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 2394fdd3b4fc73..390b14006d86dd 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 @@ -22,7 +23,6 @@ #import #import #import -#import #if RCT_DEV @@ -59,27 +59,6 @@ - (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; } @@ -112,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); @@ -137,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 } @@ -153,7 +157,7 @@ - (void)reconnect:(NSString *)packagerServerHostPort - (void)bundleURLSettingsChanged { - [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 0f75397d0c870b..5dbb61795e9801 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 @@ -770,11 +770,6 @@ - (BOOL)_shouldCreateObjCModule:(Class)moduleClass if ([module respondsToSelector:@selector(initialize)]) { [(id)module initialize]; } - - if ([module isKindOfClass:[RCTDevSettings class]]) { - RCTDevSettings *devSettings = (RCTDevSettings *)module; - [devSettings startPackagerConnection]; - } /** * Attach method queue to id object. 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 161681872168e9..2737d9f697a5e5 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 @@ -182,9 +182,10 @@ - (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 *() { RCTHost *strongSelf = weakSelf; @@ -192,7 +193,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 +208,9 @@ - (instancetype)initWithBundleURLProvider:(RCTHostBundleURLProvider)provider return nil; } - return strongSelf->_bundleURLProvider(); + return [strongSelf->_bundleManager.customBundleConfig getBundleURL:^NSURL * { + return strongSelf->_bundleURLProvider(); + }]; }; [_bundleManager setBridgelessBundleURLGetter:bundleURLGetter @@ -448,7 +453,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 2df9ab74d23702..524ee98f568fea 100644 --- a/packages/rn-tester/RNTester/AppDelegate.mm +++ b/packages/rn-tester/RNTester/AppDelegate.mm @@ -9,9 +9,9 @@ #import +#import #import #import -#import #import #import #import @@ -43,9 +43,10 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( #if USE_OSS_CODEGEN self.dependencyProvider = [RCTAppDependencyProvider new]; #endif - - RCTCustomBundleConfiguration *customBundleConfiguration = [[RCTCustomBundleConfiguration alloc] initWithPackagerServerScheme:@"http" packagerServerHost:@"http"]; - + + RCTCustomBundleConfiguration *customBundleConfiguration = + [[RCTCustomBundleConfiguration alloc] initWithPackagerServerScheme:@"http" packagerServerHost:@"localhost"]; + self.reactNativeFactory.customBundleConfiguration = customBundleConfiguration; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; diff --git a/packages/rn-tester/js/RNTesterAppShared.js b/packages/rn-tester/js/RNTesterAppShared.js index 6a53954e3df65d..2d8f2288f5d8bd 100644 --- a/packages/rn-tester/js/RNTesterAppShared.js +++ b/packages/rn-tester/js/RNTesterAppShared.js @@ -292,7 +292,7 @@ const RNTesterApp = ({ ) : undefined} )} - @@ -308,7 +308,7 @@ const RNTesterApp = ({ handleModuleCardPress={handleModuleCardPress} /> )} - + */} {!shouldHideChrome && ( Date: Wed, 1 Oct 2025 15:12:29 +0200 Subject: [PATCH 05/13] setup path in RCTBundleManager --- packages/react-native/React/Base/RCTBundleManager.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react-native/React/Base/RCTBundleManager.m b/packages/react-native/React/Base/RCTBundleManager.m index 781ba509c0ec42..dc05f04d41b71d 100644 --- a/packages/react-native/React/Base/RCTBundleManager.m +++ b/packages/react-native/React/Base/RCTBundleManager.m @@ -55,7 +55,8 @@ - (NSURL *)getBundleURL:(NSURL * (^)(void))fallbackURLProvider if (_packagerServerScheme && _packagerServerHost) { NSArray *jsBundleURLQuery = [[RCTBundleURLProvider sharedSettings] createJSBundleURLQuery:_packagerServerHost packagerScheme:_packagerServerScheme]; - return [[RCTBundleURLProvider class] resourceURLForResourcePath:@"js/RNTesterApp.ios" + NSString *path = [NSString stringWithFormat:@"/%@.bundle", @"js/RNTesterApp.ios"]; + return [[RCTBundleURLProvider class] resourceURLForResourcePath:path packagerHost:_packagerServerHost scheme:_packagerServerScheme queryItems:jsBundleURLQuery]; From b9a85b686a977380f43133bb6f64211b111c75ac Mon Sep 17 00:00:00 2001 From: Dawid Malecki Date: Wed, 1 Oct 2025 16:29:24 +0200 Subject: [PATCH 06/13] fix builds --- .../Libraries/AppDelegate/RCTRootViewFactory.mm | 2 ++ .../runtime/platform/ios/ReactCommon/RCTHost.h | 2 +- .../runtime/platform/ios/ReactCommon/RCTHost.mm | 17 ++--------------- packages/rn-tester/js/RNTesterAppShared.js | 4 ++-- 4 files changed, 7 insertions(+), 18 deletions(-) diff --git a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm index acd7f2fc124336..2fd7ba8b2bc182 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm @@ -136,6 +136,7 @@ - (UIView *)viewWithModuleName:(NSString *)moduleName initialProperties:(NSDicti return [self viewWithModuleName:moduleName initialProperties:initialProperties launchOptions:nil + customBundleConfiguration:nil devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]]; } @@ -144,6 +145,7 @@ - (UIView *)viewWithModuleName:(NSString *)moduleName return [self viewWithModuleName:moduleName initialProperties:nil launchOptions:nil + customBundleConfiguration:nil devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]]; } 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 ac648c84fb6a83..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 @@ -67,7 +67,7 @@ typedef std::shared_ptr (^RCTHostJSEngineProv jsEngineProvider:(RCTHostJSEngineProvider)jsEngineProvider launchOptions:(nullable NSDictionary *)launchOptions customBundleConfiguration:(nullable RCTCustomBundleConfiguration *)customBundleConfiguration - devMenuConfiguration:(RCTDevMenuConfiguration *__nullable)devMenuConfiguration + 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 3e2bf14c0bc86a..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 @@ -163,7 +163,8 @@ - (instancetype)initWithBundleURLProvider:(RCTHostBundleURLProvider)provider turboModuleManagerDelegate:turboModuleManagerDelegate jsEngineProvider:jsEngineProvider launchOptions:launchOptions - customBundleConfiguration:nil]; + customBundleConfiguration:nil + devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]]; } /** @@ -176,20 +177,6 @@ - (instancetype)initWithBundleURLProvider:(RCTHostBundleURLProvider)provider jsEngineProvider:(RCTHostJSEngineProvider)jsEngineProvider launchOptions:(nullable NSDictionary *)launchOptions customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration -{ - return [self initWithBundleURLProvider:provider - hostDelegate:hostDelegate - turboModuleManagerDelegate:turboModuleManagerDelegate - jsEngineProvider:jsEngineProvider - launchOptions:launchOptions - devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]]; -} - -- (instancetype)initWithBundleURLProvider:(RCTHostBundleURLProvider)provider - hostDelegate:(id)hostDelegate - turboModuleManagerDelegate:(id)turboModuleManagerDelegate - jsEngineProvider:(RCTHostJSEngineProvider)jsEngineProvider - launchOptions:(nullable NSDictionary *)launchOptions devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration { if (self = [super init]) { diff --git a/packages/rn-tester/js/RNTesterAppShared.js b/packages/rn-tester/js/RNTesterAppShared.js index 2d8f2288f5d8bd..6a53954e3df65d 100644 --- a/packages/rn-tester/js/RNTesterAppShared.js +++ b/packages/rn-tester/js/RNTesterAppShared.js @@ -292,7 +292,7 @@ const RNTesterApp = ({ ) : undefined} )} - {/* @@ -308,7 +308,7 @@ const RNTesterApp = ({ handleModuleCardPress={handleModuleCardPress} /> )} - */} + {!shouldHideChrome && ( Date: Fri, 3 Oct 2025 12:14:57 +0200 Subject: [PATCH 07/13] pass bundlePath --- .../React/Base/RCTBundleManager.h | 8 ++++- .../React/Base/RCTBundleManager.m | 33 +++++++++++-------- packages/rn-tester/RNTester/AppDelegate.mm | 2 +- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/packages/react-native/React/Base/RCTBundleManager.h b/packages/react-native/React/Base/RCTBundleManager.h index e3bc5b3310571a..21153bc36d6be4 100644 --- a/packages/react-native/React/Base/RCTBundleManager.h +++ b/packages/react-native/React/Base/RCTBundleManager.h @@ -32,10 +32,16 @@ typedef void (^RCTBridgelessBundleURLSetter)(NSURL *bundleURL); */ @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; + packagerServerHost:(NSString *)packagerServerHost + bundlePath:(NSString *)bundlePath; - (NSURL *)getBundleURL:(NSURL *__nullable (^)(void))fallbackURLProvider; diff --git a/packages/react-native/React/Base/RCTBundleManager.m b/packages/react-native/React/Base/RCTBundleManager.m index a24e90e16de592..0dce2bfdea4c84 100644 --- a/packages/react-native/React/Base/RCTBundleManager.m +++ b/packages/react-native/React/Base/RCTBundleManager.m @@ -6,10 +6,10 @@ */ #import "RCTBundleManager.h" +#import #import "RCTAssert.h" #import "RCTBridge+Private.h" #import "RCTBridge.h" -#import @implementation RCTCustomBundleConfiguration @@ -18,17 +18,20 @@ - (instancetype)initWithBundleFilePath:(NSURL *)bundleFilePath if (self = [super init]) { _bundleFilePath = bundleFilePath; } - + return self; } -- (instancetype)initWithPackagerServerScheme:(NSString *)packagerServerScheme packagerServerHost:(NSString *)packagerServerHost +- (instancetype)initWithPackagerServerScheme:(NSString *)packagerServerScheme xw + packagerServerHost:(NSString *)packagerServerHost + bundlePath:(NSString *)bundlePath { if (self = [super init]) { _packagerServerScheme = packagerServerScheme; _packagerServerHost = packagerServerHost; + _bundlePath = bundlePath; } - + return self; } @@ -37,7 +40,7 @@ - (NSString *)getPackagerServerScheme if (!_packagerServerScheme) { return [[RCTBundleURLProvider sharedSettings] packagerScheme]; } - + return _packagerServerScheme; } @@ -46,26 +49,28 @@ - (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", @"js/RNTesterApp.ios"]; + 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]; + packagerHost:_packagerServerHost + scheme:_packagerServerScheme + queryItems:jsBundleURLQuery]; } - + if (_bundleFilePath) { // TODO: modify bundle path } - + return fallbackURLProvider(); } diff --git a/packages/rn-tester/RNTester/AppDelegate.mm b/packages/rn-tester/RNTester/AppDelegate.mm index cb90b5b3b1e7eb..3f3b42a4282585 100644 --- a/packages/rn-tester/RNTester/AppDelegate.mm +++ b/packages/rn-tester/RNTester/AppDelegate.mm @@ -49,7 +49,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( #endif RCTCustomBundleConfiguration *customBundleConfiguration = - [[RCTCustomBundleConfiguration alloc] initWithPackagerServerScheme:@"http" packagerServerHost:@"localhost"]; + [[RCTCustomBundleConfiguration alloc] initWithPackagerServerScheme:@"http" packagerServerHost:@"localhost" bundlePath:kBundlePath]; self.reactNativeFactory.customBundleConfiguration = customBundleConfiguration; From 2d05c323883ef247eb0387421d794dd250413279 Mon Sep 17 00:00:00 2001 From: Dawid Malecki Date: Fri, 3 Oct 2025 12:16:03 +0200 Subject: [PATCH 08/13] fix --- packages/react-native/React/Base/RCTBundleManager.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/React/Base/RCTBundleManager.m b/packages/react-native/React/Base/RCTBundleManager.m index 0dce2bfdea4c84..4f35e26a3eaf7d 100644 --- a/packages/react-native/React/Base/RCTBundleManager.m +++ b/packages/react-native/React/Base/RCTBundleManager.m @@ -22,7 +22,7 @@ - (instancetype)initWithBundleFilePath:(NSURL *)bundleFilePath return self; } -- (instancetype)initWithPackagerServerScheme:(NSString *)packagerServerScheme xw +- (instancetype)initWithPackagerServerScheme:(NSString *)packagerServerScheme packagerServerHost:(NSString *)packagerServerHost bundlePath:(NSString *)bundlePath { From fb8d9f0340313a884946700566a3e66e1d8d5e66 Mon Sep 17 00:00:00 2001 From: Dawid Malecki Date: Fri, 3 Oct 2025 17:37:43 +0200 Subject: [PATCH 09/13] fail if bundleFilePath has not fileURL --- packages/react-native/React/Base/RCTBundleManager.m | 8 +++++++- packages/rn-tester/RNTester/AppDelegate.mm | 9 ++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/react-native/React/Base/RCTBundleManager.m b/packages/react-native/React/Base/RCTBundleManager.m index 4f35e26a3eaf7d..29afbd3ead14df 100644 --- a/packages/react-native/React/Base/RCTBundleManager.m +++ b/packages/react-native/React/Base/RCTBundleManager.m @@ -10,6 +10,7 @@ #import "RCTAssert.h" #import "RCTBridge+Private.h" #import "RCTBridge.h" +#import "RCTLog.h" @implementation RCTCustomBundleConfiguration @@ -68,7 +69,12 @@ - (NSURL *)getBundleURL:(NSURL * (^)(void))fallbackURLProvider } if (_bundleFilePath) { - // TODO: modify bundle path + if (!_bundleFilePath.fileURL) { + RCTLogError(@"Bundle file path must be a file URL"); + return nil; + } + + return _bundleFilePath; } return fallbackURLProvider(); diff --git a/packages/rn-tester/RNTester/AppDelegate.mm b/packages/rn-tester/RNTester/AppDelegate.mm index 3f3b42a4282585..c32f32e079828e 100644 --- a/packages/rn-tester/RNTester/AppDelegate.mm +++ b/packages/rn-tester/RNTester/AppDelegate.mm @@ -48,9 +48,12 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( self.dependencyProvider = [RCTAppDependencyProvider new]; #endif - RCTCustomBundleConfiguration *customBundleConfiguration = - [[RCTCustomBundleConfiguration alloc] initWithPackagerServerScheme:@"http" packagerServerHost:@"localhost" bundlePath:kBundlePath]; - +// 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 From d2b94f152654dbdca24553c458b29f874578bb26 Mon Sep 17 00:00:00 2001 From: Dawid Malecki Date: Mon, 6 Oct 2025 16:58:30 +0200 Subject: [PATCH 10/13] remove clean --- packages/react-native/React/Base/RCTBundleManager.h | 2 -- packages/react-native/React/Base/RCTBundleManager.m | 7 ------- 2 files changed, 9 deletions(-) diff --git a/packages/react-native/React/Base/RCTBundleManager.h b/packages/react-native/React/Base/RCTBundleManager.h index 21153bc36d6be4..10bd81f621af63 100644 --- a/packages/react-native/React/Base/RCTBundleManager.h +++ b/packages/react-native/React/Base/RCTBundleManager.h @@ -49,8 +49,6 @@ typedef void (^RCTBridgelessBundleURLSetter)(NSURL *bundleURL); - (NSString *)getPackagerServerHost; -- (void)clean; - @end /** diff --git a/packages/react-native/React/Base/RCTBundleManager.m b/packages/react-native/React/Base/RCTBundleManager.m index 29afbd3ead14df..71fab0e2eb42c7 100644 --- a/packages/react-native/React/Base/RCTBundleManager.m +++ b/packages/react-native/React/Base/RCTBundleManager.m @@ -80,13 +80,6 @@ - (NSURL *)getBundleURL:(NSURL * (^)(void))fallbackURLProvider return fallbackURLProvider(); } -- (void)clean -{ - _packagerServerHost = nil; - _packagerServerScheme = nil; - _bundleFilePath = nil; -} - @end @implementation RCTBundleManager { From c8c11c5edb95ecad706fe858a896920e174d068d Mon Sep 17 00:00:00 2001 From: Dawid Malecki Date: Tue, 7 Oct 2025 13:53:07 +0200 Subject: [PATCH 11/13] fix RCT_DEV set to false --- packages/react-native/React/CoreModules/RCTDevSettings.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react-native/React/CoreModules/RCTDevSettings.h b/packages/react-native/React/CoreModules/RCTDevSettings.h index 00069c61b8f5f0..87dbebf7154c4a 100644 --- a/packages/react-native/React/CoreModules/RCTDevSettings.h +++ b/packages/react-native/React/CoreModules/RCTDevSettings.h @@ -10,7 +10,8 @@ #import #import #import -#import + +@class RCTPackagerConnection; @protocol RCTPackagerClientMethod; From 57004dd7cabd46073deaa9fc7009e1558b8bb0a5 Mon Sep 17 00:00:00 2001 From: Dawid Malecki Date: Tue, 7 Oct 2025 14:08:57 +0200 Subject: [PATCH 12/13] hide connect to Metro message --- .../react/runtime/platform/ios/ReactCommon/RCTInstance.mm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 d065b1b7943d0a..babd9584d3bfc1 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 @@ -535,7 +535,11 @@ - (void)_loadJSBundle:(NSURL *)sourceURL { id loadingView = (id)[_turboModuleManager moduleForName:"DevLoadingView"]; - [loadingView showWithURL:sourceURL]; + // When the bundleFilePath is set in the RCTCustomBundleConfiguration it on purpose does not run the packager connection + // so we don't show the message. + if (!_bridgeModuleDecorator.bundleManager.customBundleConfig.bundleFilePath) { + [loadingView showWithURL:sourceURL]; + } } #endif From 4b5f8fb411ac98000336674b54469b6a2c328db0 Mon Sep 17 00:00:00 2001 From: Dawid Malecki Date: Tue, 7 Oct 2025 14:25:46 +0200 Subject: [PATCH 13/13] change message --- .../react/runtime/platform/ios/ReactCommon/RCTInstance.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 babd9584d3bfc1..b25f4d1db91d13 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 @@ -535,8 +535,8 @@ - (void)_loadJSBundle:(NSURL *)sourceURL { id loadingView = (id)[_turboModuleManager moduleForName:"DevLoadingView"]; - // When the bundleFilePath is set in the RCTCustomBundleConfiguration it on purpose does not run the packager connection - // so we don't show the message. + // When the bundleFilePath is set in the RCTCustomBundleConfiguration the Metro connection + // shouldn't be suggested/required. if (!_bridgeModuleDecorator.bundleManager.customBundleConfig.bundleFilePath) { [loadingView showWithURL:sourceURL]; }