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 +19 -2
- package/android/build.gradle +13 -2
- package/android/src/debug/java/expo/modules/devlauncher/DevLauncherController.kt +4 -0
- package/android/src/debug/java/expo/modules/devlauncher/launcher/DevLauncherActivity.kt +0 -12
- package/expo-dev-launcher-gradle-plugin/src/main/kotlin/expo/modules/devlauncher/DevLauncherPlugin.kt +1 -1
- package/ios/DevLauncherDevMenuDelegate.swift +18 -0
- package/ios/EXDevLauncherController.h +1 -8
- package/ios/EXDevLauncherController.m +25 -154
- package/ios/ReactDelegateHandler/ExpoDevLauncherAppDelegateSubscriber.swift +0 -8
- package/ios/ReactDelegateHandler/ExpoDevLauncherReactDelegateHandler.swift +19 -21
- package/ios/SwiftUI/DevLauncherViewController.swift +8 -4
- package/package.json +4 -4
- package/ios/ReactDelegateHandler/EXDevLauncherDeferredRCTRootView.h +0 -14
- package/ios/ReactDelegateHandler/EXDevLauncherDeferredRCTRootView.m +0 -47
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
|
-
- [
|
|
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
|
package/android/build.gradle
CHANGED
|
@@ -20,21 +20,32 @@ expoModule {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
group = "host.exp.exponent"
|
|
23
|
-
version = "6.1.0-canary-
|
|
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-
|
|
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)
|
|
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)
|
|
156
|
+
- (void)start:(id<EXDevLauncherControllerDelegate>)delegate launchOptions:(NSDictionary * _Nullable)launchOptions
|
|
256
157
|
{
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
-
|
|
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
|
|
637
|
+
[manager updateCurrentBridge:self.appBridge.parentBridge];
|
|
770
638
|
|
|
771
639
|
if (self.manifest != nil) {
|
|
772
|
-
manager.
|
|
773
|
-
|
|
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
|
|
781
|
-
manager
|
|
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
|
-
|
|
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().
|
|
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
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
31
|
-
|
|
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-
|
|
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
|
|
19
|
-
"expo-manifests": "1.0.9-canary-
|
|
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-
|
|
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
|
-
|