expo-dev-launcher 6.1.0-canary-20251009-9919e08 → 6.1.0-canary-20251015-a6a1272
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 +3 -0
- package/android/build.gradle +2 -2
- package/android/src/debug/java/expo/modules/devlauncher/DevLauncherController.kt +14 -9
- package/android/src/debug/java/expo/modules/devlauncher/helpers/DevLauncherReactUtils.kt +4 -78
- package/android/src/debug/java/expo/modules/devlauncher/launcher/errors/DevLauncherUncaughtExceptionHandler.kt +1 -1
- package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherAppLoader.kt +2 -2
- package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherAppLoaderFactory.kt +2 -2
- package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherExpoAppLoader.kt +2 -2
- package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherLocalAppLoader.kt +2 -2
- package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherPublishedAppLoader.kt +2 -2
- package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherReactNativeAppLoader.kt +2 -2
- package/android/src/debug/java/expo/modules/devlauncher/react/DevLauncherDevSupportManagerSwapper.kt +1 -34
- package/android/src/main/java/expo/modules/devlauncher/launcher/DevLauncherControllerInterface.kt +2 -2
- package/android/src/release/java/expo/modules/devlauncher/DevLauncherController.kt +9 -5
- package/ios/EXDevLauncherController.m +33 -2
- package/ios/SwiftUI/Data.swift +2 -0
- package/ios/SwiftUI/DevLauncherViewModel.swift +0 -1
- package/ios/SwiftUI/HomeTabView.swift +0 -1
- package/ios/SwiftUI/SettingsTabView.swift +1 -1
- package/package.json +4 -4
- package/ios/EXDevLauncherDevMenuExtensions.m +0 -38
package/CHANGELOG.md
CHANGED
|
@@ -10,11 +10,14 @@
|
|
|
10
10
|
|
|
11
11
|
### 🐛 Bug fixes
|
|
12
12
|
|
|
13
|
+
- [iOS] Fixes updates not being viewable on first launch. ([#40324](https://github.com/expo/expo/pull/40324) by [@alanjhughes](https://github.com/alanjhughes))
|
|
14
|
+
|
|
13
15
|
### 💡 Others
|
|
14
16
|
|
|
15
17
|
- [Android] Migrated from `kotlinOptions` to `compilerOptions` DSL. ([#39794](https://github.com/expo/expo/pull/39794) by [@huextrat](https://github.com/huextrat))
|
|
16
18
|
- [android] Make reactNativeHost optional in ReactHostWrapper ([#40085](https://github.com/expo/expo/pull/40085) by [@gabrieldonadel](https://github.com/gabrieldonadel))
|
|
17
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
|
+
- [Android] Remove `ReactHostWrapper` ([#40295](https://github.com/expo/expo/pull/40295) by [@gabrieldonadel](https://github.com/gabrieldonadel))
|
|
18
21
|
|
|
19
22
|
## 6.0.13 - 2025-10-01
|
|
20
23
|
|
package/android/build.gradle
CHANGED
|
@@ -20,13 +20,13 @@ expoModule {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
group = "host.exp.exponent"
|
|
23
|
-
version = "6.1.0-canary-
|
|
23
|
+
version = "6.1.0-canary-20251015-a6a1272"
|
|
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-20251015-a6a1272"
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
buildTypes {
|
|
@@ -13,9 +13,9 @@ import androidx.core.net.toUri
|
|
|
13
13
|
import com.facebook.react.ReactActivity
|
|
14
14
|
import com.facebook.react.ReactActivityDelegate
|
|
15
15
|
import com.facebook.react.ReactApplication
|
|
16
|
+
import com.facebook.react.ReactHost
|
|
16
17
|
import com.facebook.react.ReactPackage
|
|
17
18
|
import com.facebook.react.bridge.ReactContext
|
|
18
|
-
import expo.interfaces.devmenu.ReactHostWrapper
|
|
19
19
|
import expo.modules.devlauncher.helpers.DevLauncherInstallationIDHelper
|
|
20
20
|
import expo.modules.devlauncher.helpers.DevLauncherMetadataHelper
|
|
21
21
|
import expo.modules.devlauncher.helpers.DevLauncherUrl
|
|
@@ -64,7 +64,7 @@ class DevLauncherController private constructor() :
|
|
|
64
64
|
private val context: Context by lazy {
|
|
65
65
|
DevLauncherKoinContext.app.koin.get()
|
|
66
66
|
}
|
|
67
|
-
override val appHost:
|
|
67
|
+
override val appHost: ReactHost by inject()
|
|
68
68
|
private val httpClient: OkHttpClient by inject()
|
|
69
69
|
private val lifecycle: DevLauncherLifecycle by inject()
|
|
70
70
|
private val pendingIntentRegistry: DevLauncherIntentRegistryInterface by inject()
|
|
@@ -302,8 +302,8 @@ class DevLauncherController private constructor() :
|
|
|
302
302
|
return false
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
-
private fun ensureHostWasCleared(host:
|
|
306
|
-
if (host.
|
|
305
|
+
private fun ensureHostWasCleared(host: ReactHost, activityToBeInvalidated: ReactActivity? = null) {
|
|
306
|
+
if (host.currentReactContext?.hasActiveReactInstance() == true) {
|
|
307
307
|
runBlockingOnMainThread {
|
|
308
308
|
networkInterceptor?.close()
|
|
309
309
|
networkInterceptor = null
|
|
@@ -328,8 +328,8 @@ class DevLauncherController private constructor() :
|
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
@UiThread
|
|
331
|
-
private fun clearHost(host:
|
|
332
|
-
host.destroy()
|
|
331
|
+
private fun clearHost(host: ReactHost, activityToBeInvalidated: ReactActivity?) {
|
|
332
|
+
host.destroy("DevLauncher reloading app", null)
|
|
333
333
|
activityToBeInvalidated?.let {
|
|
334
334
|
invalidateActivity(it)
|
|
335
335
|
}
|
|
@@ -423,7 +423,7 @@ class DevLauncherController private constructor() :
|
|
|
423
423
|
}
|
|
424
424
|
|
|
425
425
|
@JvmStatic
|
|
426
|
-
internal fun initialize(context: Context, reactHost:
|
|
426
|
+
internal fun initialize(context: Context, reactHost: ReactHost) {
|
|
427
427
|
try {
|
|
428
428
|
val splashScreenManagerClass = Class.forName("expo.modules.splashscreen.SplashScreenManager")
|
|
429
429
|
val splashScreenManager = splashScreenManagerClass
|
|
@@ -464,14 +464,19 @@ class DevLauncherController private constructor() :
|
|
|
464
464
|
}
|
|
465
465
|
|
|
466
466
|
@JvmStatic
|
|
467
|
-
fun initialize(context: Context, reactHost:
|
|
467
|
+
fun initialize(context: Context, reactHost: ReactHost, launcherClass: Class<*>? = null) {
|
|
468
468
|
initialize(context, reactHost)
|
|
469
469
|
sLauncherClass = launcherClass
|
|
470
470
|
}
|
|
471
471
|
|
|
472
472
|
@JvmStatic
|
|
473
473
|
fun initialize(reactApplication: ReactApplication, additionalPackages: List<ReactPackage>? = null, launcherClass: Class<*>? = null) {
|
|
474
|
-
|
|
474
|
+
val reactHost = reactApplication.reactHost
|
|
475
|
+
checkNotNull(reactHost) {
|
|
476
|
+
"DevLauncherController.initialize() was called before reactHost was initialized"
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
initialize(reactApplication as Context, reactHost)
|
|
475
480
|
sAdditionalPackages = additionalPackages
|
|
476
481
|
sLauncherClass = launcherClass
|
|
477
482
|
}
|
|
@@ -17,7 +17,6 @@ import com.facebook.react.devsupport.interfaces.DevSupportManager
|
|
|
17
17
|
import com.facebook.react.modules.systeminfo.AndroidInfoHelpers
|
|
18
18
|
import com.facebook.react.runtime.ReactHostDelegate
|
|
19
19
|
import com.facebook.react.runtime.ReactHostImpl
|
|
20
|
-
import expo.interfaces.devmenu.ReactHostWrapper
|
|
21
20
|
import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
|
|
22
21
|
import expo.modules.devlauncher.react.DevLauncherBridgeDevSupportManager
|
|
23
22
|
import expo.modules.devlauncher.react.DevLauncherBridgelessDevSupportManager
|
|
@@ -31,7 +30,7 @@ private const val EXPO_REACT_HOST_DELEGATE_CLASS = "expo.modules.ExpoReactHostFa
|
|
|
31
30
|
|
|
32
31
|
fun injectReactInterceptor(
|
|
33
32
|
context: Context,
|
|
34
|
-
reactHost:
|
|
33
|
+
reactHost: ReactHost,
|
|
35
34
|
url: Uri
|
|
36
35
|
): Boolean {
|
|
37
36
|
val (debugServerHost, appBundleName) = parseUrl(url)
|
|
@@ -44,15 +43,11 @@ fun injectReactInterceptor(
|
|
|
44
43
|
debugServerHost,
|
|
45
44
|
appBundleName
|
|
46
45
|
)
|
|
47
|
-
|
|
48
|
-
(reactHost.devSupportManager as? DevLauncherBridgelessDevSupportManager)?.startInspectorWhenDevLauncherReady()
|
|
49
|
-
} else {
|
|
50
|
-
(reactHost.devSupportManager as? DevLauncherBridgeDevSupportManager)?.startInspectorWhenDevLauncherReady()
|
|
51
|
-
}
|
|
46
|
+
(reactHost.devSupportManager as? DevLauncherBridgelessDevSupportManager)?.startInspectorWhenDevLauncherReady()
|
|
52
47
|
return result
|
|
53
48
|
}
|
|
54
49
|
|
|
55
|
-
private fun injectDevSupportManager(reactHost:
|
|
50
|
+
private fun injectDevSupportManager(reactHost: ReactHost) {
|
|
56
51
|
DevLauncherDevSupportManagerSwapper().swapDevSupportManagerImpl(reactHost)
|
|
57
52
|
|
|
58
53
|
// Swapping dev support manager overrides dev menu setup.
|
|
@@ -60,41 +55,6 @@ private fun injectDevSupportManager(reactHost: ReactHostWrapper) {
|
|
|
60
55
|
DevMenuManager.initializeWithReactHost(reactHost)
|
|
61
56
|
}
|
|
62
57
|
|
|
63
|
-
fun injectDebugServerHost(
|
|
64
|
-
context: Context,
|
|
65
|
-
reactHost: ReactHostWrapper,
|
|
66
|
-
debugServerHost: String,
|
|
67
|
-
appBundleName: String
|
|
68
|
-
): Boolean {
|
|
69
|
-
return if (reactHost.isBridgelessMode) {
|
|
70
|
-
injectDebugServerHost(context, reactHost.reactHost, debugServerHost, appBundleName)
|
|
71
|
-
} else {
|
|
72
|
-
injectDebugServerHost(context, reactHost.reactNativeHost, debugServerHost, appBundleName)
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
fun injectDebugServerHost(
|
|
77
|
-
context: Context,
|
|
78
|
-
reactNativeHost: ReactNativeHost,
|
|
79
|
-
debugServerHost: String,
|
|
80
|
-
appBundleName: String
|
|
81
|
-
): Boolean {
|
|
82
|
-
return try {
|
|
83
|
-
val instanceManager = reactNativeHost.reactInstanceManager
|
|
84
|
-
val devSupportManager = instanceManager.devSupportManager
|
|
85
|
-
injectDebugServerHost(context, devSupportManager, debugServerHost, appBundleName)
|
|
86
|
-
|
|
87
|
-
// set useDeveloperSupport to true in case it was previously set to false from loading a published app
|
|
88
|
-
val mUseDeveloperSupportField = instanceManager.javaClass.getDeclaredField("mUseDeveloperSupport")
|
|
89
|
-
mUseDeveloperSupportField.isAccessible = true
|
|
90
|
-
mUseDeveloperSupportField[instanceManager] = true
|
|
91
|
-
true
|
|
92
|
-
} catch (e: Exception) {
|
|
93
|
-
Log.e("DevLauncher", "Unable to inject debug server host settings.", e)
|
|
94
|
-
false
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
58
|
fun injectDebugServerHost(
|
|
99
59
|
context: Context,
|
|
100
60
|
reactHost: ReactHost,
|
|
@@ -140,42 +100,8 @@ private fun injectDebugServerHost(
|
|
|
140
100
|
packagerConnectionSettingsField[devServerHelper] = settings.public_getPackagerConnectionSettings()
|
|
141
101
|
}
|
|
142
102
|
|
|
143
|
-
fun injectLocalBundleLoader(
|
|
144
|
-
reactHost: ReactHostWrapper,
|
|
145
|
-
bundlePath: String
|
|
146
|
-
): Boolean {
|
|
147
|
-
return if (reactHost.isBridgelessMode) {
|
|
148
|
-
injectLocalBundleLoader(reactHost.reactHost, bundlePath)
|
|
149
|
-
} else {
|
|
150
|
-
injectLocalBundleLoader(reactHost.reactNativeHost, bundlePath)
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
private fun injectLocalBundleLoader(
|
|
155
|
-
reactNativeHost: ReactNativeHost,
|
|
156
|
-
bundlePath: String
|
|
157
|
-
): Boolean {
|
|
158
|
-
return try {
|
|
159
|
-
val instanceManager = reactNativeHost.reactInstanceManager
|
|
160
|
-
val instanceManagerClass = instanceManager.javaClass
|
|
161
|
-
|
|
162
|
-
val jsBundleLoader = JSBundleLoader.createFileLoader(bundlePath)
|
|
163
|
-
val mBundleLoaderField = instanceManagerClass.getDeclaredField("mBundleLoader")
|
|
164
|
-
mBundleLoaderField.isAccessible = true
|
|
165
|
-
mBundleLoaderField[instanceManager] = jsBundleLoader
|
|
166
|
-
|
|
167
|
-
val mUseDeveloperSupportField = instanceManagerClass.getDeclaredField("mUseDeveloperSupport")
|
|
168
|
-
mUseDeveloperSupportField.isAccessible = true
|
|
169
|
-
mUseDeveloperSupportField[instanceManager] = false
|
|
170
|
-
true
|
|
171
|
-
} catch (e: Exception) {
|
|
172
|
-
Log.e("DevLauncher", "Unable to load local bundle file", e)
|
|
173
|
-
false
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
103
|
@OptIn(UnstableReactNativeAPI::class)
|
|
178
|
-
|
|
104
|
+
fun injectLocalBundleLoader(
|
|
179
105
|
reactHost: ReactHost,
|
|
180
106
|
bundlePath: String
|
|
181
107
|
): Boolean {
|
|
@@ -102,7 +102,7 @@ class DevLauncherUncaughtExceptionHandler(
|
|
|
102
102
|
private fun tryToSendExceptionToBundler(exception: Throwable) {
|
|
103
103
|
if (
|
|
104
104
|
controller.mode != DevLauncherController.Mode.APP ||
|
|
105
|
-
|
|
105
|
+
controller.appHost.currentReactContext?.hasActiveReactInstance() != true ||
|
|
106
106
|
controller.appHost.currentReactContext === null
|
|
107
107
|
) {
|
|
108
108
|
return
|
package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherAppLoader.kt
CHANGED
|
@@ -5,9 +5,9 @@ import android.content.Intent
|
|
|
5
5
|
import androidx.lifecycle.DefaultLifecycleObserver
|
|
6
6
|
import androidx.lifecycle.LifecycleOwner
|
|
7
7
|
import com.facebook.react.ReactActivity
|
|
8
|
+
import com.facebook.react.ReactHost
|
|
8
9
|
import com.facebook.react.ReactInstanceEventListener
|
|
9
10
|
import com.facebook.react.bridge.ReactContext
|
|
10
|
-
import expo.interfaces.devmenu.ReactHostWrapper
|
|
11
11
|
import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
|
|
12
12
|
import kotlinx.coroutines.Dispatchers
|
|
13
13
|
import kotlinx.coroutines.withContext
|
|
@@ -35,7 +35,7 @@ import kotlin.coroutines.suspendCoroutine
|
|
|
35
35
|
* - `onReactContext` - is called after the `ReactContext` was loaded.
|
|
36
36
|
*/
|
|
37
37
|
abstract class DevLauncherAppLoader(
|
|
38
|
-
private val appHost:
|
|
38
|
+
private val appHost: ReactHost,
|
|
39
39
|
private val context: Context,
|
|
40
40
|
private val controller: DevLauncherControllerInterface
|
|
41
41
|
) {
|
|
@@ -2,8 +2,8 @@ package expo.modules.devlauncher.launcher.loaders
|
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import android.net.Uri
|
|
5
|
+
import com.facebook.react.ReactHost
|
|
5
6
|
import expo.modules.devlauncher.helpers.DevLauncherInstallationIDHelper
|
|
6
|
-
import expo.interfaces.devmenu.ReactHostWrapper
|
|
7
7
|
import expo.modules.devlauncher.helpers.createUpdatesConfigurationWithUrl
|
|
8
8
|
import expo.modules.devlauncher.helpers.loadUpdate
|
|
9
9
|
import expo.modules.devlauncher.koin.DevLauncherKoinComponent
|
|
@@ -22,7 +22,7 @@ interface DevLauncherAppLoaderFactoryInterface {
|
|
|
22
22
|
|
|
23
23
|
class DevLauncherAppLoaderFactory : DevLauncherKoinComponent, DevLauncherAppLoaderFactoryInterface {
|
|
24
24
|
private val context: Context by inject()
|
|
25
|
-
private val appHost:
|
|
25
|
+
private val appHost: ReactHost by inject()
|
|
26
26
|
private val updatesInterface: UpdatesInterface? by optInject()
|
|
27
27
|
private val controller: DevLauncherControllerInterface by inject()
|
|
28
28
|
private val installationIDHelper: DevLauncherInstallationIDHelper by inject()
|
package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherExpoAppLoader.kt
CHANGED
|
@@ -5,9 +5,9 @@ import android.graphics.Color
|
|
|
5
5
|
import android.util.Log
|
|
6
6
|
import android.view.View
|
|
7
7
|
import com.facebook.react.ReactActivity
|
|
8
|
+
import com.facebook.react.ReactHost
|
|
8
9
|
import com.facebook.react.bridge.ReactContext
|
|
9
10
|
import com.facebook.react.modules.appearance.AppearanceModule
|
|
10
|
-
import expo.interfaces.devmenu.ReactHostWrapper
|
|
11
11
|
import expo.modules.devlauncher.helpers.isValidColor
|
|
12
12
|
import expo.modules.devlauncher.helpers.setProtectedDeclaredField
|
|
13
13
|
import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
|
|
@@ -17,7 +17,7 @@ import expo.modules.manifests.core.Manifest
|
|
|
17
17
|
|
|
18
18
|
abstract class DevLauncherExpoAppLoader(
|
|
19
19
|
private val manifest: Manifest,
|
|
20
|
-
appHost:
|
|
20
|
+
appHost: ReactHost,
|
|
21
21
|
context: Context,
|
|
22
22
|
controller: DevLauncherControllerInterface,
|
|
23
23
|
private val activityConfigurator: DevLauncherExpoActivityConfigurator =
|
|
@@ -2,7 +2,7 @@ package expo.modules.devlauncher.launcher.loaders
|
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import android.net.Uri
|
|
5
|
-
import
|
|
5
|
+
import com.facebook.react.ReactHost
|
|
6
6
|
import expo.modules.devlauncher.helpers.injectReactInterceptor
|
|
7
7
|
import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
|
|
8
8
|
import expo.modules.manifests.core.Manifest
|
|
@@ -13,7 +13,7 @@ import expo.modules.manifests.core.Manifest
|
|
|
13
13
|
*/
|
|
14
14
|
class DevLauncherLocalAppLoader(
|
|
15
15
|
private val manifest: Manifest,
|
|
16
|
-
private val appHost:
|
|
16
|
+
private val appHost: ReactHost,
|
|
17
17
|
private val context: Context,
|
|
18
18
|
controller: DevLauncherControllerInterface
|
|
19
19
|
) : DevLauncherExpoAppLoader(manifest, appHost, context, controller) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
package expo.modules.devlauncher.launcher.loaders
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
-
import
|
|
4
|
+
import com.facebook.react.ReactHost
|
|
5
5
|
import expo.modules.devlauncher.helpers.injectLocalBundleLoader
|
|
6
6
|
import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
|
|
7
7
|
import expo.modules.manifests.core.Manifest
|
|
@@ -13,7 +13,7 @@ import expo.modules.manifests.core.Manifest
|
|
|
13
13
|
class DevLauncherPublishedAppLoader(
|
|
14
14
|
manifest: Manifest,
|
|
15
15
|
private val localBundlePath: String,
|
|
16
|
-
private val appHost:
|
|
16
|
+
private val appHost: ReactHost,
|
|
17
17
|
context: Context,
|
|
18
18
|
controller: DevLauncherControllerInterface
|
|
19
19
|
) : DevLauncherExpoAppLoader(manifest, appHost, context, controller) {
|
|
@@ -2,13 +2,13 @@ package expo.modules.devlauncher.launcher.loaders
|
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import android.net.Uri
|
|
5
|
-
import
|
|
5
|
+
import com.facebook.react.ReactHost
|
|
6
6
|
import expo.modules.devlauncher.helpers.injectReactInterceptor
|
|
7
7
|
import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
|
|
8
8
|
|
|
9
9
|
class DevLauncherReactNativeAppLoader(
|
|
10
10
|
private val url: Uri,
|
|
11
|
-
private val appHost:
|
|
11
|
+
private val appHost: ReactHost,
|
|
12
12
|
private val context: Context,
|
|
13
13
|
controller: DevLauncherControllerInterface
|
|
14
14
|
) : DevLauncherAppLoader(appHost, context, controller) {
|
package/android/src/debug/java/expo/modules/devlauncher/react/DevLauncherDevSupportManagerSwapper.kt
CHANGED
|
@@ -11,7 +11,6 @@ import com.facebook.react.devsupport.ReleaseDevSupportManager
|
|
|
11
11
|
import com.facebook.react.devsupport.interfaces.DevSupportManager
|
|
12
12
|
import com.facebook.react.packagerconnection.JSPackagerClient
|
|
13
13
|
import com.facebook.react.runtime.ReactHostImpl
|
|
14
|
-
import expo.interfaces.devmenu.ReactHostWrapper
|
|
15
14
|
import expo.modules.devlauncher.helpers.getProtectedFieldValue
|
|
16
15
|
import expo.modules.devlauncher.helpers.setProtectedDeclaredField
|
|
17
16
|
import expo.modules.devlauncher.koin.DevLauncherKoinComponent
|
|
@@ -23,39 +22,7 @@ import org.koin.core.component.inject
|
|
|
23
22
|
internal class DevLauncherDevSupportManagerSwapper : DevLauncherKoinComponent {
|
|
24
23
|
private val controller: DevLauncherControllerInterface by inject()
|
|
25
24
|
|
|
26
|
-
fun swapDevSupportManagerImpl(reactHost:
|
|
27
|
-
if (reactHost.isBridgelessMode) {
|
|
28
|
-
swapDevSupportManagerImpl(reactHost.reactHost)
|
|
29
|
-
} else {
|
|
30
|
-
swapDevSupportManagerImpl(reactHost.reactNativeHost)
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
private fun swapDevSupportManagerImpl(reactNativeHost: ReactNativeHost) {
|
|
35
|
-
val reactInstanceManager = reactNativeHost.reactInstanceManager
|
|
36
|
-
val currentDevSupportManager = reactInstanceManager.devSupportManager
|
|
37
|
-
if (currentDevSupportManager is DevLauncherBridgeDevSupportManager) {
|
|
38
|
-
// DevSupportManager was swapped by the DevLauncherReactNativeHostHandler
|
|
39
|
-
return
|
|
40
|
-
}
|
|
41
|
-
if (currentDevSupportManager is ReleaseDevSupportManager) {
|
|
42
|
-
Log.i("DevLauncher", "DevSupportManager is disabled. So we don't want to override it.")
|
|
43
|
-
return
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
try {
|
|
47
|
-
val devManagerClass = DevSupportManagerBase::class.java
|
|
48
|
-
val newDevSupportManager = createDevLauncherBridgeDevSupportManager(devManagerClass, currentDevSupportManager)
|
|
49
|
-
|
|
50
|
-
ReactInstanceManager::class.java.setProtectedDeclaredField(reactInstanceManager, "devSupportManager", newDevSupportManager)
|
|
51
|
-
|
|
52
|
-
closeExistingConnection(devManagerClass, currentDevSupportManager)
|
|
53
|
-
} catch (e: Exception) {
|
|
54
|
-
Log.i("DevLauncher", "Couldn't inject `DevLauncherDevSupportManager`.", e)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
private fun swapDevSupportManagerImpl(reactHost: ReactHost) {
|
|
25
|
+
fun swapDevSupportManagerImpl(reactHost: ReactHost) {
|
|
59
26
|
val currentDevSupportManager = requireNotNull(reactHost.devSupportManager)
|
|
60
27
|
if (currentDevSupportManager is DevLauncherBridgelessDevSupportManager) {
|
|
61
28
|
// DevSupportManager was swapped by the DevLauncherReactNativeHostHandler
|
package/android/src/main/java/expo/modules/devlauncher/launcher/DevLauncherControllerInterface.kt
CHANGED
|
@@ -4,9 +4,9 @@ import android.content.Intent
|
|
|
4
4
|
import android.net.Uri
|
|
5
5
|
import com.facebook.react.ReactActivity
|
|
6
6
|
import com.facebook.react.ReactActivityDelegate
|
|
7
|
+
import com.facebook.react.ReactHost
|
|
7
8
|
import com.facebook.react.bridge.ReactContext
|
|
8
9
|
import expo.modules.devlauncher.DevLauncherController
|
|
9
|
-
import expo.interfaces.devmenu.ReactHostWrapper
|
|
10
10
|
import expo.modules.manifests.core.Manifest
|
|
11
11
|
import expo.modules.updatesinterface.UpdatesInterface
|
|
12
12
|
import expo.modules.updatesinterface.UpdatesInterfaceCallbacks
|
|
@@ -27,7 +27,7 @@ interface DevLauncherControllerInterface :
|
|
|
27
27
|
val manifest: Manifest?
|
|
28
28
|
val manifestURL: Uri?
|
|
29
29
|
val mode: DevLauncherController.Mode
|
|
30
|
-
val appHost:
|
|
30
|
+
val appHost: ReactHost
|
|
31
31
|
val latestLoadedApp: Uri?
|
|
32
32
|
val useDeveloperSupport: Boolean
|
|
33
33
|
var updatesInterface: UpdatesInterface?
|
|
@@ -6,8 +6,8 @@ import android.net.Uri
|
|
|
6
6
|
import com.facebook.react.ReactActivity
|
|
7
7
|
import com.facebook.react.ReactActivityDelegate
|
|
8
8
|
import com.facebook.react.ReactApplication
|
|
9
|
+
import com.facebook.react.ReactHost
|
|
9
10
|
import com.facebook.react.bridge.ReactContext
|
|
10
|
-
import expo.interfaces.devmenu.ReactHostWrapper
|
|
11
11
|
import expo.modules.devlauncher.launcher.DevLauncherAppEntry
|
|
12
12
|
import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
|
|
13
13
|
import expo.modules.devlauncher.launcher.DevLauncherReactActivityDelegateSupplier
|
|
@@ -34,7 +34,7 @@ class DevLauncherController private constructor() : DevLauncherControllerInterfa
|
|
|
34
34
|
override val manifestURL: Uri
|
|
35
35
|
get() = throw IllegalStateException(DEV_LAUNCHER_IS_NOT_AVAILABLE)
|
|
36
36
|
|
|
37
|
-
override val appHost:
|
|
37
|
+
override val appHost: ReactHost
|
|
38
38
|
get() = throw IllegalStateException(DEV_LAUNCHER_IS_NOT_AVAILABLE)
|
|
39
39
|
|
|
40
40
|
override var updatesInterface: UpdatesInterface?
|
|
@@ -96,19 +96,23 @@ class DevLauncherController private constructor() : DevLauncherControllerInterfa
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
@JvmStatic
|
|
99
|
-
internal fun initialize(context: Context, reactHost:
|
|
99
|
+
internal fun initialize(context: Context, reactHost: ReactHost) {
|
|
100
100
|
check(sInstance == null) { "DevelopmentClientController was initialized." }
|
|
101
101
|
sInstance = DevLauncherController()
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
@JvmStatic
|
|
105
|
-
fun initialize(context: Context, reactHost:
|
|
105
|
+
fun initialize(context: Context, reactHost: ReactHost, launcherClass: Class<*>? = null) {
|
|
106
106
|
initialize(context, reactHost)
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
@JvmStatic
|
|
110
110
|
fun initialize(reactApplication: ReactApplication, additionalPackages: List<*>? = null, launcherClass: Class<*>? = null) {
|
|
111
|
-
|
|
111
|
+
val reactHost = reactApplication.reactHost
|
|
112
|
+
checkNotNull(reactHost) {
|
|
113
|
+
"DevLauncherController.initialize() was called before reactHost was initialized"
|
|
114
|
+
}
|
|
115
|
+
initialize(reactApplication as Context, reactHost)
|
|
112
116
|
}
|
|
113
117
|
|
|
114
118
|
@JvmStatic
|
|
@@ -686,10 +686,28 @@
|
|
|
686
686
|
NSString *appVersion = [self getFormattedAppVersion];
|
|
687
687
|
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleDisplayName"] ?: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleExecutable"];
|
|
688
688
|
|
|
689
|
+
NSString *sdkVersion = nil;
|
|
690
|
+
if (self.manifest != nil) {
|
|
691
|
+
NSDictionary *expoConfig = [self.manifest expoClientConfigRootObject];
|
|
692
|
+
id sdk = expoConfig[@"sdkVersion"];
|
|
693
|
+
if ([sdk isKindOfClass:[NSString class]]) {
|
|
694
|
+
sdkVersion = (NSString *)sdk;
|
|
695
|
+
} else {
|
|
696
|
+
NSDictionary *rawManifest = [self.manifest rawManifestJSON];
|
|
697
|
+
id sdkFromManifest = rawManifest[@"sdkVersion"];
|
|
698
|
+
if ([sdkFromManifest isKindOfClass:[NSString class]]) {
|
|
699
|
+
sdkVersion = (NSString *)sdkFromManifest;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
689
704
|
[buildInfo setObject:appName forKey:@"appName"];
|
|
690
705
|
[buildInfo setObject:appIcon forKey:@"appIcon"];
|
|
691
706
|
[buildInfo setObject:appVersion forKey:@"appVersion"];
|
|
692
707
|
[buildInfo setObject:runtimeVersion forKey:@"runtimeVersion"];
|
|
708
|
+
if (sdkVersion) {
|
|
709
|
+
[buildInfo setObject:sdkVersion forKey:@"sdkVersion"];
|
|
710
|
+
}
|
|
693
711
|
|
|
694
712
|
return buildInfo;
|
|
695
713
|
}
|
|
@@ -775,17 +793,30 @@
|
|
|
775
793
|
|
|
776
794
|
// the project url field is added to app.json.updates when running `eas update:configure`
|
|
777
795
|
// the `u.expo.dev` determines that it is the modern manifest protocol
|
|
796
|
+
NSURL *updateURL = _updatesInterface ? _updatesInterface.updateURL : nil;
|
|
778
797
|
NSString *projectUrl = @"";
|
|
779
798
|
if (_updatesInterface) {
|
|
780
|
-
projectUrl = [[self.manifest updatesInfo] valueForKey:@"url"];
|
|
799
|
+
projectUrl = [[self.manifest updatesInfo] valueForKey:@"url"] ?: @"";
|
|
800
|
+
if (projectUrl.length == 0 && updateURL) {
|
|
801
|
+
projectUrl = updateURL.absoluteString ?: @"";
|
|
802
|
+
}
|
|
781
803
|
}
|
|
782
804
|
|
|
783
|
-
NSURL *url = [NSURL URLWithString:projectUrl];
|
|
805
|
+
NSURL *url = projectUrl.length > 0 ? [NSURL URLWithString:projectUrl] : updateURL;
|
|
784
806
|
|
|
785
807
|
BOOL isModernManifestProtocol = [[url host] isEqualToString:@"u.expo.dev"] || [[url host] isEqualToString:@"staging-u.expo.dev"];
|
|
786
808
|
BOOL expoUpdatesInstalled = EXDevLauncherController.sharedInstance.updatesInterface != nil;
|
|
787
809
|
|
|
788
810
|
NSString *appId = [constants valueForKeyPath:@"manifest.extra.eas.projectId"] ?: [self.manifest easProjectId];
|
|
811
|
+
if (appId.length == 0 && updateURL) {
|
|
812
|
+
NSString *possibleAppId = updateURL.lastPathComponent ?: @"";
|
|
813
|
+
if (possibleAppId.length == 0 && updateURL.pathComponents.count > 0) {
|
|
814
|
+
possibleAppId = updateURL.pathComponents.lastObject ?: @"";
|
|
815
|
+
}
|
|
816
|
+
if (possibleAppId.length > 0 && ![possibleAppId isEqualToString:@"/"]) {
|
|
817
|
+
appId = possibleAppId;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
789
820
|
BOOL hasAppId = appId.length > 0;
|
|
790
821
|
|
|
791
822
|
BOOL usesEASUpdates = isModernManifestProtocol && expoUpdatesInstalled && hasAppId;
|
package/ios/SwiftUI/Data.swift
CHANGED
|
@@ -16,12 +16,14 @@ struct BuildInfo {
|
|
|
16
16
|
let runtimeVersion: String
|
|
17
17
|
let usesEASUpdates: Bool
|
|
18
18
|
let projectUrl: String?
|
|
19
|
+
let sdkVersion: String?
|
|
19
20
|
|
|
20
21
|
init(buildInfo: [AnyHashable: Any], updatesConfig: [AnyHashable: Any]) {
|
|
21
22
|
self.appId = (updatesConfig["appId"] as? String) ?? (buildInfo["appId"] as? String) ?? ""
|
|
22
23
|
self.runtimeVersion = (updatesConfig["runtimeVersion"] as? String) ?? (buildInfo["runtimeVersion"] as? String) ?? ""
|
|
23
24
|
self.usesEASUpdates = updatesConfig["usesEASUpdates"] as? Bool ?? false
|
|
24
25
|
self.projectUrl = updatesConfig["projectUrl"] as? String
|
|
26
|
+
self.sdkVersion = buildInfo["sdkVersion"] as? String
|
|
25
27
|
}
|
|
26
28
|
}
|
|
27
29
|
|
|
@@ -212,7 +212,6 @@ class DevLauncherViewModel: ObservableObject {
|
|
|
212
212
|
|
|
213
213
|
private func saveMenuPreference(key: String, value: Bool) {
|
|
214
214
|
UserDefaults.standard.set(value, forKey: key)
|
|
215
|
-
UserDefaults.standard.synchronize()
|
|
216
215
|
}
|
|
217
216
|
|
|
218
217
|
private func checkAuthenticationStatus() {
|
|
@@ -14,7 +14,7 @@ struct SettingsTabView: View {
|
|
|
14
14
|
private func createBuildInfoJSON() -> String {
|
|
15
15
|
let buildInfoDict: [String: Any] = [
|
|
16
16
|
"runtimeVersion": viewModel.buildInfo["runtimeVersion"] as? String ?? "",
|
|
17
|
-
"sdkVersion":
|
|
17
|
+
"sdkVersion": viewModel.structuredBuildInfo.sdkVersion ?? "",
|
|
18
18
|
"appName": viewModel.buildInfo["appName"] as? String ?? "",
|
|
19
19
|
"appVersion": viewModel.buildInfo["appVersion"] as? String ?? ""
|
|
20
20
|
]
|
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-20251015-a6a1272",
|
|
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.0.15-canary-20251015-a6a1272",
|
|
19
|
+
"expo-manifests": "1.0.9-canary-20251015-a6a1272"
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
|
-
"expo": "55.0.0-canary-
|
|
22
|
+
"expo": "55.0.0-canary-20251015-a6a1272"
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
#import <EXDevLauncher/EXDevLauncherController.h>
|
|
2
|
-
|
|
3
|
-
@import EXDevMenuInterface;
|
|
4
|
-
|
|
5
|
-
@interface EXDevLauncherDevMenuExtensions : NSObject <RCTBridgeModule>
|
|
6
|
-
|
|
7
|
-
@end
|
|
8
|
-
|
|
9
|
-
@implementation EXDevLauncherDevMenuExtensions
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
// Need to explicitly define `moduleName` here for dev menu to pick it up
|
|
13
|
-
RCT_EXTERN void RCTRegisterModule(Class);
|
|
14
|
-
|
|
15
|
-
+ (NSString *)moduleName
|
|
16
|
-
{
|
|
17
|
-
return @"EXDevLauncherExtension";
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
+ (void)load
|
|
21
|
-
{
|
|
22
|
-
RCTRegisterModule(self);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
+ (BOOL)requiresMainQueueSetup {
|
|
26
|
-
return YES;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
RCT_EXPORT_METHOD(navigateToLauncherAsync:(RCTPromiseResolveBlock)resolve
|
|
30
|
-
rejecter:(RCTPromiseRejectBlock)reject)
|
|
31
|
-
{
|
|
32
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
33
|
-
[[EXDevLauncherController sharedInstance] navigateToLauncher];
|
|
34
|
-
});
|
|
35
|
-
resolve(nil);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
@end
|