expo-dev-launcher 6.1.0-canary-20251015-a6a1272 → 6.1.0-canary-20251031-b135dff

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,18 +7,35 @@
7
7
  ### 🎉 New features
8
8
 
9
9
  - Remove `ExpoAppDelegate` inheritance requirement ([#39417](https://github.com/expo/expo/pull/39417) by [@gabrieldonadel](https://github.com/gabrieldonadel))
10
+ - [iOS] Add brownfield support ([#40463](https://github.com/expo/expo/pull/40463) by [@gabrieldonadel](https://github.com/gabrieldonadel))
11
+ - [iOS] Decouple menu from dev-launcher ([#40669](https://github.com/expo/expo/pull/40669) by [@alanjhughes](https://github.com/alanjhughes))
10
12
 
11
13
  ### 🐛 Bug fixes
12
14
 
13
- - [iOS] Fixes updates not being viewable on first launch. ([#40324](https://github.com/expo/expo/pull/40324) by [@alanjhughes](https://github.com/alanjhughes))
15
+ - [Android] Fix launching apps in brownfield ([#40711](https://github.com/expo/expo/pull/40711) by [@gabrieldonadel](https://github.com/gabrieldonadel))
14
16
 
15
17
  ### 💡 Others
16
18
 
17
19
  - [Android] Migrated from `kotlinOptions` to `compilerOptions` DSL. ([#39794](https://github.com/expo/expo/pull/39794) by [@huextrat](https://github.com/huextrat))
18
20
  - [android] Make reactNativeHost optional in ReactHostWrapper ([#40085](https://github.com/expo/expo/pull/40085) by [@gabrieldonadel](https://github.com/gabrieldonadel))
19
- - [Android] Prevented the app from crashing during the initialization of the `ErrorViewModel`. ([#40148](https://github.com/expo/expo/pull/40148) by [@lukmccall](https://github.com/lukmccall))
20
21
  - [Android] Remove `ReactHostWrapper` ([#40295](https://github.com/expo/expo/pull/40295) by [@gabrieldonadel](https://github.com/gabrieldonadel))
21
22
 
23
+ ## 6.0.16 - 2025-10-21
24
+
25
+ _This version does not introduce any user-facing changes._
26
+
27
+ ## 6.0.15 - 2025-10-10
28
+
29
+ ### 🐛 Bug fixes
30
+
31
+ - [iOS] Fixes updates not being viewable on first launch. ([#40324](https://github.com/expo/expo/pull/40324) by [@alanjhughes](https://github.com/alanjhughes))
32
+
33
+ ## 6.0.14 - 2025-10-09
34
+
35
+ ### 💡 Others
36
+
37
+ - [Android] Prevented the app from crashing during the initialization of the `ErrorViewModel`. ([#40148](https://github.com/expo/expo/pull/40148) by [@lukmccall](https://github.com/lukmccall))
38
+
22
39
  ## 6.0.13 - 2025-10-01
23
40
 
24
41
  ### 🐛 Bug fixes
@@ -20,21 +20,32 @@ expoModule {
20
20
  }
21
21
 
22
22
  group = "host.exp.exponent"
23
- version = "6.1.0-canary-20251015-a6a1272"
23
+ version = "6.1.0-canary-20251031-b135dff"
24
24
 
25
25
  android {
26
26
  namespace "expo.modules.devlauncher"
27
27
  defaultConfig {
28
28
  versionCode 9
29
- versionName "6.1.0-canary-20251015-a6a1272"
29
+ versionName "6.1.0-canary-20251031-b135dff"
30
30
  }
31
31
 
32
32
  buildTypes {
33
+ create("debugOptimized") {
34
+ initWith(buildTypes.debug)
35
+ matchingFallbacks += ["release"]
36
+ }
37
+
33
38
  buildTypes.each {
34
39
  it.buildConfigField 'String', 'VERSION', "\"${defaultConfig.versionName}\""
35
40
  }
36
41
  }
37
42
 
43
+ sourceSets {
44
+ debugOptimized {
45
+ setRoot 'src/debug'
46
+ }
47
+ }
48
+
38
49
  buildFeatures {
39
50
  buildConfig true
40
51
  viewBinding true
@@ -483,6 +483,10 @@ class DevLauncherController private constructor() :
483
483
 
484
484
  @JvmStatic
485
485
  fun wrapReactActivityDelegate(activity: ReactActivity, devLauncherReactActivityDelegateSupplier: DevLauncherReactActivityDelegateSupplier): ReactActivityDelegate {
486
+ // Set activity class as launcher for createBasicAppIntent() to correctly identify the React Native Intent when launching,
487
+ // otherwise it will just use the main app intent, which is not always true in brownfield.
488
+ sLauncherClass = activity::class.java
489
+
486
490
  devLauncherKoin()
487
491
  .get<DevLauncherLifecycle>()
488
492
  .delegateWillBeCreated(activity)
@@ -1,15 +1,12 @@
1
1
  package expo.modules.devlauncher.launcher
2
2
 
3
3
  import android.os.Bundle
4
- import android.view.KeyEvent
5
- import android.view.MotionEvent
6
4
  import androidx.appcompat.app.AppCompatActivity
7
5
  import androidx.core.view.WindowCompat
8
6
  import expo.modules.devlauncher.DevLauncherController
9
7
  import expo.modules.devlauncher.compose.BindingView
10
8
  import expo.modules.devlauncher.koin.DevLauncherKoinComponent
11
9
  import expo.modules.devlauncher.services.DependencyInjection
12
- import expo.modules.devmenu.DevMenuManager
13
10
  import org.koin.core.component.inject
14
11
 
15
12
  class DevLauncherActivity : AppCompatActivity(), DevLauncherKoinComponent {
@@ -36,13 +33,4 @@ class DevLauncherActivity : AppCompatActivity(), DevLauncherKoinComponent {
36
33
  overridePendingTransition(0, 0)
37
34
  super.onPause()
38
35
  }
39
-
40
- override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
41
- DevMenuManager.onTouchEvent(ev)
42
- return super.dispatchTouchEvent(ev)
43
- }
44
-
45
- override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
46
- return DevMenuManager.onKeyEvent(keyCode, event) || super.onKeyUp(keyCode, event)
47
- }
48
36
  }
@@ -51,7 +51,7 @@ abstract class DevLauncherPlugin : Plugin<Project> {
51
51
  return if (System.getenv("EX_UPDATES_NATIVE_DEBUG") != "1" && reactExtension != null) {
52
52
  reactExtension.debuggableVariants.get().any { it.equals(variant.name, ignoreCase = true) }
53
53
  } else {
54
- variant.buildType == "debug"
54
+ variant.buildType == "debug" || variant.buildType == "debugOptimized"
55
55
  }
56
56
  }
57
57
 
@@ -0,0 +1,18 @@
1
+ // Copyright 2015-present 650 Industries. All rights reserved.
2
+
3
+ import Foundation
4
+ import EXDevMenuInterface
5
+
6
+ @objcMembers
7
+ public class DevLauncherDevMenuDelegate: NSObject, DevMenuHostDelegate {
8
+ public weak var controller: EXDevLauncherController?
9
+
10
+ public init(controller: EXDevLauncherController) {
11
+ self.controller = controller
12
+ super.init()
13
+ }
14
+
15
+ public func devMenuNavigateHome() {
16
+ controller?.navigateToLauncher()
17
+ }
18
+ }
@@ -46,15 +46,10 @@ NS_ASSUME_NONNULL_BEGIN
46
46
  @property (nonatomic, strong) EXDevLauncherPendingDeepLinkRegistry *pendingDeepLinkRegistry;
47
47
  @property (nonatomic, strong) EXDevLauncherRecentlyOpenedAppsRegistry *recentlyOpenedAppsRegistry;
48
48
  @property (nonatomic, strong) id<EXUpdatesExternalInterface> updatesInterface;
49
- @property (nonatomic, readonly, assign) BOOL isStarted;
50
49
 
51
50
  + (instancetype)sharedInstance;
52
51
 
53
- - (void)startWithWindow:(UIWindow *)window;
54
-
55
- - (void)autoSetupPrepare:(id<EXDevLauncherControllerDelegate>)delegate launchOptions:(NSDictionary * _Nullable)launchOptions;
56
-
57
- - (void)autoSetupStart:(UIWindow *)window;
52
+ - (void)start:(id<EXDevLauncherControllerDelegate>)delegate launchOptions:(NSDictionary * _Nullable)launchOptions;
58
53
 
59
54
  - (nullable NSURL *)sourceUrl;
60
55
 
@@ -78,8 +73,6 @@ NS_ASSUME_NONNULL_BEGIN
78
73
 
79
74
  - (BOOL)isAppRunning;
80
75
 
81
- - (BOOL)isStarted;
82
-
83
76
  - (UIWindow * _Nullable)currentWindow;
84
77
 
85
78
  - (EXDevLauncherErrorManager *)errorManager;
@@ -40,9 +40,6 @@
40
40
  #define VERSION @ STRINGIZE2(EX_DEV_LAUNCHER_VERSION)
41
41
  #endif
42
42
 
43
- #define EX_DEV_LAUNCHER_PACKAGER_PATH @"packages/expo-dev-launcher/index.bundle?platform=ios&dev=true&minify=false"
44
-
45
-
46
43
  @interface EXDevLauncherController ()
47
44
 
48
45
  @property (nonatomic, weak) UIWindow *window;
@@ -56,9 +53,10 @@
56
53
  @property (nonatomic, strong) EXDevLauncherErrorManager *errorManager;
57
54
  @property (nonatomic, strong) EXDevLauncherInstallationIDHelper *installationIDHelper;
58
55
  @property (nonatomic, strong, nullable) EXDevLauncherNetworkInterceptor *networkInterceptor;
59
- @property (nonatomic, assign) BOOL isStarted;
60
56
  @property (nonatomic, strong) EXDevLauncherReactNativeFactory *reactNativeFactory;
57
+ @property (nonatomic, strong) DevLauncherViewController *devLauncherViewController;
61
58
  @property (nonatomic, strong) NSURL *lastOpenedAppUrl;
59
+ @property (nonatomic, strong) DevLauncherDevMenuDelegate *devMenuDelegate;
62
60
 
63
61
  @end
64
62
 
@@ -87,23 +85,12 @@
87
85
 
88
86
  self.dependencyProvider = [RCTAppDependencyProvider new];
89
87
  self.reactNativeFactory = [[EXDevLauncherReactNativeFactory alloc] initWithDelegate:self releaseLevel:[self getReactNativeReleaseLevel]];
88
+ self.devMenuDelegate = [[DevLauncherDevMenuDelegate alloc] initWithController:self];
89
+ [[DevMenuManager shared] setDelegate:self.devMenuDelegate];
90
90
  }
91
91
  return self;
92
92
  }
93
93
 
94
- - (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
95
- {
96
- NSMutableArray<id<RCTBridgeModule>> *modules = [NSMutableArray new];
97
-
98
- [modules addObject:[RCTDevMenu new]];
99
- #ifndef EX_DEV_LAUNCHER_URL
100
- [modules addObject:[EXDevLauncherRCTDevSettings new]];
101
- #endif
102
- [modules addObject:[DevClientNoOpLoadingView new]];
103
-
104
- return modules;
105
- }
106
-
107
94
  + (NSString * _Nullable)version {
108
95
  #ifdef VERSION
109
96
  return VERSION;
@@ -111,88 +98,6 @@
111
98
  return nil;
112
99
  }
113
100
 
114
- // Expo developers: Enable the below code by running
115
- // export EX_DEV_LAUNCHER_URL=http://localhost:8090
116
- // in your shell before doing pod install. This will cause the controller to see if
117
- // the expo-launcher packager is running, and if so, use that instead of
118
- // the prebuilt bundle.
119
- // See the pod_target_xcconfig definition in expo-dev-launcher.podspec
120
-
121
- - (nullable NSURL *)devLauncherBaseURL
122
- {
123
- #ifdef EX_DEV_LAUNCHER_URL
124
- return [NSURL URLWithString:@EX_DEV_LAUNCHER_URL];
125
- #endif
126
- return nil;
127
- }
128
- - (nullable NSURL *)devLauncherURL
129
- {
130
- #ifdef EX_DEV_LAUNCHER_URL
131
- return [NSURL URLWithString:EX_DEV_LAUNCHER_PACKAGER_PATH
132
- relativeToURL:[self devLauncherBaseURL]];
133
- #endif
134
- return nil;
135
- }
136
-
137
- - (nullable NSURL *)devLauncherStatusURL
138
- {
139
- #ifdef EX_DEV_LAUNCHER_URL
140
- return [NSURL URLWithString:@"status"
141
- relativeToURL:[self devLauncherBaseURL]];
142
- #endif
143
- return nil;
144
- }
145
-
146
- - (BOOL)isLauncherPackagerRunning
147
- {
148
- // Shamelessly copied from RN core (RCTBundleURLProvider)
149
-
150
- // If we are not running in the main thread, run away
151
- if (![NSThread isMainThread]) {
152
- return NO;
153
- }
154
-
155
- NSURL *url = [self devLauncherStatusURL];
156
- NSURLSession *session = [NSURLSession sharedSession];
157
- NSURLRequest *request = [NSURLRequest requestWithURL:url
158
- cachePolicy:NSURLRequestUseProtocolCachePolicy
159
- timeoutInterval:1];
160
- __block NSURLResponse *response;
161
- __block NSData *data;
162
-
163
- dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
164
- [[session dataTaskWithRequest:request
165
- completionHandler:^(NSData *d, NSURLResponse *res, __unused NSError *err) {
166
- data = d;
167
- response = res;
168
- dispatch_semaphore_signal(semaphore);
169
- }] resume];
170
- dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
171
-
172
- NSString *status = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
173
- return [status isEqualToString:@"packager-status:running"];
174
- }
175
-
176
- - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
177
- {
178
- return [self getSourceURL];
179
- }
180
-
181
- - (NSURL *)bundleURL
182
- {
183
- return [self getSourceURL];
184
- }
185
-
186
- - (NSURL *)getSourceURL
187
- {
188
- NSURL *launcherURL = [self devLauncherURL];
189
- if (launcherURL != nil && [self isLauncherPackagerRunning]) {
190
- return launcherURL;
191
- }
192
- NSURL *bundleURL = [[NSBundle mainBundle] URLForResource:@"EXDevLauncher" withExtension:@"bundle"];
193
- return [[NSBundle bundleWithURL:bundleURL] URLForResource:@"main" withExtension:@"jsbundle"];
194
- }
195
-
196
101
  - (void)clearRecentlyOpenedApps
197
102
  {
198
103
  return [_recentlyOpenedAppsRegistry clearRegistry];
@@ -242,27 +147,22 @@
242
147
  return _possibleManifestURL;
243
148
  }
244
149
 
245
- - (UIWindow *)currentWindow
246
- {
247
- return _window;
248
- }
249
150
 
250
151
  - (EXDevLauncherErrorManager *)errorManage
251
152
  {
252
153
  return _errorManager;
253
154
  }
254
155
 
255
- - (void)startWithWindow:(UIWindow *)window
156
+ - (void)start:(id<EXDevLauncherControllerDelegate>)delegate launchOptions:(NSDictionary * _Nullable)launchOptions
256
157
  {
257
- _isStarted = YES;
258
- _window = window;
259
- EXDevLauncherUncaughtExceptionHandler.isInstalled = true;
260
-
261
- if (_launchOptions[UIApplicationLaunchOptionsURLKey]) {
262
- // For deeplink launch, we need the keyWindow for expo-splash-screen to setup correctly.
263
- [_window makeKeyWindow];
264
- return;
158
+ _delegate = delegate;
159
+ _launchOptions = launchOptions;
160
+ NSDictionary *lastOpenedApp = [self.recentlyOpenedAppsRegistry mostRecentApp];
161
+ if (lastOpenedApp != nil) {
162
+ _lastOpenedAppUrl = [NSURL URLWithString:lastOpenedApp[@"url"]];
265
163
  }
164
+ EXDevLauncherBundleURLProviderInterceptor.isInstalled = true;
165
+ EXDevLauncherUncaughtExceptionHandler.isInstalled = true;
266
166
 
267
167
  void (^navigateToLauncher)(NSError *) = ^(NSError *error) {
268
168
  __weak typeof(self) weakSelf = self;
@@ -294,26 +194,6 @@
294
194
  [self navigateToLauncher];
295
195
  }
296
196
 
297
- - (void)autoSetupPrepare:(id<EXDevLauncherControllerDelegate>)delegate launchOptions:(NSDictionary * _Nullable)launchOptions
298
- {
299
- _delegate = delegate;
300
- _launchOptions = launchOptions;
301
- NSDictionary *lastOpenedApp = [self.recentlyOpenedAppsRegistry mostRecentApp];
302
- if (lastOpenedApp != nil) {
303
- _lastOpenedAppUrl = [NSURL URLWithString:lastOpenedApp[@"url"]];
304
- }
305
- EXDevLauncherBundleURLProviderInterceptor.isInstalled = true;
306
- }
307
-
308
- - (void)autoSetupStart:(UIWindow *)window
309
- {
310
- if (_delegate != nil) {
311
- [self startWithWindow:window];
312
- } else {
313
- @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"[EXDevLauncherController autoSetupStart:] was called before autoSetupPrepare:. Make sure you've set up expo-modules correctly in AppDelegate and are using ReactDelegate to create a bridge before calling [super application:didFinishLaunchingWithOptions:]." userInfo:nil];
314
- }
315
- }
316
-
317
197
  - (void)navigateToLauncher
318
198
  {
319
199
  NSAssert([NSThread isMainThread], @"This function must be called on main thread");
@@ -330,23 +210,11 @@
330
210
  [self.delegate destroyReactInstance];
331
211
 
332
212
  #if RCT_DEV
333
- NSURL *url = [self devLauncherURL];
334
- if (url != nil) {
335
- // Connect to the websocket
336
- [[RCTPackagerConnection sharedPackagerConnection] setSocketConnectionURL:url];
337
- }
338
-
339
213
  [self _addInitModuleObserver];
340
214
  #endif
341
-
342
- DevLauncherViewController *swiftUIViewController = [[DevLauncherViewController alloc] init];
343
-
344
- _window.rootViewController = swiftUIViewController;
345
- [_window makeKeyAndVisible];
346
-
347
- dispatch_async(dispatch_get_main_queue(), ^{
348
- [self onAppContentDidAppear];
349
- });
215
+ if (_devLauncherViewController != nil) {
216
+ [_devLauncherViewController resetHostingController];
217
+ }
350
218
  }
351
219
 
352
220
  - (BOOL)onDeepLink:(NSURL *)url options:(NSDictionary *)options
@@ -766,20 +634,20 @@
766
634
  - (void)setDevMenuAppBridge
767
635
  {
768
636
  DevMenuManager *manager = [DevMenuManager shared];
769
- manager.currentBridge = self.appBridge.parentBridge;
637
+ [manager updateCurrentBridge:self.appBridge.parentBridge];
770
638
 
771
639
  if (self.manifest != nil) {
772
- manager.currentManifest = self.manifest;
773
- manager.currentManifestURL = self.manifestURL;
640
+ [manager updateCurrentManifest:self.manifest manifestURL:self.manifestURL];
641
+ } else {
642
+ [manager updateCurrentManifest:nil manifestURL:nil];
774
643
  }
775
644
  }
776
645
 
777
646
  - (void)invalidateDevMenuApp
778
647
  {
779
648
  DevMenuManager *manager = [DevMenuManager shared];
780
- manager.currentBridge = nil;
781
- manager.currentManifest = nil;
782
- manager.currentManifestURL = nil;
649
+ [manager updateCurrentBridge:nil];
650
+ [manager updateCurrentManifest:nil manifestURL:nil];
783
651
  }
784
652
 
785
653
  -(NSDictionary *)getUpdatesConfig: (nullable NSDictionary *) constants
@@ -863,7 +731,10 @@
863
731
 
864
732
  - (UIViewController *)createRootViewController
865
733
  {
866
- return [[DevLauncherViewController alloc] init];
734
+ if (_devLauncherViewController == nil){
735
+ _devLauncherViewController = [[DevLauncherViewController alloc] init];
736
+ }
737
+ return _devLauncherViewController;
867
738
  }
868
739
 
869
740
  - (void)setRootView:(UIView *)rootView toRootViewController:(UIViewController *)rootViewController
@@ -3,14 +3,6 @@
3
3
  import ExpoModulesCore
4
4
 
5
5
  public class ExpoDevLauncherAppDelegateSubscriber: ExpoAppDelegateSubscriber {
6
- public func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
7
- guard let window = UIApplication.shared.delegate?.window ?? UIApplication.shared.windows.filter { $0.isKeyWindow }.first else {
8
- fatalError("Cannot find the keyWindow. Make sure to call `window.makeKeyAndVisible()`.")
9
- }
10
- EXDevLauncherController.sharedInstance().autoSetupStart(window)
11
- return false
12
- }
13
-
14
6
  public func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
15
7
  return EXDevLauncherController.sharedInstance().onDeepLink(url, options: options)
16
8
  }
@@ -8,9 +8,9 @@ public class ExpoDevLauncherReactDelegateHandler: ExpoReactDelegateHandler, EXDe
8
8
  private weak var reactNativeFactory: RCTReactNativeFactory?
9
9
  private weak var reactDelegate: ExpoReactDelegate?
10
10
  private var launchOptions: [AnyHashable: Any]?
11
- private var deferredRootView: EXDevLauncherDeferredRCTRootView?
12
11
  private var rootViewModuleName: String?
13
12
  private var rootViewInitialProperties: [AnyHashable: Any]?
13
+ private weak var rootViewController: UIViewController?
14
14
 
15
15
  public override func createReactRootView(
16
16
  reactDelegate: ExpoReactDelegate,
@@ -24,7 +24,7 @@ public class ExpoDevLauncherReactDelegateHandler: ExpoReactDelegateHandler, EXDe
24
24
 
25
25
  self.reactDelegate = reactDelegate
26
26
  self.launchOptions = launchOptions
27
- EXDevLauncherController.sharedInstance().autoSetupPrepare(self, launchOptions: launchOptions)
27
+ EXDevLauncherController.sharedInstance().start(self, launchOptions: launchOptions)
28
28
  if let sharedController = UpdatesControllerRegistry.sharedInstance.controller {
29
29
  // for some reason the swift compiler and bridge are having issues here
30
30
  EXDevLauncherController.sharedInstance().updatesInterface = sharedController
@@ -33,8 +33,22 @@ public class ExpoDevLauncherReactDelegateHandler: ExpoReactDelegateHandler, EXDe
33
33
 
34
34
  self.rootViewModuleName = moduleName
35
35
  self.rootViewInitialProperties = initialProperties
36
- self.deferredRootView = EXDevLauncherDeferredRCTRootView()
37
- return self.deferredRootView
36
+
37
+ let viewController = EXDevLauncherController.sharedInstance().createRootViewController()
38
+ rootViewController = viewController
39
+
40
+ // We need to create a wrapper View because React Native Factory will reassign rootViewController later
41
+ let wrapperView = UIView()
42
+ wrapperView.addSubview(viewController.view)
43
+ viewController.view.translatesAutoresizingMaskIntoConstraints = false
44
+ NSLayoutConstraint.activate([
45
+ viewController.view.topAnchor.constraint(equalTo: wrapperView.topAnchor),
46
+ viewController.view.leadingAnchor.constraint(equalTo: wrapperView.leadingAnchor),
47
+ viewController.view.trailingAnchor.constraint(equalTo: wrapperView.trailingAnchor),
48
+ viewController.view.bottomAnchor.constraint(equalTo: wrapperView.bottomAnchor)
49
+ ])
50
+
51
+ return wrapperView
38
52
  }
39
53
 
40
54
  @objc
@@ -71,29 +85,13 @@ public class ExpoDevLauncherReactDelegateHandler: ExpoReactDelegateHandler, EXDe
71
85
  launchOptions: developmentClientController.getLaunchOptions()
72
86
  )
73
87
  developmentClientController.appBridge = RCTBridge.current()
74
- rootView.backgroundColor = self.deferredRootView?.backgroundColor ?? UIColor.white
75
- let window = getWindow()
76
88
 
77
- // NOTE: this order of assignment seems to actually have an effect on behaviour
78
- // direct assignment of window.rootViewController.view = rootView does not work
79
- guard let rootViewController = self.reactDelegate?.createRootViewController() else {
89
+ guard let rootViewController = rootViewController ?? self.reactDelegate?.createRootViewController() else {
80
90
  fatalError("Invalid rootViewController returned from ExpoReactDelegate")
81
91
  }
82
92
  rootViewController.view = rootView
83
- window.rootViewController = rootViewController
84
- window.makeKeyAndVisible()
85
-
86
93
  // it is purposeful that we don't clean up saved properties here, because we may initialize
87
94
  // several React instances over a single app lifetime and we want them all to have the same
88
95
  // initial properties
89
96
  }
90
-
91
- // MARK: Internals
92
-
93
- private func getWindow() -> UIWindow {
94
- guard let window = UIApplication.shared.windows.filter(\.isKeyWindow).first ?? UIApplication.shared.delegate?.window as? UIWindow else {
95
- fatalError("Cannot find the current window.")
96
- }
97
- return window
98
- }
99
97
  }
@@ -7,7 +7,7 @@ import SwiftUI
7
7
 
8
8
  @objc public override init(nibName: String?, bundle: Bundle?) {
9
9
  super.init(nibName: nibName, bundle: bundle)
10
- setupViewController()
10
+ addHostingController()
11
11
  }
12
12
 
13
13
  @objc public convenience init() {
@@ -16,7 +16,7 @@ import SwiftUI
16
16
 
17
17
  required init?(coder: NSCoder) {
18
18
  super.init(coder: coder)
19
- setupViewController()
19
+ addHostingController()
20
20
  }
21
21
 
22
22
  private func setupViewController() {
@@ -27,9 +27,13 @@ import SwiftUI
27
27
  hostingController?.view.backgroundColor = UIColor.clear
28
28
  }
29
29
 
30
- public override func viewDidLoad() {
31
- super.viewDidLoad()
30
+ @objc public func resetHostingController() {
31
+ hostingController = nil
32
+ view = UIView()
33
+ addHostingController()
34
+ }
32
35
 
36
+ private func addHostingController() {
33
37
  if hostingController == nil {
34
38
  setupViewController()
35
39
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "expo-dev-launcher",
3
3
  "title": "Expo Development Launcher",
4
- "version": "6.1.0-canary-20251015-a6a1272",
4
+ "version": "6.1.0-canary-20251031-b135dff",
5
5
  "description": "Pre-release version of the Expo development launcher package for testing.",
6
6
  "repository": {
7
7
  "type": "git",
@@ -15,10 +15,10 @@
15
15
  "license": "MIT",
16
16
  "homepage": "https://docs.expo.dev",
17
17
  "dependencies": {
18
- "expo-dev-menu": "7.0.15-canary-20251015-a6a1272",
19
- "expo-manifests": "1.0.9-canary-20251015-a6a1272"
18
+ "expo-dev-menu": "7.1.0-canary-20251031-b135dff",
19
+ "expo-manifests": "1.0.9-canary-20251031-b135dff"
20
20
  },
21
21
  "peerDependencies": {
22
- "expo": "55.0.0-canary-20251015-a6a1272"
22
+ "expo": "55.0.0-canary-20251031-b135dff"
23
23
  }
24
24
  }
@@ -1,14 +0,0 @@
1
- // Copyright 2018-present 650 Industries. All rights reserved.
2
-
3
- #import <React/RCTRootView.h>
4
-
5
- NS_ASSUME_NONNULL_BEGIN
6
-
7
- /**
8
- EXDevLauncherDeferredRCTRootView is a special no-op class for expo-dev-launcher to defer react instance creation until `EXDevLauncherController` finishing setup.
9
- */
10
- @interface EXDevLauncherDeferredRCTRootView : UIView
11
-
12
- @end
13
-
14
- NS_ASSUME_NONNULL_END
@@ -1,47 +0,0 @@
1
- // Copyright 2018-present 650 Industries. All rights reserved.
2
-
3
- #import <EXDevLauncher/EXDevLauncherDeferredRCTRootView.h>
4
-
5
- @interface EXDevLauncherNoopUIView : UIView
6
-
7
- @end
8
-
9
- @implementation EXDevLauncherNoopUIView
10
-
11
- - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
12
- {
13
- NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
14
- if (!signature) {
15
- signature = [NSMethodSignature signatureWithObjCTypes:"@@:"];
16
- }
17
- return signature;
18
- }
19
-
20
- - (void)forwardInvocation:(NSInvocation *)anInvocation
21
- {
22
- id nilPtr = nil;
23
- [anInvocation setReturnValue:&nilPtr];
24
- }
25
-
26
- @end
27
-
28
- @implementation EXDevLauncherDeferredRCTRootView
29
-
30
- #pragma clang diagnostic push
31
- #pragma clang diagnostic ignored "-Wobjc-designated-initializers"
32
-
33
- - (instancetype)initWithBridge:(RCTBridge *)bridge
34
- moduleName:(NSString *)moduleName
35
- initialProperties:(NSDictionary *)initialProperties
36
- {
37
- // RCTRootView throws an exception for default initializers
38
- // and other designated initializers requires a real bridge.
39
- // We use a trick here to initialize a NoopUIView and cast back to EXDevLauncherDeferredRCTRootView.
40
- self = (EXDevLauncherDeferredRCTRootView *)[[EXDevLauncherNoopUIView alloc] initWithFrame:CGRectZero];
41
- return self;
42
- }
43
-
44
- #pragma clang diagnostic pop
45
-
46
- @end
47
-