Commit 8da150a
iOS: Add new
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: coadoRCTCustomBundleConfiguration for modifying bundle URL (facebook#54006)1 parent 4c4270d commit 8da150a
File tree
16 files changed
+293
-70
lines changed- packages
- react-native
- Libraries/AppDelegate
- ReactCommon/react/runtime/platform/ios/ReactCommon
- React
- Base
- CoreModules
- DevSupport
- rn-tester/RNTester
16 files changed
+293
-70
lines changedLines changed: 3 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| 27 | + | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
| |||
117 | 118 | | |
118 | 119 | | |
119 | 120 | | |
| 121 | + | |
| 122 | + | |
120 | 123 | | |
121 | 124 | | |
122 | 125 | | |
| |||
Lines changed: 4 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| 9 | + | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
| |||
59 | 60 | | |
60 | 61 | | |
61 | 62 | | |
| 63 | + | |
| 64 | + | |
62 | 65 | | |
63 | 66 | | |
64 | 67 | | |
| |||
84 | 87 | | |
85 | 88 | | |
86 | 89 | | |
| 90 | + | |
87 | 91 | | |
88 | 92 | | |
89 | 93 | | |
| |||
Lines changed: 9 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| 21 | + | |
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
| |||
202 | 203 | | |
203 | 204 | | |
204 | 205 | | |
| 206 | + | |
205 | 207 | | |
206 | 208 | | |
207 | 209 | | |
208 | 210 | | |
209 | 211 | | |
| 212 | + | |
210 | 213 | | |
211 | 214 | | |
212 | 215 | | |
| |||
226 | 229 | | |
227 | 230 | | |
228 | 231 | | |
| 232 | + | |
229 | 233 | | |
230 | 234 | | |
231 | 235 | | |
| 236 | + | |
232 | 237 | | |
233 | 238 | | |
234 | | - | |
235 | | - | |
236 | 239 | | |
237 | | - | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
238 | 244 | | |
239 | 245 | | |
240 | 246 | | |
| |||
Lines changed: 22 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
24 | | - | |
25 | 24 | | |
26 | 25 | | |
27 | 26 | | |
| |||
137 | 136 | | |
138 | 137 | | |
139 | 138 | | |
| 139 | + | |
140 | 140 | | |
141 | 141 | | |
142 | 142 | | |
| |||
145 | 145 | | |
146 | 146 | | |
147 | 147 | | |
| 148 | + | |
148 | 149 | | |
149 | 150 | | |
150 | 151 | | |
151 | 152 | | |
| 153 | + | |
152 | 154 | | |
153 | 155 | | |
154 | 156 | | |
155 | 157 | | |
156 | 158 | | |
157 | 159 | | |
158 | | - | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
159 | 163 | | |
160 | 164 | | |
161 | 165 | | |
| |||
166 | 170 | | |
167 | 171 | | |
168 | 172 | | |
| 173 | + | |
169 | 174 | | |
170 | 175 | | |
171 | 176 | | |
172 | 177 | | |
173 | 178 | | |
174 | 179 | | |
| 180 | + | |
175 | 181 | | |
176 | 182 | | |
177 | | - | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
178 | 186 | | |
179 | 187 | | |
180 | 188 | | |
| |||
245 | 253 | | |
246 | 254 | | |
247 | 255 | | |
| 256 | + | |
248 | 257 | | |
249 | 258 | | |
250 | 259 | | |
251 | 260 | | |
252 | 261 | | |
253 | | - | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
254 | 266 | | |
255 | 267 | | |
256 | 268 | | |
257 | 269 | | |
258 | | - | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
259 | 273 | | |
260 | 274 | | |
261 | 275 | | |
262 | | - | |
| 276 | + | |
| 277 | + | |
263 | 278 | | |
264 | 279 | | |
265 | 280 | | |
| |||
270 | 285 | | |
271 | 286 | | |
272 | 287 | | |
| 288 | + | |
273 | 289 | | |
274 | 290 | | |
275 | 291 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
13 | | - | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
14 | 53 | | |
15 | 54 | | |
16 | 55 | | |
17 | 56 | | |
18 | 57 | | |
19 | 58 | | |
20 | | - | |
| 59 | + | |
21 | 60 | | |
22 | | - | |
23 | | - | |
24 | | - | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
25 | 64 | | |
26 | | - | |
| 65 | + | |
| 66 | + | |
27 | 67 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| 9 | + | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
12 | 84 | | |
13 | 85 | | |
14 | 86 | | |
| |||
18 | 90 | | |
19 | 91 | | |
20 | 92 | | |
| 93 | + | |
21 | 94 | | |
22 | 95 | | |
23 | 96 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
97 | 97 | | |
98 | 98 | | |
99 | 99 | | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
100 | 113 | | |
101 | 114 | | |
102 | 115 | | |
| |||
0 commit comments