Skip to content

Commit 18295d2

Browse files
coadofacebook-github-bot
authored andcommitted
iOS: Add new RCTCustomBundleConfiguration for modifying bundle URL (#54006)
Summary: Following the [RFC](react-native-community/discussions-and-proposals#933), this PR introduces a new `RCTCustomBundleConfiguration` 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. It associates `RCTPackagerConnection` (previously singleton) with the `RCTDevSettings` instance, which has access to the `RCTBundleManager`, which contains the specified configuration object. The connection is now established in the `RCTDevSettings initialize` method, called after the bundle manager is set by invoking the new `startWithBundleManager` method on the `RCTPackagerConnection`. The `RCTCustomBundleConfiguration` 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 as `[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. When the `bundleFilePath` is set in the `RCTCustomBundleConfiguration` the `Connect to Metro...` message shouldn't be suggested. ## Changelog: [IOS][ADDED] - Add new `RCTCustomBundleConfiguration` for modifying bundle URL on `RCTReactNativeFactory`. Test Plan: Tested changing `packagerServerHost` from the `AppDelegate` by re-creating the React Native instance with updated `RCTCustomBundleConfiguration`. I've run two Metro instances, each serving a different JS bundle (changed background) on `8081` and `8082` ports. The native `Restart RN:<current port>` button on top of the screen toggles between ports (used in bundle configuration) and re-creates connections. Tested with `RCT_DEV` set to true and false. https://github.com/user-attachments/assets/fd57068b-869c-4f45-93be-09d33f691cea <details> <summary>code:</summary> `AppDelegate.mm` ```objc #import "AppDelegate.h" #import <UserNotifications/UserNotifications.h> #import <React/RCTBundleManager.h> #import <React/RCTBundleURLProvider.h> #import <React/RCTDefines.h> #import <React/RCTLinkingManager.h> #import <ReactCommon/RCTSampleTurboModule.h> #import <ReactCommon/RCTTurboModuleManager.h> #import <React/RCTPushNotificationManager.h> #import <NativeCxxModuleExample/NativeCxxModuleExample.h> #ifndef RN_DISABLE_OSS_PLUGIN_HEADER #import <RNTMyNativeViewComponentView.h> #endif #if __has_include(<ReactAppDependencyProvider/RCTAppDependencyProvider.h>) #define USE_OSS_CODEGEN 1 #import <ReactAppDependencyProvider/RCTAppDependencyProvider.h> #else #define USE_OSS_CODEGEN 0 #endif static NSString *kBundlePath = @"js/RNTesterApp.ios"; interface AppDelegate () <UNUserNotificationCenterDelegate> end implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.launchOptions = launchOptions; self.port = @"8081"; #if USE_OSS_CODEGEN self.dependencyProvider = [RCTAppDependencyProvider new]; #endif self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; [self startReactNative]; [[UNUserNotificationCenter currentNotificationCenter] setDelegate:self]; return YES; } - (void)startReactNative { self.reactNativeFactory = [[RCTReactNativeFactory alloc] initWithDelegate:self]; NSString *packagerServerHost = [NSString stringWithFormat:@"localhost:%@", self.port]; RCTCustomBundleConfiguration *customBundleConfiguration = [[RCTCustomBundleConfiguration alloc] initWithPackagerServerScheme:@"http" packagerServerHost:packagerServerHost]; self.reactNativeFactory.customBundleConfiguration = customBundleConfiguration; [self.reactNativeFactory startReactNativeWithModuleName:@"RNTesterApp" inWindow:self.window initialProperties:[self prepareInitialProps] launchOptions:self.launchOptions]; [self createTopButton]; } - (void)createTopButton { NSString *title = [NSString stringWithFormat:@"Restart RN:%@", self.port]; self.topButton = [UIButton buttonWithType:UIButtonTypeSystem]; [self.topButton setTitle:title forState:UIControlStateNormal]; [self.topButton setBackgroundColor:[UIColor colorWithRed:0.0 green:0.5 blue:1.0 alpha:1]]; [self.topButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; CGFloat buttonWidth = 120; CGFloat buttonHeight = 44; CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width; self.topButton.frame = CGRectMake((screenWidth - buttonWidth) / 2, 50, buttonWidth, buttonHeight); self.topButton.layer.cornerRadius = 8; [self.topButton addTarget:self action:selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside]; [self.window addSubview:self.topButton]; [self.window bringSubviewToFront:self.topButton]; } - (void)togglePort { self.port = [self.port isEqual: @"8081"] ? @"8082" : @"8081"; } - (void)buttonTapped:(UIButton *)sender { self.reactNativeFactory = nil; [self togglePort]; [self startReactNative]; } - (NSDictionary *)prepareInitialProps { NSMutableDictionary *initProps = [NSMutableDictionary new]; NSString *_routeUri = [[NSUserDefaults standardUserDefaults] stringForKey:@"route"]; if (_routeUri) { initProps[@"exampleFromAppetizeParams"] = [NSString stringWithFormat:@"rntester://example/%Example", _routeUri]; } return initProps; } - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:kBundlePath]; } - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options { return [RCTLinkingManager application:app openURL:url options:options]; } - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker { if (name == facebook::react::NativeCxxModuleExample::kModuleName) { return std::make_shared<facebook::react::NativeCxxModuleExample>(jsInvoker); } return [super getTurboModule:name jsInvoker:jsInvoker]; } // Required for the remoteNotificationsRegistered event. - (void)application:(__unused UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { [RCTPushNotificationManager didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; } // Required for the remoteNotificationRegistrationError event. - (void)application:(__unused UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { [RCTPushNotificationManager didFailToRegisterForRemoteNotificationsWithError:error]; } #pragma mark - UNUserNotificationCenterDelegate // Required for the remoteNotificationReceived and localNotificationReceived events - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { [RCTPushNotificationManager didReceiveNotification:notification]; completionHandler(UNNotificationPresentationOptionNone); } // Required for the remoteNotificationReceived and localNotificationReceived events // Called when a notification is tapped from background. (Foreground notification will not be shown per // the presentation option selected above). - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler { UNNotification *notification = response.notification; // This condition will be true if tapping the notification launched the app. if ([response.actionIdentifier isEqualToString:UNNotificationDefaultActionIdentifier]) { // This can be retrieved with getInitialNotification. [RCTPushNotificationManager setInitialNotification:notification]; } [RCTPushNotificationManager didReceiveNotification:notification]; completionHandler(); } #pragma mark - New Arch Enabled settings - (BOOL)bridgelessEnabled { return YES; } #pragma mark - RCTComponentViewFactoryComponentProvider #ifndef RN_DISABLE_OSS_PLUGIN_HEADER - (nonnull NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents { NSMutableDictionary *dict = [super thirdPartyFabricComponents].mutableCopy; if (!dict[@"RNTMyNativeView"]) { dict[@"RNTMyNativeView"] = NSClassFromString(@"RNTMyNativeViewComponentView"); } if (!dict[@"SampleNativeComponent"]) { dict[@"SampleNativeComponent"] = NSClassFromString(@"RCTSampleNativeComponentComponentView"); } return dict; } #endif - (NSURL *)bundleURL { return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:kBundlePath]; } end ``` `AppDelegate.h` ```objc #import <RCTDefaultReactNativeFactoryDelegate.h> #import <RCTReactNativeFactory.h> #import <UIKit/UIKit.h> interface AppDelegate : RCTDefaultReactNativeFactoryDelegate <UIApplicationDelegate> property (nonatomic, strong, nonnull) UIWindow *window; property (nonatomic, strong, nonnull) RCTReactNativeFactory *reactNativeFactory; property (nonatomic, strong, nullable) UIButton *topButton; property (nonatomic, strong) NSDictionary *launchOptions; property (nonatomic, assign) NSString *port; end ``` </details> Differential Revision: D84058022 Pulled By: coado
1 parent 269b0bd commit 18295d2

File tree

16 files changed

+300
-70
lines changed

16 files changed

+300
-70
lines changed

packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
@class RCTBridge;
2525
@protocol RCTComponentViewProtocol;
2626
@class RCTSurfacePresenterBridgeAdapter;
27+
@class RCTCustomBundleConfiguration;
2728
@class RCTDevMenuConfiguration;
2829

2930
NS_ASSUME_NONNULL_BEGIN
@@ -117,6 +118,8 @@ typedef NS_ENUM(NSInteger, RCTReleaseLevel) { Canary, Experimental, Stable };
117118

118119
@property (nonatomic, weak) id<RCTReactNativeFactoryDelegate> delegate;
119120

121+
@property (nonatomic, nullable) RCTCustomBundleConfiguration *customBundleConfiguration;
122+
120123
@property (nonatomic, nullable) RCTDevMenuConfiguration *devMenuConfiguration;
121124

122125
@end

packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm

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

88
#import "RCTReactNativeFactory.h"
9+
#import <React/RCTBundleManager.h>
910
#import <React/RCTColorSpaceUtils.h>
1011
#import <React/RCTDevMenu.h>
1112
#import <React/RCTLog.h>
@@ -59,6 +60,8 @@ - (instancetype)initWithDelegate:(id<RCTReactNativeFactoryDelegate>)delegate rel
5960
self.rootViewFactory = [self createRCTRootViewFactory];
6061

6162
[RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self;
63+
64+
self.customBundleConfiguration = [[RCTCustomBundleConfiguration alloc] init];
6265
}
6366

6467
return self;
@@ -84,6 +87,7 @@ - (void)startReactNativeWithModuleName:(NSString *)moduleName
8487
UIView *rootView = [self.rootViewFactory viewWithModuleName:moduleName
8588
initialProperties:initialProperties
8689
launchOptions:launchOptions
90+
customBundleConfiguration:self.customBundleConfiguration
8791
devMenuConfiguration:self.devMenuConfiguration];
8892
UIViewController *rootViewController = [_delegate createRootViewController];
8993
[_delegate setRootView:rootView toRootViewController:rootViewController];

packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
@class RCTHost;
1919
@class RCTRootView;
2020
@class RCTSurfacePresenterBridgeAdapter;
21+
@class RCTCustomBundleConfiguration;
2122
@class RCTDevMenuConfiguration;
2223

2324
NS_ASSUME_NONNULL_BEGIN
@@ -202,11 +203,13 @@ typedef void (^RCTLoadSourceForBridgeBlock)(RCTBridge *bridge, RCTSourceLoadBloc
202203
* @parameter: moduleName - the name of the app, used by Metro to resolve the module.
203204
* @parameter: initialProperties - a set of initial properties.
204205
* @parameter: launchOptions - a dictionary with a set of options.
206+
* @parameter: customBundleConfiguration - a configuration for custom bundle source URL.
205207
* @parameter: devMenuConfiguration - a configuration for enabling/disabling dev menu.
206208
*/
207209
- (UIView *_Nonnull)viewWithModuleName:(NSString *)moduleName
208210
initialProperties:(NSDictionary *__nullable)initialProperties
209211
launchOptions:(NSDictionary *__nullable)launchOptions
212+
customBundleConfiguration:(RCTCustomBundleConfiguration *__nullable)customBundleConfiguration
210213
devMenuConfiguration:(RCTDevMenuConfiguration *__nullable)devMenuConfiguration;
211214

212215
- (UIView *_Nonnull)viewWithModuleName:(NSString *)moduleName
@@ -226,15 +229,18 @@ typedef void (^RCTLoadSourceForBridgeBlock)(RCTBridge *bridge, RCTSourceLoadBloc
226229
* Use it to speed up later viewWithModuleName: calls.
227230
*
228231
* @parameter: launchOptions - a dictionary with a set of options.
232+
* @parameter: customBundleConfiguration - a configuration for custom bundle source URL.
229233
* @parameter: devMenuConfiguration - a configuration for enabling/disabling dev menu.
230234
*/
231235
- (void)initializeReactHostWithLaunchOptions:(NSDictionary *__nullable)launchOptions
236+
customBundleConfiguration:(RCTCustomBundleConfiguration *__nullable)customBundleConfiguration
232237
devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration;
233238

234-
- (RCTHost *)createReactHost:(NSDictionary *__nullable)launchOptions;
235-
236239
- (RCTHost *)createReactHost:(NSDictionary *__nullable)launchOptions
237-
devMenuConfiguration:(RCTDevMenuConfiguration *__nullable)devMenuConfiguration;
240+
customBundleConfiguration:(RCTCustomBundleConfiguration *__nullable)customBundleConfiguration
241+
devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration;
242+
243+
- (RCTHost *)createReactHost:(NSDictionary *__nullable)launchOptions;
238244

239245
@end
240246

packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
#else
2222
#import <React/CoreModulesPlugins.h>
2323
#endif
24-
#import <React/RCTBundleURLProvider.h>
2524
#import <React/RCTComponentViewFactory.h>
2625
#import <React/RCTComponentViewProtocol.h>
2726
#import <React/RCTFabricSurface.h>
@@ -137,6 +136,7 @@ - (UIView *)viewWithModuleName:(NSString *)moduleName initialProperties:(NSDicti
137136
return [self viewWithModuleName:moduleName
138137
initialProperties:initialProperties
139138
launchOptions:nil
139+
customBundleConfiguration:nil
140140
devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]];
141141
}
142142

@@ -145,17 +145,21 @@ - (UIView *)viewWithModuleName:(NSString *)moduleName
145145
return [self viewWithModuleName:moduleName
146146
initialProperties:nil
147147
launchOptions:nil
148+
customBundleConfiguration:nil
148149
devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]];
149150
}
150151

151152
- (void)initializeReactHostWithLaunchOptions:(NSDictionary *)launchOptions
153+
customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration
152154
devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration
153155
{
154156
// Enable TurboModule interop by default in Bridgeless mode
155157
RCTEnableTurboModuleInterop(YES);
156158
RCTEnableTurboModuleInteropBridgeProxy(YES);
157159

158-
[self createReactHostIfNeeded:launchOptions devMenuConfiguration:devMenuConfiguration];
160+
[self createReactHostIfNeeded:launchOptions
161+
customBundleConfiguration:customBundleConfiguration
162+
devMenuConfiguration:devMenuConfiguration];
159163
return;
160164
}
161165

@@ -166,15 +170,19 @@ - (UIView *)viewWithModuleName:(NSString *)moduleName
166170
return [self viewWithModuleName:moduleName
167171
initialProperties:initialProperties
168172
launchOptions:launchOptions
173+
customBundleConfiguration:nil
169174
devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]];
170175
}
171176

172177
- (UIView *)viewWithModuleName:(NSString *)moduleName
173178
initialProperties:(NSDictionary *)initProps
174179
launchOptions:(NSDictionary *)launchOptions
180+
customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration
175181
devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration
176182
{
177-
[self initializeReactHostWithLaunchOptions:launchOptions devMenuConfiguration:devMenuConfiguration];
183+
[self initializeReactHostWithLaunchOptions:launchOptions
184+
customBundleConfiguration:customBundleConfiguration
185+
devMenuConfiguration:devMenuConfiguration];
178186

179187
RCTFabricSurface *surface = [self.reactHost createSurfaceWithModuleName:moduleName
180188
initialProperties:initProps ? initProps : @{}];
@@ -245,21 +253,28 @@ - (void)createBridgeAdapterIfNeeded
245253
#pragma mark - New Arch Utilities
246254

247255
- (void)createReactHostIfNeeded:(NSDictionary *)launchOptions
256+
customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration
248257
devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration
249258
{
250259
if (self.reactHost) {
251260
return;
252261
}
253-
self.reactHost = [self createReactHost:launchOptions devMenuConfiguration:devMenuConfiguration];
262+
263+
self.reactHost = [self createReactHost:launchOptions
264+
customBundleConfiguration:customBundleConfiguration
265+
devMenuConfiguration:devMenuConfiguration];
254266
}
255267

256268
- (RCTHost *)createReactHost:(NSDictionary *)launchOptions
257269
{
258-
return [self createReactHost:launchOptions devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]];
270+
return [self createReactHost:launchOptions
271+
customBundleConfiguration:nil
272+
devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]];
259273
}
260274

261275
- (RCTHost *)createReactHost:(NSDictionary *)launchOptions
262-
devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration
276+
customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration
277+
devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration
263278
{
264279
__weak __typeof(self) weakSelf = self;
265280
RCTHost *reactHost =
@@ -270,6 +285,7 @@ - (RCTHost *)createReactHost:(NSDictionary *)launchOptions
270285
return [weakSelf createJSRuntimeFactory];
271286
}
272287
launchOptions:launchOptions
288+
customBundleConfiguration:customBundleConfiguration
273289
devMenuConfiguration:devMenuConfiguration];
274290
[reactHost setBundleURLProvider:^NSURL *() {
275291
return [weakSelf bundleURL];

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

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,59 @@
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+
15+
/**
16+
* Configuration class for setting up custom bundle locations
17+
*/
18+
@interface RCTCustomBundleConfiguration : NSObject
19+
20+
/**
21+
* The URL of the bundle to load from the file system
22+
*/
23+
@property (nonatomic, readonly, nullable) NSURL *bundleFilePath;
24+
25+
/**
26+
* The server scheme (e.g. http or https) to use when loading from the packager
27+
*/
28+
@property (nonatomic, readonly, nullable) NSString *packagerServerScheme;
29+
30+
/**
31+
* The server host (e.g. localhost) to use when loading from the packager
32+
*/
33+
@property (nonatomic, readonly, nullable) NSString *packagerServerHost;
34+
35+
/**
36+
* The relative path to the bundle.
37+
*/
38+
@property (nonatomic, readonly, nullable) NSString *bundlePath;
39+
40+
- (nonnull instancetype)initWithBundleFilePath:(nullable NSURL *)bundleFilePath;
41+
42+
- (nonnull instancetype)initWithPackagerServerScheme:(nullable NSString *)packagerServerScheme
43+
packagerServerHost:(nullable NSString *)packagerServerHost
44+
bundlePath:(nullable NSString *)bundlePath;
45+
46+
- (nullable NSURL *)getBundleURL:(NSURL *_Nullable (^_Nullable)(void))fallbackURLProvider;
47+
48+
- (nullable NSString *)getPackagerServerScheme;
49+
50+
- (nullable NSString *)getPackagerServerHost;
51+
52+
@end
1453

1554
/**
1655
* A class that allows NativeModules/TurboModules to read/write the bundleURL, with or without the bridge.
1756
*/
1857
@interface RCTBundleManager : NSObject
1958
#ifndef RCT_REMOVE_LEGACY_ARCH
20-
- (void)setBridge:(RCTBridge *)bridge;
59+
- (void)setBridge:(nullable RCTBridge *)bridge;
2160
#endif // RCT_REMOVE_LEGACY_ARCH
22-
- (void)setBridgelessBundleURLGetter:(RCTBridgelessBundleURLGetter)getter
23-
andSetter:(RCTBridgelessBundleURLSetter)setter
24-
andDefaultGetter:(RCTBridgelessBundleURLGetter)defaultGetter;
61+
- (void)setBridgelessBundleURLGetter:(nullable RCTBridgelessBundleURLGetter)getter
62+
andSetter:(nullable RCTBridgelessBundleURLSetter)setter
63+
andDefaultGetter:(nullable RCTBridgelessBundleURLGetter)defaultGetter;
2564
- (void)resetBundleURL;
26-
@property NSURL *bundleURL;
65+
@property (nonatomic, nullable) NSURL *bundleURL;
66+
@property (nonatomic, nullable) RCTCustomBundleConfiguration *customBundleConfig;
2767
@end

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

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,81 @@
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 RCTCustomBundleConfiguration
16+
17+
- (instancetype)initWithBundleFilePath:(NSURL *)bundleFilePath
18+
{
19+
if (self = [super init]) {
20+
_bundleFilePath = bundleFilePath;
21+
}
22+
23+
return self;
24+
}
25+
26+
- (instancetype)initWithPackagerServerScheme:(NSString *)packagerServerScheme
27+
packagerServerHost:(NSString *)packagerServerHost
28+
bundlePath:(NSString *)bundlePath
29+
{
30+
if (self = [super init]) {
31+
_packagerServerScheme = packagerServerScheme;
32+
_packagerServerHost = packagerServerHost;
33+
_bundlePath = bundlePath;
34+
}
35+
36+
return self;
37+
}
38+
39+
- (NSString *)getPackagerServerScheme
40+
{
41+
if (!_packagerServerScheme) {
42+
return [[RCTBundleURLProvider sharedSettings] packagerScheme];
43+
}
44+
45+
return _packagerServerScheme;
46+
}
47+
48+
- (NSString *)getPackagerServerHost
49+
{
50+
if (!_packagerServerHost) {
51+
return [[RCTBundleURLProvider sharedSettings] packagerServerHostPort];
52+
}
53+
54+
return _packagerServerHost;
55+
}
56+
57+
- (NSURL *)getBundleURL:(NSURL * (^)(void))fallbackURLProvider
58+
{
59+
if (_packagerServerScheme && _packagerServerHost) {
60+
NSArray<NSURLQueryItem *> *jsBundleURLQuery =
61+
[[RCTBundleURLProvider sharedSettings] createJSBundleURLQuery:_packagerServerHost
62+
packagerScheme:_packagerServerScheme];
63+
64+
NSString *path = [NSString stringWithFormat:@"/%@.bundle", _bundlePath];
65+
return [[RCTBundleURLProvider class] resourceURLForResourcePath:path
66+
packagerHost:_packagerServerHost
67+
scheme:_packagerServerScheme
68+
queryItems:jsBundleURLQuery];
69+
}
70+
71+
if (_bundleFilePath) {
72+
if (!_bundleFilePath.fileURL) {
73+
RCTLogError(@"Bundle file path must be a file URL");
74+
return nil;
75+
}
76+
77+
return _bundleFilePath;
78+
}
79+
80+
return fallbackURLProvider();
81+
}
82+
83+
@end
1284

1385
@implementation RCTBundleManager {
1486
#ifndef RCT_REMOVE_LEGACY_ARCH
@@ -18,6 +90,7 @@ @implementation RCTBundleManager {
1890
RCTBridgelessBundleURLSetter _bridgelessBundleURLSetter;
1991
RCTBridgelessBundleURLGetter _bridgelessBundleURLDefaultGetter;
2092
}
93+
@synthesize customBundleConfig;
2194

2295
#ifndef RCT_REMOVE_LEGACY_ARCH
2396
- (void)setBridge:(RCTBridge *)bridge

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
*/

0 commit comments

Comments
 (0)