Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
@class RCTBridge;
@protocol RCTComponentViewProtocol;
@class RCTSurfacePresenterBridgeAdapter;
@class RCTCustomBundleConfiguration;
@class RCTDevMenuConfiguration;

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

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

@property (nonatomic, nullable) RCTCustomBundleConfiguration *customBundleConfiguration;

@property (nonatomic, nullable) RCTDevMenuConfiguration *devMenuConfiguration;

@end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

#import "RCTReactNativeFactory.h"
#import <React/RCTBundleManager.h>
#import <React/RCTColorSpaceUtils.h>
#import <React/RCTDevMenu.h>
#import <React/RCTLog.h>
Expand Down Expand Up @@ -59,6 +60,8 @@ - (instancetype)initWithDelegate:(id<RCTReactNativeFactoryDelegate>)delegate rel
self.rootViewFactory = [self createRCTRootViewFactory];

[RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self;

self.customBundleConfiguration = [[RCTCustomBundleConfiguration alloc] init];
}

return self;
Expand All @@ -84,6 +87,7 @@ - (void)startReactNativeWithModuleName:(NSString *)moduleName
UIView *rootView = [self.rootViewFactory viewWithModuleName:moduleName
initialProperties:initialProperties
launchOptions:launchOptions
customBundleConfiguration:self.customBundleConfiguration
devMenuConfiguration:self.devMenuConfiguration];
UIViewController *rootViewController = [_delegate createRootViewController];
[_delegate setRootView:rootView toRootViewController:rootViewController];
Expand Down
12 changes: 9 additions & 3 deletions packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
@class RCTHost;
@class RCTRootView;
@class RCTSurfacePresenterBridgeAdapter;
@class RCTCustomBundleConfiguration;
@class RCTDevMenuConfiguration;

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

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

- (RCTHost *)createReactHost:(NSDictionary *__nullable)launchOptions;

- (RCTHost *)createReactHost:(NSDictionary *__nullable)launchOptions
devMenuConfiguration:(RCTDevMenuConfiguration *__nullable)devMenuConfiguration;
customBundleConfiguration:(RCTCustomBundleConfiguration *__nullable)customBundleConfiguration
devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration;

- (RCTHost *)createReactHost:(NSDictionary *__nullable)launchOptions;

@end

Expand Down
28 changes: 22 additions & 6 deletions packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
#else
#import <React/CoreModulesPlugins.h>
#endif
#import <React/RCTBundleURLProvider.h>
#import <React/RCTComponentViewFactory.h>
#import <React/RCTComponentViewProtocol.h>
#import <React/RCTFabricSurface.h>
Expand Down Expand Up @@ -137,6 +136,7 @@ - (UIView *)viewWithModuleName:(NSString *)moduleName initialProperties:(NSDicti
return [self viewWithModuleName:moduleName
initialProperties:initialProperties
launchOptions:nil
customBundleConfiguration:nil
devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]];
}

Expand All @@ -145,17 +145,21 @@ - (UIView *)viewWithModuleName:(NSString *)moduleName
return [self viewWithModuleName:moduleName
initialProperties:nil
launchOptions:nil
customBundleConfiguration:nil
devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]];
}

- (void)initializeReactHostWithLaunchOptions:(NSDictionary *)launchOptions
customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration
devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration
{
// Enable TurboModule interop by default in Bridgeless mode
RCTEnableTurboModuleInterop(YES);
RCTEnableTurboModuleInteropBridgeProxy(YES);

[self createReactHostIfNeeded:launchOptions devMenuConfiguration:devMenuConfiguration];
[self createReactHostIfNeeded:launchOptions
customBundleConfiguration:customBundleConfiguration
devMenuConfiguration:devMenuConfiguration];
return;
}

Expand All @@ -166,15 +170,19 @@ - (UIView *)viewWithModuleName:(NSString *)moduleName
return [self viewWithModuleName:moduleName
initialProperties:initialProperties
launchOptions:launchOptions
customBundleConfiguration:nil
devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]];
}

- (UIView *)viewWithModuleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initProps
launchOptions:(NSDictionary *)launchOptions
customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration
devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration
{
[self initializeReactHostWithLaunchOptions:launchOptions devMenuConfiguration:devMenuConfiguration];
[self initializeReactHostWithLaunchOptions:launchOptions
customBundleConfiguration:customBundleConfiguration
devMenuConfiguration:devMenuConfiguration];

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

- (void)createReactHostIfNeeded:(NSDictionary *)launchOptions
customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration
devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration
{
if (self.reactHost) {
return;
}
self.reactHost = [self createReactHost:launchOptions devMenuConfiguration:devMenuConfiguration];

self.reactHost = [self createReactHost:launchOptions
customBundleConfiguration:customBundleConfiguration
devMenuConfiguration:devMenuConfiguration];
}

- (RCTHost *)createReactHost:(NSDictionary *)launchOptions
{
return [self createReactHost:launchOptions devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]];
return [self createReactHost:launchOptions
customBundleConfiguration:nil
devMenuConfiguration:[RCTDevMenuConfiguration defaultConfiguration]];
}

- (RCTHost *)createReactHost:(NSDictionary *)launchOptions
devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration
customBundleConfiguration:(RCTCustomBundleConfiguration *)customBundleConfiguration
devMenuConfiguration:(RCTDevMenuConfiguration *)devMenuConfiguration
{
__weak __typeof(self) weakSelf = self;
RCTHost *reactHost =
Expand All @@ -270,6 +285,7 @@ - (RCTHost *)createReactHost:(NSDictionary *)launchOptions
return [weakSelf createJSRuntimeFactory];
}
launchOptions:launchOptions
customBundleConfiguration:customBundleConfiguration
devMenuConfiguration:devMenuConfiguration];
[reactHost setBundleURLProvider:^NSURL *() {
return [weakSelf bundleURL];
Expand Down
40 changes: 40 additions & 0 deletions packages/react-native/React/Base/RCTBundleManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,45 @@
typedef NSURL * (^RCTBridgelessBundleURLGetter)(void);
typedef void (^RCTBridgelessBundleURLSetter)(NSURL *bundleURL);

/**
* Configuration class for setting up custom bundle locations
*/
@interface RCTCustomBundleConfiguration : NSObject

/**
* The URL of the bundle to load from the file system
*/
@property (nonatomic, readonly, nullable) NSURL *bundleFilePath;

/**
* The server scheme (e.g. http or https) to use when loading from the packager
*/
@property (nonatomic, readonly, nullable) NSString *packagerServerScheme;

/**
* The server host (e.g. localhost) to use when loading from the packager
*/
@property (nonatomic, readonly, nullable) NSString *packagerServerHost;

/**
* The relative path to the bundle.
*/
@property (nonatomic, readonly, nullable) NSString *bundlePath;

- (instancetype)initWithBundleFilePath:(NSURL *)bundleFilePath;

- (instancetype)initWithPackagerServerScheme:(NSString *)packagerServerScheme
packagerServerHost:(NSString *)packagerServerHost
bundlePath:(NSString *)bundlePath;

- (NSURL *)getBundleURL:(NSURL *__nullable (^)(void))fallbackURLProvider;

- (NSString *)getPackagerServerScheme;

- (NSString *)getPackagerServerHost;

@end

/**
* A class that allows NativeModules/TurboModules to read/write the bundleURL, with or without the bridge.
*/
Expand All @@ -24,4 +63,5 @@ typedef void (^RCTBridgelessBundleURLSetter)(NSURL *bundleURL);
andDefaultGetter:(RCTBridgelessBundleURLGetter)defaultGetter;
- (void)resetBundleURL;
@property NSURL *bundleURL;
@property (nonatomic, nullable) RCTCustomBundleConfiguration *customBundleConfig;
@end
73 changes: 73 additions & 0 deletions packages/react-native/React/Base/RCTBundleManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,81 @@
*/

#import "RCTBundleManager.h"
#import <React/RCTBundleURLProvider.h>
#import "RCTAssert.h"
#import "RCTBridge+Private.h"
#import "RCTBridge.h"
#import "RCTLog.h"

@implementation RCTCustomBundleConfiguration

- (instancetype)initWithBundleFilePath:(NSURL *)bundleFilePath
{
if (self = [super init]) {
_bundleFilePath = bundleFilePath;
}

return self;
}

- (instancetype)initWithPackagerServerScheme:(NSString *)packagerServerScheme
packagerServerHost:(NSString *)packagerServerHost
bundlePath:(NSString *)bundlePath
{
if (self = [super init]) {
_packagerServerScheme = packagerServerScheme;
_packagerServerHost = packagerServerHost;
_bundlePath = bundlePath;
}

return self;
}

- (NSString *)getPackagerServerScheme
{
if (!_packagerServerScheme) {
return [[RCTBundleURLProvider sharedSettings] packagerScheme];
}

return _packagerServerScheme;
}

- (NSString *)getPackagerServerHost
{
if (!_packagerServerHost) {
return [[RCTBundleURLProvider sharedSettings] packagerServerHostPort];
}

return _packagerServerHost;
}

- (NSURL *)getBundleURL:(NSURL * (^)(void))fallbackURLProvider
{
if (_packagerServerScheme && _packagerServerHost) {
NSArray<NSURLQueryItem *> *jsBundleURLQuery =
[[RCTBundleURLProvider sharedSettings] createJSBundleURLQuery:_packagerServerHost
packagerScheme:_packagerServerScheme];

NSString *path = [NSString stringWithFormat:@"/%@.bundle", _bundlePath];
return [[RCTBundleURLProvider class] resourceURLForResourcePath:path
packagerHost:_packagerServerHost
scheme:_packagerServerScheme
queryItems:jsBundleURLQuery];
}

if (_bundleFilePath) {
if (!_bundleFilePath.fileURL) {
RCTLogError(@"Bundle file path must be a file URL");
return nil;
}

return _bundleFilePath;
}

return fallbackURLProvider();
}

@end

@implementation RCTBundleManager {
#ifndef RCT_REMOVE_LEGACY_ARCH
Expand All @@ -18,6 +90,7 @@ @implementation RCTBundleManager {
RCTBridgelessBundleURLSetter _bridgelessBundleURLSetter;
RCTBridgelessBundleURLGetter _bridgelessBundleURLDefaultGetter;
}
@synthesize customBundleConfig;

#ifndef RCT_REMOVE_LEGACY_ARCH
- (void)setBridge:(RCTBridge *)bridge
Expand Down
13 changes: 13 additions & 0 deletions packages/react-native/React/Base/RCTBundleURLProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,19 @@ NS_ASSUME_NONNULL_BEGIN
resourceExtension:(NSString *)extension
offlineBundle:(NSBundle *)offlineBundle;

- (NSArray<NSURLQueryItem *> *)createJSBundleURLQuery:(NSString *)packagerHost
packagerScheme:(NSString *__nullable)scheme;

+ (NSArray<NSURLQueryItem *> *)createJSBundleURLQuery:(NSString *)packagerHost
packagerScheme:(NSString *__nullable)scheme
enableDev:(BOOL)enableDev
enableMinification:(BOOL)enableMinification
inlineSourceMap:(BOOL)inlineSourceMap
modulesOnly:(BOOL)modulesOnly
runModule:(BOOL)runModule
additionalOptions:(NSDictionary<NSString *, NSString *>
*__nullable)additionalOptions;

/**
* The IP address or hostname of the packager.
*/
Expand Down
Loading
Loading