expo-dev-launcher 6.1.0-canary-20250919-7a31b96 → 6.1.0-canary-20250930-9dc59d3
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 +4 -0
- package/android/build.gradle +14 -14
- package/android/src/debug/java/expo/modules/devlauncher/DevLauncherController.kt +10 -1
- package/android/src/debug/java/expo/modules/devlauncher/compose/DevLauncherBottomTabsNavigator.kt +83 -0
- package/android/src/debug/java/expo/modules/devlauncher/compose/primitives/CircularProgressBar.kt +3 -2
- package/android/src/debug/java/expo/modules/devlauncher/compose/ui/ServerUrlInput.kt +18 -10
- package/android/src/debug/java/expo/modules/devlauncher/compose/utils/UrlUtils.kt +23 -4
- package/ios/EXDevLauncherController.h +1 -1
- package/ios/EXDevLauncherController.m +3 -5
- package/ios/SwiftUI/DevServersView.swift +21 -13
- package/ios/SwiftUI/HomeTabView.swift +3 -0
- package/ios/SwiftUI/SettingsTabView.swift +3 -0
- package/ios/SwiftUI/UpdatesTab/UpdatesTabView.swift +3 -0
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -7,9 +7,13 @@
|
|
|
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
|
+
- [Android] Adds loading state when connecting to a development server. ([#39873](https://github.com/expo/expo/pull/39873) by [@lukmccall](https://github.com/lukmccall))
|
|
10
11
|
|
|
11
12
|
### 🐛 Bug fixes
|
|
12
13
|
|
|
14
|
+
- [expo-dev-launcher] Fix manual URL entry: decode percent-encoded URLs, enable return key submit, and support dark mode text. ([#39840](https://github.com/expo/expo/pull/39840) by [@blazejkustra](https://github.com/blazejkustra))
|
|
15
|
+
- [iOS] Adjust tvOS colors. ([#40006](https://github.com/expo/expo/pull/40006) by [@douglowder](https://github.com/douglowder))
|
|
16
|
+
|
|
13
17
|
### 💡 Others
|
|
14
18
|
|
|
15
19
|
- [Android] Migrated from `kotlinOptions` to `compilerOptions` DSL. ([#39794](https://github.com/expo/expo/pull/39794) by [@huextrat](https://github.com/huextrat))
|
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-20250930-9dc59d3"
|
|
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-20250930-9dc59d3"
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
buildTypes {
|
|
@@ -75,17 +75,17 @@ dependencies {
|
|
|
75
75
|
|
|
76
76
|
implementation 'commons-io:commons-io:2.6'
|
|
77
77
|
|
|
78
|
-
implementation 'com.squareup.okhttp3:okhttp:
|
|
79
|
-
implementation 'com.google.code.gson:gson:2.
|
|
78
|
+
implementation 'com.squareup.okhttp3:okhttp:4.9.2'
|
|
79
|
+
implementation 'com.google.code.gson:gson:2.13.2'
|
|
80
80
|
|
|
81
81
|
// Fixes
|
|
82
82
|
// Cannot access 'androidx....' which is a supertype of 'expo.modules.devmenu.DevMenuActivity'.
|
|
83
83
|
// Check your module classpath for missing or conflicting dependencies
|
|
84
|
-
api "androidx.appcompat:appcompat:1.1
|
|
84
|
+
api "androidx.appcompat:appcompat:1.7.1"
|
|
85
85
|
api "androidx.lifecycle:lifecycle-extensions:2.2.0"
|
|
86
86
|
|
|
87
|
-
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.
|
|
88
|
-
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.
|
|
87
|
+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
|
|
88
|
+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2")
|
|
89
89
|
implementation "org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}"
|
|
90
90
|
|
|
91
91
|
api project.dependencies.platform("io.insert-koin:koin-bom:3.5.6")
|
|
@@ -96,7 +96,7 @@ dependencies {
|
|
|
96
96
|
implementation "androidx.compose.foundation:foundation-android:$composeVersion"
|
|
97
97
|
implementation "androidx.compose.ui:ui:$composeVersion"
|
|
98
98
|
implementation "androidx.compose.ui:ui-tooling:$composeVersion"
|
|
99
|
-
implementation "androidx.navigation:navigation-compose:2.9.
|
|
99
|
+
implementation "androidx.navigation:navigation-compose:2.9.4"
|
|
100
100
|
implementation "com.google.android.gms:play-services-code-scanner:16.1.0"
|
|
101
101
|
implementation "com.google.mlkit:barcode-scanning:17.3.0"
|
|
102
102
|
|
|
@@ -104,14 +104,14 @@ dependencies {
|
|
|
104
104
|
|
|
105
105
|
implementation("com.apollographql.apollo:apollo-runtime:4.3.1")
|
|
106
106
|
|
|
107
|
-
implementation("com.composables:core:1.
|
|
107
|
+
implementation("com.composables:core:1.43.1")
|
|
108
108
|
|
|
109
|
-
testImplementation 'androidx.test:core:1.
|
|
110
|
-
testImplementation 'androidx.test:core-ktx:1.
|
|
111
|
-
testImplementation "com.google.truth:truth:1.
|
|
112
|
-
testImplementation 'com.squareup.okhttp3:mockwebserver:4.
|
|
109
|
+
testImplementation 'androidx.test:core:1.7.0'
|
|
110
|
+
testImplementation 'androidx.test:core-ktx:1.7.0'
|
|
111
|
+
testImplementation "com.google.truth:truth:1.4.5"
|
|
112
|
+
testImplementation 'com.squareup.okhttp3:mockwebserver:4.9.2'
|
|
113
113
|
testImplementation "io.insert-koin:koin-test"
|
|
114
114
|
testImplementation "io.insert-koin:koin-test-junit4"
|
|
115
115
|
testImplementation 'io.mockk:mockk:1.12.3'
|
|
116
|
-
testImplementation "org.robolectric:robolectric:4.
|
|
116
|
+
testImplementation "org.robolectric:robolectric:4.16"
|
|
117
117
|
}
|
|
@@ -7,6 +7,8 @@ import android.net.Uri
|
|
|
7
7
|
import android.os.Bundle
|
|
8
8
|
import android.util.Log
|
|
9
9
|
import androidx.annotation.UiThread
|
|
10
|
+
import androidx.compose.runtime.State
|
|
11
|
+
import androidx.compose.runtime.mutableStateOf
|
|
10
12
|
import androidx.core.net.toUri
|
|
11
13
|
import com.facebook.react.ReactActivity
|
|
12
14
|
import com.facebook.react.ReactActivityDelegate
|
|
@@ -107,6 +109,10 @@ class DevLauncherController private constructor() :
|
|
|
107
109
|
|
|
108
110
|
private var appIsLoading = false
|
|
109
111
|
|
|
112
|
+
private val _isLoadingToBundler = mutableStateOf(false)
|
|
113
|
+
val isLoadingToBundler: State<Boolean>
|
|
114
|
+
get() = _isLoadingToBundler
|
|
115
|
+
|
|
110
116
|
private var networkInterceptor: DevLauncherNetworkInterceptor? = null
|
|
111
117
|
private var pendingIntentExtras: Bundle? = null
|
|
112
118
|
|
|
@@ -130,6 +136,7 @@ class DevLauncherController private constructor() :
|
|
|
130
136
|
return
|
|
131
137
|
}
|
|
132
138
|
appIsLoading = true
|
|
139
|
+
_isLoadingToBundler.value = true
|
|
133
140
|
}
|
|
134
141
|
|
|
135
142
|
try {
|
|
@@ -163,6 +170,7 @@ class DevLauncherController private constructor() :
|
|
|
163
170
|
lifecycle.addListener(appLoaderListener)
|
|
164
171
|
mode = Mode.APP
|
|
165
172
|
|
|
173
|
+
_isLoadingToBundler.value = false
|
|
166
174
|
// Note that `launch` method is a suspend one. So the execution will be stopped here until the method doesn't finish.
|
|
167
175
|
if (appLoader.launch(appIntent)) {
|
|
168
176
|
recentlyOpedAppsRegistry.appWasOpened(parsedUrl.toString(), devLauncherUrl.queryParams, manifest)
|
|
@@ -179,6 +187,7 @@ class DevLauncherController private constructor() :
|
|
|
179
187
|
} catch (e: Exception) {
|
|
180
188
|
synchronized(this) {
|
|
181
189
|
appIsLoading = false
|
|
190
|
+
_isLoadingToBundler.value = false
|
|
182
191
|
}
|
|
183
192
|
throw e
|
|
184
193
|
}
|
|
@@ -234,7 +243,7 @@ class DevLauncherController private constructor() :
|
|
|
234
243
|
// used by appetize for snack
|
|
235
244
|
if (intent.getBooleanExtra("EXDevMenuDisableAutoLaunch", false)) {
|
|
236
245
|
canLaunchDevMenuOnStart = false
|
|
237
|
-
this.devMenuManager.setCanLaunchDevMenuOnStart(
|
|
246
|
+
this.devMenuManager.setCanLaunchDevMenuOnStart(false)
|
|
238
247
|
}
|
|
239
248
|
|
|
240
249
|
if (!isDevLauncherUrl(uri)) {
|
package/android/src/debug/java/expo/modules/devlauncher/compose/DevLauncherBottomTabsNavigator.kt
CHANGED
|
@@ -1,14 +1,40 @@
|
|
|
1
1
|
package expo.modules.devlauncher.compose
|
|
2
2
|
|
|
3
3
|
import androidx.compose.animation.AnimatedContentTransitionScope
|
|
4
|
+
import androidx.compose.animation.AnimatedVisibility
|
|
4
5
|
import androidx.compose.animation.EnterTransition
|
|
5
6
|
import androidx.compose.animation.ExitTransition
|
|
6
7
|
import androidx.compose.animation.core.tween
|
|
8
|
+
import androidx.compose.animation.fadeIn
|
|
9
|
+
import androidx.compose.animation.fadeOut
|
|
10
|
+
import androidx.compose.animation.slideIn
|
|
11
|
+
import androidx.compose.animation.slideOut
|
|
12
|
+
import androidx.compose.foundation.background
|
|
13
|
+
import androidx.compose.foundation.clickable
|
|
14
|
+
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
15
|
+
import androidx.compose.foundation.layout.Arrangement
|
|
16
|
+
import androidx.compose.foundation.layout.Box
|
|
17
|
+
import androidx.compose.foundation.layout.Row
|
|
18
|
+
import androidx.compose.foundation.layout.fillMaxSize
|
|
19
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
20
|
+
import androidx.compose.foundation.layout.navigationBarsPadding
|
|
21
|
+
import androidx.compose.foundation.layout.padding
|
|
22
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
7
23
|
import androidx.compose.runtime.Composable
|
|
24
|
+
import androidx.compose.runtime.getValue
|
|
8
25
|
import androidx.compose.runtime.remember
|
|
26
|
+
import androidx.compose.ui.Alignment
|
|
27
|
+
import androidx.compose.ui.Modifier
|
|
28
|
+
import androidx.compose.ui.draw.clip
|
|
29
|
+
import androidx.compose.ui.graphics.Color
|
|
30
|
+
import androidx.compose.ui.text.font.FontWeight
|
|
31
|
+
import androidx.compose.ui.unit.IntOffset
|
|
32
|
+
import androidx.compose.ui.unit.dp
|
|
9
33
|
import androidx.navigation.compose.NavHost
|
|
10
34
|
import androidx.navigation.compose.composable
|
|
11
35
|
import androidx.navigation.compose.rememberNavController
|
|
36
|
+
import expo.modules.devlauncher.DevLauncherController
|
|
37
|
+
import expo.modules.devlauncher.compose.primitives.CircularProgressBar
|
|
12
38
|
import expo.modules.devlauncher.compose.primitives.DefaultScaffold
|
|
13
39
|
import expo.modules.devlauncher.compose.routes.CrashReport
|
|
14
40
|
import expo.modules.devlauncher.compose.routes.CrashReportRoute
|
|
@@ -21,6 +47,8 @@ import expo.modules.devlauncher.compose.routes.UpdatesRoute
|
|
|
21
47
|
import expo.modules.devlauncher.compose.ui.BottomTabBar
|
|
22
48
|
import expo.modules.devlauncher.compose.ui.Full
|
|
23
49
|
import expo.modules.devlauncher.compose.ui.rememberBottomSheetState
|
|
50
|
+
import expo.modules.devmenu.compose.newtheme.NewAppTheme
|
|
51
|
+
import expo.modules.devmenu.compose.primitives.NewText
|
|
24
52
|
|
|
25
53
|
@Composable
|
|
26
54
|
fun DevLauncherBottomTabsNavigator() {
|
|
@@ -106,4 +134,59 @@ fun DevLauncherBottomTabsNavigator() {
|
|
|
106
134
|
ProfileRoute(profileBottomSheetState)
|
|
107
135
|
|
|
108
136
|
DevelopmentServersRoute(developmentServersBottomSheetState)
|
|
137
|
+
|
|
138
|
+
val isVisible by (DevLauncherController.instance as DevLauncherController).isLoadingToBundler
|
|
139
|
+
|
|
140
|
+
AnimatedVisibility(
|
|
141
|
+
visible = isVisible,
|
|
142
|
+
enter = fadeIn(animationSpec = tween(durationMillis = 300)),
|
|
143
|
+
exit = fadeOut(animationSpec = tween(durationMillis = 300))
|
|
144
|
+
) {
|
|
145
|
+
Box(
|
|
146
|
+
modifier = Modifier
|
|
147
|
+
.fillMaxSize()
|
|
148
|
+
.background(Color.Black.copy(alpha = 0.6f))
|
|
149
|
+
.clickable(
|
|
150
|
+
interactionSource = remember { MutableInteractionSource() },
|
|
151
|
+
indication = null,
|
|
152
|
+
onClick = {
|
|
153
|
+
// Captures all clicks to block interaction with the underlying screen
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
) {
|
|
157
|
+
Row(
|
|
158
|
+
horizontalArrangement = Arrangement.SpaceBetween,
|
|
159
|
+
verticalAlignment = Alignment.CenterVertically,
|
|
160
|
+
modifier = Modifier
|
|
161
|
+
.align(Alignment.BottomStart)
|
|
162
|
+
.animateEnterExit(
|
|
163
|
+
enter = slideIn(
|
|
164
|
+
animationSpec = tween(durationMillis = 300),
|
|
165
|
+
initialOffset = { fullSize -> IntOffset(-fullSize.height, fullSize.width) }
|
|
166
|
+
),
|
|
167
|
+
exit = slideOut(
|
|
168
|
+
animationSpec = tween(durationMillis = 300),
|
|
169
|
+
targetOffset = { fullSize -> IntOffset(fullSize.height, fullSize.width) }
|
|
170
|
+
)
|
|
171
|
+
)
|
|
172
|
+
.clip(RoundedCornerShape(topStart = 8.dp, topEnd = 8.dp))
|
|
173
|
+
.background(NewAppTheme.colors.background.element)
|
|
174
|
+
.fillMaxWidth()
|
|
175
|
+
.navigationBarsPadding()
|
|
176
|
+
.padding(NewAppTheme.spacing.`3`)
|
|
177
|
+
.padding(top = NewAppTheme.spacing.`1`)
|
|
178
|
+
) {
|
|
179
|
+
NewText(
|
|
180
|
+
"Connecting to the development server...",
|
|
181
|
+
style = NewAppTheme.font.md.copy(
|
|
182
|
+
fontWeight = FontWeight.SemiBold
|
|
183
|
+
)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
CircularProgressBar(
|
|
187
|
+
size = 20.dp
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
109
192
|
}
|
package/android/src/debug/java/expo/modules/devlauncher/compose/primitives/CircularProgressBar.kt
CHANGED
|
@@ -25,13 +25,14 @@ import kotlin.time.Duration.Companion.seconds
|
|
|
25
25
|
|
|
26
26
|
@Composable
|
|
27
27
|
fun CircularProgressBar(
|
|
28
|
+
modifier: Modifier = Modifier,
|
|
28
29
|
startAngle: Float = 270f,
|
|
29
30
|
size: Dp = 96.dp,
|
|
30
31
|
strokeWidth: Dp = size / 8,
|
|
31
32
|
duration: Duration = 1.seconds
|
|
32
33
|
) {
|
|
33
34
|
val backgroundColor = NewAppTheme.pallet.gray.`3`
|
|
34
|
-
val progressColor = NewAppTheme.pallet.blue.`
|
|
35
|
+
val progressColor = NewAppTheme.pallet.blue.`8`
|
|
35
36
|
|
|
36
37
|
val transition = rememberInfiniteTransition(label = "infiniteSpinningTransition")
|
|
37
38
|
|
|
@@ -44,7 +45,7 @@ fun CircularProgressBar(
|
|
|
44
45
|
label = "Progress Animation"
|
|
45
46
|
)
|
|
46
47
|
|
|
47
|
-
Canvas(modifier = Modifier.size(size)) {
|
|
48
|
+
Canvas(modifier = Modifier.size(size).then(modifier)) {
|
|
48
49
|
val strokeWidthPx = strokeWidth.toPx()
|
|
49
50
|
val arcSize = size.toPx() - strokeWidthPx
|
|
50
51
|
drawArc(
|
|
@@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|
|
9
9
|
import androidx.compose.foundation.layout.padding
|
|
10
10
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
11
11
|
import androidx.compose.foundation.text.KeyboardOptions
|
|
12
|
+
import androidx.compose.foundation.text.KeyboardActions
|
|
12
13
|
import androidx.compose.runtime.Composable
|
|
13
14
|
import androidx.compose.runtime.getValue
|
|
14
15
|
import androidx.compose.runtime.mutableStateOf
|
|
@@ -26,7 +27,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|
|
26
27
|
import androidx.compose.ui.unit.dp
|
|
27
28
|
import com.composeunstyled.TextField
|
|
28
29
|
import com.composeunstyled.TextInput
|
|
29
|
-
import expo.modules.devlauncher.compose.utils.
|
|
30
|
+
import expo.modules.devlauncher.compose.utils.sanitizeUrlString
|
|
30
31
|
import expo.modules.devmenu.compose.newtheme.NewAppTheme
|
|
31
32
|
import expo.modules.devmenu.compose.primitives.NewText
|
|
32
33
|
|
|
@@ -37,6 +38,18 @@ fun ServerUrlInput(
|
|
|
37
38
|
var url by remember { mutableStateOf("") }
|
|
38
39
|
val context = LocalContext.current
|
|
39
40
|
|
|
41
|
+
fun connectToURL() {
|
|
42
|
+
val sanitizedURL = sanitizeUrlString(url)
|
|
43
|
+
if (sanitizedURL != null) {
|
|
44
|
+
openApp(sanitizedURL)
|
|
45
|
+
url = ""
|
|
46
|
+
} else {
|
|
47
|
+
Toast
|
|
48
|
+
.makeText(context, "Invalid URL", Toast.LENGTH_SHORT)
|
|
49
|
+
.show()
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
40
53
|
Column(
|
|
41
54
|
verticalArrangement = Arrangement.spacedBy(NewAppTheme.spacing.`2`)
|
|
42
55
|
) {
|
|
@@ -63,6 +76,9 @@ fun ServerUrlInput(
|
|
|
63
76
|
autoCorrectEnabled = false,
|
|
64
77
|
keyboardType = KeyboardType.Uri
|
|
65
78
|
),
|
|
79
|
+
keyboardActions = KeyboardActions(
|
|
80
|
+
onDone = { connectToURL() }
|
|
81
|
+
),
|
|
66
82
|
cursorBrush = SolidColor(NewAppTheme.colors.text.default.copy(alpha = 0.9f))
|
|
67
83
|
) {
|
|
68
84
|
TextInput(
|
|
@@ -85,15 +101,7 @@ fun ServerUrlInput(
|
|
|
85
101
|
textStyle = NewAppTheme.font.md.merge(
|
|
86
102
|
fontWeight = FontWeight.SemiBold
|
|
87
103
|
),
|
|
88
|
-
onClick = {
|
|
89
|
-
if (validateUrl(url)) {
|
|
90
|
-
openApp(url)
|
|
91
|
-
} else {
|
|
92
|
-
Toast
|
|
93
|
-
.makeText(context, "Invalid URL", Toast.LENGTH_SHORT)
|
|
94
|
-
.show()
|
|
95
|
-
}
|
|
96
|
-
}
|
|
104
|
+
onClick = { connectToURL() }
|
|
97
105
|
)
|
|
98
106
|
}
|
|
99
107
|
}
|
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
package expo.modules.devlauncher.compose.utils
|
|
2
2
|
|
|
3
3
|
import androidx.core.net.toUri
|
|
4
|
+
import java.net.URLDecoder
|
|
5
|
+
import java.nio.charset.StandardCharsets
|
|
6
|
+
|
|
7
|
+
fun sanitizeUrlString(urlString: String): String? {
|
|
8
|
+
var sanitizedUrl = urlString.trim()
|
|
9
|
+
|
|
10
|
+
val decodedUrl = try {
|
|
11
|
+
URLDecoder.decode(sanitizedUrl, StandardCharsets.UTF_8.toString())
|
|
12
|
+
} catch (_: Exception) {
|
|
13
|
+
sanitizedUrl
|
|
14
|
+
}
|
|
15
|
+
sanitizedUrl = decodedUrl
|
|
16
|
+
|
|
17
|
+
if (!sanitizedUrl.contains("://")) {
|
|
18
|
+
sanitizedUrl = "http://$sanitizedUrl"
|
|
19
|
+
}
|
|
4
20
|
|
|
5
|
-
fun validateUrl(url: String): Boolean {
|
|
6
21
|
return try {
|
|
7
|
-
val uri =
|
|
8
|
-
uri.scheme != null && uri.host != null
|
|
22
|
+
val uri = sanitizedUrl.toUri()
|
|
23
|
+
if (uri.scheme != null && uri.host != null) {
|
|
24
|
+
sanitizedUrl
|
|
25
|
+
} else {
|
|
26
|
+
null
|
|
27
|
+
}
|
|
9
28
|
} catch (_: Throwable) {
|
|
10
|
-
|
|
29
|
+
null
|
|
11
30
|
}
|
|
12
31
|
}
|
|
@@ -50,7 +50,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
50
50
|
|
|
51
51
|
+ (instancetype)sharedInstance;
|
|
52
52
|
|
|
53
|
-
- (void)startWithWindow:(UIWindow *)window
|
|
53
|
+
- (void)startWithWindow:(UIWindow *)window;
|
|
54
54
|
|
|
55
55
|
- (void)autoSetupPrepare:(id<EXDevLauncherControllerDelegate>)delegate launchOptions:(NSDictionary * _Nullable)launchOptions;
|
|
56
56
|
|
|
@@ -252,15 +252,13 @@
|
|
|
252
252
|
return _errorManager;
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
- (void)startWithWindow:(UIWindow *)window
|
|
255
|
+
- (void)startWithWindow:(UIWindow *)window
|
|
256
256
|
{
|
|
257
257
|
_isStarted = YES;
|
|
258
|
-
_delegate = delegate;
|
|
259
|
-
_launchOptions = launchOptions;
|
|
260
258
|
_window = window;
|
|
261
259
|
EXDevLauncherUncaughtExceptionHandler.isInstalled = true;
|
|
262
260
|
|
|
263
|
-
if (
|
|
261
|
+
if (_launchOptions[UIApplicationLaunchOptionsURLKey]) {
|
|
264
262
|
// For deeplink launch, we need the keyWindow for expo-splash-screen to setup correctly.
|
|
265
263
|
[_window makeKeyWindow];
|
|
266
264
|
return;
|
|
@@ -310,7 +308,7 @@
|
|
|
310
308
|
- (void)autoSetupStart:(UIWindow *)window
|
|
311
309
|
{
|
|
312
310
|
if (_delegate != nil) {
|
|
313
|
-
[self startWithWindow:window
|
|
311
|
+
[self startWithWindow:window];
|
|
314
312
|
} else {
|
|
315
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];
|
|
316
314
|
}
|
|
@@ -8,6 +8,10 @@ import Combine
|
|
|
8
8
|
private func sanitizeUrlString(_ urlString: String) -> String? {
|
|
9
9
|
var sanitizedUrl = urlString.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
10
10
|
|
|
11
|
+
if let decodedUrl = sanitizedUrl.removingPercentEncoding {
|
|
12
|
+
sanitizedUrl = decodedUrl
|
|
13
|
+
}
|
|
14
|
+
|
|
11
15
|
if !sanitizedUrl.contains("://") {
|
|
12
16
|
sanitizedUrl = "http://" + sanitizedUrl
|
|
13
17
|
}
|
|
@@ -26,6 +30,19 @@ struct DevServersView: View {
|
|
|
26
30
|
@State private var urlText = ""
|
|
27
31
|
@State private var cancellables = Set<AnyCancellable>()
|
|
28
32
|
|
|
33
|
+
private func connectToURL() {
|
|
34
|
+
if !urlText.isEmpty {
|
|
35
|
+
let sanitizedURL = sanitizeUrlString(urlText)
|
|
36
|
+
if let validURL = sanitizedURL {
|
|
37
|
+
viewModel.openApp(url: validURL)
|
|
38
|
+
withAnimation(.easeInOut(duration: 0.3)) {
|
|
39
|
+
showingURLInput = false
|
|
40
|
+
}
|
|
41
|
+
urlText = ""
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
29
46
|
var body: some View {
|
|
30
47
|
VStack(alignment: .leading, spacing: 12) {
|
|
31
48
|
header
|
|
@@ -81,7 +98,8 @@ struct DevServersView: View {
|
|
|
81
98
|
.disableAutocorrection(true)
|
|
82
99
|
.padding(.horizontal, 16)
|
|
83
100
|
.padding(.vertical, 12)
|
|
84
|
-
.foregroundColor(.
|
|
101
|
+
.foregroundColor(.primary)
|
|
102
|
+
.onSubmit(connectToURL)
|
|
85
103
|
#if !os(tvOS)
|
|
86
104
|
.overlay(
|
|
87
105
|
RoundedRectangle(cornerRadius: 5)
|
|
@@ -114,6 +132,7 @@ struct DevServersView: View {
|
|
|
114
132
|
} label: {
|
|
115
133
|
Text("info".uppercased())
|
|
116
134
|
#if os(tvOS)
|
|
135
|
+
.foregroundColor(.primary)
|
|
117
136
|
.font(.system(size: 24))
|
|
118
137
|
#else
|
|
119
138
|
.font(.system(size: 12))
|
|
@@ -124,18 +143,7 @@ struct DevServersView: View {
|
|
|
124
143
|
}
|
|
125
144
|
|
|
126
145
|
private var connectButton: some View {
|
|
127
|
-
Button {
|
|
128
|
-
if !urlText.isEmpty {
|
|
129
|
-
let sanitizedURL = sanitizeUrlString(urlText)
|
|
130
|
-
if let validURL = sanitizedURL {
|
|
131
|
-
viewModel.openApp(url: validURL)
|
|
132
|
-
withAnimation(.easeInOut(duration: 0.3)) {
|
|
133
|
-
showingURLInput = false
|
|
134
|
-
}
|
|
135
|
-
urlText = ""
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
} label: {
|
|
146
|
+
Button(action: connectToURL) {
|
|
139
147
|
Text("Connect")
|
|
140
148
|
.font(.headline)
|
|
141
149
|
.foregroundColor(.white)
|
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-20250930-9dc59d3",
|
|
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.13-canary-20250930-9dc59d3",
|
|
19
|
+
"expo-manifests": "1.0.9-canary-20250930-9dc59d3"
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
|
-
"expo": "55.0.0-canary-
|
|
22
|
+
"expo": "55.0.0-canary-20250930-9dc59d3"
|
|
23
23
|
}
|
|
24
24
|
}
|