Skip to content

Commit b98dd4d

Browse files
coadofacebook-github-bot
authored andcommitted
iOS: Add new RCTCustomBundleConfiguration for modifying bundle URL (facebook#54006)
Summary: ## Summary: Following the [RFC](react-native-community/discussions-and-proposals#933), this PR introduces a new `RCTBundleConfiguration` interface for modifying the bundle URL and exposes a new API for setting its instance in the `RCTReactNativeFactory`. The configuration object includes: - bundleFilePath - the URL of the bundle to load from the file system, - packagerServerScheme - the server scheme (e.g. http or https) to use when loading from the packager, - packagerServerHost - the server host (e.g. localhost) to use when loading from the packager. The `RCTBundleConfiguration` allows only for either `bundleFilePath` or `(packagerServerScheme, packagerServerHost)` to be set by defining appropriate initializers. The logic for creating bundle URL query items is extracted to a separate `createJSBundleURLQuery` method and is used by `RCTBundleManager` to set the configured `packagerServerHost` and `packagerServerScheme`. If the configuration is not defined, the `getBundleURL` method returns the result of the passed `fallbackURLProvider`. The `bundleFilePath` should be created with `[NSURL fileURLWithPath:<path>]`, as otherwise the HMR client is created and fails ungracefully. The check is added in the `getBundle` method to log the error beforehand: <img width="306" height="822" alt="Simulator Screenshot - iPhone 16 Pro - 2025-10-15 at 17 09 58" src="https://github.com/user-attachments/assets/869eed16-c5d8-4204-81d7-bd9cd42b2223" /> When the `bundleFilePath` is set in the `RCTBundleConfiguration` the `Connect to Metro...` message shouldn't be suggested. ## Changelog: [IOS][ADDED] - Add new `RCTBundleConfiguration` for modifying bundle URL on `RCTReactNativeFactory`. Test Plan: Test plan included in the last diff in the stack. Differential Revision: D84058022 Pulled By: coado
1 parent e81626e commit b98dd4d

File tree

4 files changed

+215
-11
lines changed

4 files changed

+215
-11
lines changed

packages/react-native/React/Base/RCTBundleManager.h

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,71 @@
99

1010
@class RCTBridge;
1111

12-
typedef NSURL * (^RCTBridgelessBundleURLGetter)(void);
13-
typedef void (^RCTBridgelessBundleURLSetter)(NSURL *bundleURL);
12+
typedef NSURL *_Nullable (^RCTBridgelessBundleURLGetter)(void);
13+
typedef void (^RCTBridgelessBundleURLSetter)(NSURL *_Nullable bundleURL);
14+
typedef NSMutableArray<NSURLQueryItem *> *_Nullable (^RCTPackagerOptionsUpdater)(
15+
NSMutableArray<NSURLQueryItem *> *_Nullable options);
16+
17+
/**
18+
* Configuration class for setting up custom bundle locations
19+
*/
20+
@interface RCTBundleConfiguration : NSObject
21+
22+
+ (nonnull instancetype)defaultConfiguration;
23+
24+
/**
25+
* The URL of the bundle to load from the file system
26+
*/
27+
@property (nonatomic, readonly, nullable) NSURL *bundleFilePath;
28+
29+
/**
30+
* The server scheme (e.g. http or https) to use when loading from the packager
31+
*/
32+
@property (nonatomic, readonly, nullable) NSString *packagerServerScheme;
33+
34+
/**
35+
* The server host (e.g. localhost) to use when loading from the packager
36+
*/
37+
@property (nonatomic, readonly, nullable) NSString *packagerServerHost;
38+
39+
/**
40+
* A block that modifies the packager options when loading from the packager
41+
*/
42+
@property (nonatomic, copy, nullable) RCTPackagerOptionsUpdater packagerOptionsUpdater;
43+
44+
/**
45+
* The relative path to the bundle.
46+
*/
47+
@property (nonatomic, readonly, nullable) NSString *bundlePath;
48+
49+
- (nonnull instancetype)initWithBundleFilePath:(nullable NSURL *)bundleFilePath;
50+
51+
- (nonnull instancetype)initWithPackagerServerScheme:(nullable NSString *)packagerServerScheme
52+
packagerServerHost:(nullable NSString *)packagerServerHost
53+
bundlePath:(nullable NSString *)bundlePath;
54+
55+
- (nullable NSURL *)getBundleURL:(NSURL *_Nullable (^_Nullable)(void))fallbackURLProvider;
56+
57+
- (nullable NSString *)getPackagerServerScheme;
58+
59+
- (nullable NSString *)getPackagerServerHost;
60+
61+
@end
1462

1563
/**
1664
* A class that allows NativeModules/TurboModules to read/write the bundleURL, with or without the bridge.
1765
*/
1866
@interface RCTBundleManager : NSObject
67+
68+
- (instancetype)initWithBundleConfig:(nullable RCTBundleConfiguration *)bundleConfig;
69+
1970
#ifndef RCT_REMOVE_LEGACY_ARCH
20-
- (void)setBridge:(RCTBridge *)bridge;
71+
- (void)setBridge:(nullable RCTBridge *)bridge;
2172
#endif // RCT_REMOVE_LEGACY_ARCH
22-
- (void)setBridgelessBundleURLGetter:(RCTBridgelessBundleURLGetter)getter
23-
andSetter:(RCTBridgelessBundleURLSetter)setter
24-
andDefaultGetter:(RCTBridgelessBundleURLGetter)defaultGetter;
73+
- (void)setBridgelessBundleURLGetter:(nullable RCTBridgelessBundleURLGetter)getter
74+
andSetter:(nullable RCTBridgelessBundleURLSetter)setter
75+
andDefaultGetter:(nullable RCTBridgelessBundleURLGetter)defaultGetter;
2576
- (void)resetBundleURL;
26-
@property NSURL *bundleURL;
77+
@property (nonatomic, nullable) NSURL *bundleURL;
78+
@property (nonatomic, nonnull) RCTBundleConfiguration *bundleConfig;
2779
@end

packages/react-native/React/Base/RCTBundleManager.m

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,99 @@
66
*/
77

88
#import "RCTBundleManager.h"
9+
#import <React/RCTBundleURLProvider.h>
910
#import "RCTAssert.h"
1011
#import "RCTBridge+Private.h"
1112
#import "RCTBridge.h"
13+
#import "RCTLog.h"
14+
15+
@implementation RCTBundleConfiguration
16+
17+
+ (instancetype)defaultConfiguration
18+
{
19+
return [[self alloc] initWithBundleFilePath:nil packagerServerScheme:nil packagerServerHost:nil bundlePath:nil];
20+
}
21+
22+
- (instancetype)initWithBundleFilePath:(NSURL *)bundleFilePath
23+
{
24+
return [self initWithBundleFilePath:bundleFilePath packagerServerScheme:nil packagerServerHost:nil bundlePath:nil];
25+
}
26+
27+
- (instancetype)initWithPackagerServerScheme:(NSString *)packagerServerScheme
28+
packagerServerHost:(NSString *)packagerServerHost
29+
bundlePath:(NSString *)bundlePath
30+
{
31+
return [self initWithBundleFilePath:nil
32+
packagerServerScheme:packagerServerScheme
33+
packagerServerHost:packagerServerHost
34+
bundlePath:bundlePath];
35+
}
36+
37+
- (instancetype)initWithBundleFilePath:(NSURL *)bundleFilePath
38+
packagerServerScheme:(NSString *)packagerServerScheme
39+
packagerServerHost:(NSString *)packagerServerHost
40+
bundlePath:(NSString *)bundlePath
41+
{
42+
if (self = [super init]) {
43+
_bundleFilePath = bundleFilePath;
44+
_packagerServerScheme = packagerServerScheme;
45+
_packagerServerHost = packagerServerHost;
46+
_bundlePath = bundlePath;
47+
_packagerOptionsUpdater = ^NSMutableArray<NSURLQueryItem *> *(NSMutableArray<NSURLQueryItem *> *options)
48+
{
49+
return options;
50+
};
51+
}
52+
53+
return self;
54+
}
55+
56+
- (NSString *)getPackagerServerScheme
57+
{
58+
if (!_packagerServerScheme) {
59+
return [[RCTBundleURLProvider sharedSettings] packagerScheme];
60+
}
61+
62+
return _packagerServerScheme;
63+
}
64+
65+
- (NSString *)getPackagerServerHost
66+
{
67+
if (!_packagerServerHost) {
68+
return [[RCTBundleURLProvider sharedSettings] packagerServerHostPort];
69+
}
70+
71+
return _packagerServerHost;
72+
}
73+
74+
- (NSURL *)getBundleURL:(NSURL * (^)(void))fallbackURLProvider
75+
{
76+
if (_packagerServerScheme && _packagerServerHost) {
77+
NSArray<NSURLQueryItem *> *jsBundleURLQuery =
78+
[[RCTBundleURLProvider sharedSettings] createJSBundleURLQuery:_packagerServerHost
79+
packagerScheme:_packagerServerScheme];
80+
81+
NSArray<NSURLQueryItem *> *updatedBundleURLQuery = _packagerOptionsUpdater((NSMutableArray *)jsBundleURLQuery);
82+
NSString *path = [NSString stringWithFormat:@"/%@.bundle", _bundlePath];
83+
return [[RCTBundleURLProvider class] resourceURLForResourcePath:path
84+
packagerHost:_packagerServerHost
85+
scheme:_packagerServerScheme
86+
queryItems:updatedBundleURLQuery];
87+
}
88+
89+
if (_bundleFilePath) {
90+
if (!_bundleFilePath.fileURL) {
91+
RCTLogError(@"Bundle file path must be a file URL");
92+
return nil;
93+
}
94+
95+
return _bundleFilePath;
96+
}
97+
98+
return fallbackURLProvider();
99+
}
100+
101+
@end
12102

13103
@implementation RCTBundleManager {
14104
#ifndef RCT_REMOVE_LEGACY_ARCH
@@ -19,6 +109,20 @@ @implementation RCTBundleManager {
19109
RCTBridgelessBundleURLGetter _bridgelessBundleURLDefaultGetter;
20110
}
21111

112+
- (instancetype)initWithBundleConfig:(RCTBundleConfiguration *)bundleConfig
113+
{
114+
if (self = [super init]) {
115+
self.bundleConfig = bundleConfig ? bundleConfig : [RCTBundleConfiguration defaultConfiguration];
116+
}
117+
118+
return self;
119+
}
120+
121+
- (instancetype)init
122+
{
123+
return [self initWithBundleConfig:[RCTBundleConfiguration defaultConfiguration]];
124+
}
125+
22126
#ifndef RCT_REMOVE_LEGACY_ARCH
23127
- (void)setBridge:(RCTBridge *)bridge
24128
{

packages/react-native/React/Base/RCTBundleURLProvider.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,19 @@ NS_ASSUME_NONNULL_BEGIN
9797
resourceExtension:(NSString *)extension
9898
offlineBundle:(NSBundle *)offlineBundle;
9999

100+
- (NSArray<NSURLQueryItem *> *)createJSBundleURLQuery:(NSString *)packagerHost
101+
packagerScheme:(NSString *__nullable)scheme;
102+
103+
+ (NSArray<NSURLQueryItem *> *)createJSBundleURLQuery:(NSString *)packagerHost
104+
packagerScheme:(NSString *__nullable)scheme
105+
enableDev:(BOOL)enableDev
106+
enableMinification:(BOOL)enableMinification
107+
inlineSourceMap:(BOOL)inlineSourceMap
108+
modulesOnly:(BOOL)modulesOnly
109+
runModule:(BOOL)runModule
110+
additionalOptions:
111+
(NSDictionary<NSString *, NSString *> *__nullable)additionalOptions;
112+
100113
/**
101114
* The IP address or hostname of the packager.
102115
*/

packages/react-native/React/Base/RCTBundleURLProvider.mm

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,44 @@ + (NSURL *__nullable)jsBundleURLForBundleRoot:(NSString *)bundleRoot
313313
additionalOptions:(NSDictionary<NSString *, NSString *> *__nullable)additionalOptions
314314
{
315315
NSString *path = [NSString stringWithFormat:@"/%@.bundle", bundleRoot];
316+
NSArray<NSURLQueryItem *> *queryItems = [self createJSBundleURLQuery:packagerHost
317+
packagerScheme:scheme
318+
enableDev:enableDev
319+
enableMinification:enableMinification
320+
inlineSourceMap:inlineSourceMap
321+
modulesOnly:modulesOnly
322+
runModule:runModule
323+
additionalOptions:additionalOptions];
324+
325+
return [[self class] resourceURLForResourcePath:path
326+
packagerHost:packagerHost
327+
scheme:scheme
328+
queryItems:[queryItems copy]];
329+
}
330+
331+
- (NSArray<NSURLQueryItem *> *)createJSBundleURLQuery:(NSString *)packagerHost
332+
packagerScheme:(NSString *__nullable)scheme
333+
{
334+
return [[self class] createJSBundleURLQuery:packagerHost
335+
packagerScheme:scheme
336+
enableDev:[self enableDev]
337+
enableMinification:[self enableMinification]
338+
inlineSourceMap:[self inlineSourceMap]
339+
modulesOnly:NO
340+
runModule:YES
341+
additionalOptions:nil];
342+
}
343+
344+
+ (NSArray<NSURLQueryItem *> *)createJSBundleURLQuery:(NSString *)packagerHost
345+
packagerScheme:(NSString *__nullable)scheme
346+
enableDev:(BOOL)enableDev
347+
enableMinification:(BOOL)enableMinification
348+
inlineSourceMap:(BOOL)inlineSourceMap
349+
modulesOnly:(BOOL)modulesOnly
350+
runModule:(BOOL)runModule
351+
additionalOptions:
352+
(NSDictionary<NSString *, NSString *> *__nullable)additionalOptions
353+
{
316354
BOOL lazy = enableDev;
317355
NSMutableArray<NSURLQueryItem *> *queryItems = [[NSMutableArray alloc] initWithArray:@[
318356
[[NSURLQueryItem alloc] initWithName:@"platform" value:RCTPlatformName],
@@ -345,10 +383,7 @@ + (NSURL *__nullable)jsBundleURLForBundleRoot:(NSString *)bundleRoot
345383
}
346384
}
347385

348-
return [[self class] resourceURLForResourcePath:path
349-
packagerHost:packagerHost
350-
scheme:scheme
351-
queryItems:[queryItems copy]];
386+
return queryItems;
352387
}
353388

354389
+ (NSURL *)resourceURLForResourcePath:(NSString *)path

0 commit comments

Comments
 (0)