expo-dev-launcher 55.0.27 → 55.0.29

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.
Files changed (24) hide show
  1. package/CHANGELOG.md +18 -1
  2. package/android/build.gradle +2 -2
  3. package/android/src/debug/java/com/facebook/react/devsupport/DevLauncherDevServerHelper.kt +6 -8
  4. package/android/src/debug/java/expo/modules/devlauncher/DevLauncherController.kt +2 -2
  5. package/android/src/debug/java/expo/modules/devlauncher/compose/routes/Profile.kt +1 -1
  6. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/BranchesScreen.kt +6 -6
  7. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/ErrorScreen.kt +2 -2
  8. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/SettingsScreen.kt +1 -1
  9. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/AccountSelector.kt +1 -1
  10. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/SignUp.kt +2 -2
  11. package/android/src/debug/java/expo/modules/devlauncher/services/HttpClientService.kt +2 -2
  12. package/ios/EXDevLauncherController.h +1 -0
  13. package/ios/EXDevLauncherController.m +11 -1
  14. package/ios/Errors/EXDevLauncherUncaughtExceptionHandler.swift +1 -1
  15. package/ios/ReactDelegateHandler/ExpoDevLauncherReactDelegateHandler.swift +1 -0
  16. package/ios/SwiftUI/AccountSheet.swift +3 -3
  17. package/ios/SwiftUI/ErrorView.swift +1 -1
  18. package/ios/SwiftUI/HomeTabView.swift +0 -4
  19. package/ios/SwiftUI/SettingsTabView.swift +1 -6
  20. package/ios/SwiftUI/UpdatesTab/NotSignedInView.swift +3 -7
  21. package/ios/SwiftUI/UpdatesTab/NotUsingUpdatesView.swift +0 -4
  22. package/ios/SwiftUI/UpdatesTab/UpdatesListView.swift +0 -4
  23. package/ios/SwiftUI/UpdatesTab/UpdatesTabView.swift +0 -4
  24. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -10,6 +10,23 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 55.0.29 — 2026-04-21
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - [Android] Use `OkHttpClientProvider` instead of bare `OkHttpClient` so custom interceptors are applied. ([#44798](https://github.com/expo/expo/pull/44798) by [@fabriziocucci](https://github.com/fabriziocucci))
18
+ - [iOS] Fix JSI crash (`EXC_BAD_ACCESS` in `jsi::Pointer::~Pointer`) when an embedded `main.jsbundle` and dev-launcher race on startup. ([#44799](https://github.com/expo/expo/pull/44799) by [@fabriziocucci](https://github.com/fabriziocucci))
19
+
20
+ ### 💡 Others
21
+
22
+ - Align dev launcher labels across iOS and Android. ([#44720](https://github.com/expo/expo/pull/44720) by [@vonovak](https://github.com/vonovak))
23
+
24
+ ## 55.0.28 — 2026-04-11
25
+
26
+ ### 🐛 Bug fixes
27
+
28
+ - [iOS] revert Fixed deep links not reaching the app because `EXDevLauncherController.isAppRunning` always returned `false`. ([#44609](https://github.com/expo/expo/pull/44609) by [@vonovak](https://github.com/vonovak))
29
+
13
30
  ## 55.0.27 — 2026-04-10
14
31
 
15
32
  _This version does not introduce any user-facing changes._
@@ -18,7 +35,7 @@ _This version does not introduce any user-facing changes._
18
35
 
19
36
  ### 🐛 Bug fixes
20
37
 
21
- - [iOS] Fixed deep links not reaching the app because `EXDevLauncherController.isAppRunning` always returned `false`. ([#44609](https://github.com/expo/expo/pull/44609) by [@vonovak](https://github.com/vonovak))
38
+ ~~- [iOS] Fixed deep links not reaching the app because `EXDevLauncherController.isAppRunning` always returned `false`. ([#44609](https://github.com/expo/expo/pull/44609) by [@vonovak](https://github.com/vonovak))~~
22
39
 
23
40
  ## 55.0.25 — 2026-04-09
24
41
 
@@ -26,13 +26,13 @@ expoModule {
26
26
  }
27
27
 
28
28
  group = "host.exp.exponent"
29
- version = "55.0.27"
29
+ version = "55.0.29"
30
30
 
31
31
  android {
32
32
  namespace "expo.modules.devlauncher"
33
33
  defaultConfig {
34
34
  versionCode 9
35
- versionName "55.0.27"
35
+ versionName "55.0.29"
36
36
  }
37
37
 
38
38
  buildTypes {
@@ -2,13 +2,13 @@ package com.facebook.react.devsupport
2
2
 
3
3
  import android.content.Context
4
4
  import androidx.core.net.toUri
5
+ import com.facebook.react.modules.network.OkHttpClientProvider
5
6
  import com.facebook.react.devsupport.interfaces.PackagerStatusCallback
6
7
  import com.facebook.react.modules.debug.interfaces.DeveloperSettings
7
8
  import com.facebook.react.packagerconnection.PackagerConnectionSettings
8
9
  import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
9
10
  import okhttp3.Call
10
11
  import okhttp3.Callback
11
- import okhttp3.OkHttpClient
12
12
  import okhttp3.Request
13
13
  import okhttp3.Response
14
14
  import java.io.IOException
@@ -25,13 +25,11 @@ class DevLauncherDevServerHelper(
25
25
  packagerConnection: PackagerConnectionSettings
26
26
  ) : DevServerHelper(devSettings, context, packagerConnection) {
27
27
 
28
- private val httpClient: OkHttpClient by lazy {
29
- OkHttpClient.Builder()
30
- .connectTimeout(HTTP_CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS)
31
- .readTimeout(0, TimeUnit.MILLISECONDS)
32
- .writeTimeout(0, TimeUnit.MILLISECONDS)
33
- .build()
34
- }
28
+ private val httpClient = OkHttpClientProvider.getOkHttpClient().newBuilder()
29
+ .connectTimeout(HTTP_CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS)
30
+ .readTimeout(0, TimeUnit.MILLISECONDS)
31
+ .writeTimeout(0, TimeUnit.MILLISECONDS)
32
+ .build()
35
33
 
36
34
  override fun getDevServerBundleURL(jsModulePath: String): String {
37
35
  return controller?.manifest?.getBundleURL() ?: super.getDevServerBundleURL(jsModulePath)
@@ -16,6 +16,7 @@ import com.facebook.react.ReactApplication
16
16
  import com.facebook.react.ReactHost
17
17
  import com.facebook.react.ReactPackage
18
18
  import com.facebook.react.bridge.ReactContext
19
+ import com.facebook.react.modules.network.OkHttpClientProvider
19
20
  import expo.modules.devlauncher.helpers.DevLauncherInstallationIDHelper
20
21
  import expo.modules.devlauncher.helpers.DevLauncherMetadataHelper
21
22
  import expo.modules.devlauncher.helpers.DevLauncherUrl
@@ -46,7 +47,6 @@ import expo.modules.updatesinterface.UpdatesDevLauncherInterface
46
47
  import kotlinx.coroutines.CoroutineScope
47
48
  import kotlinx.coroutines.Dispatchers
48
49
  import kotlinx.coroutines.launch
49
- import okhttp3.OkHttpClient
50
50
 
51
51
  private const val NEW_ACTIVITY_FLAGS = Intent.FLAG_ACTIVITY_NEW_TASK or
52
52
  Intent.FLAG_ACTIVITY_CLEAR_TASK or
@@ -64,7 +64,7 @@ class DevLauncherController private constructor(
64
64
  val nullableContext: Context?
65
65
  get() = contextHolder.get()
66
66
 
67
- val httpClient by lazy { OkHttpClient() }
67
+ val httpClient by lazy { OkHttpClientProvider.getOkHttpClient() }
68
68
  val lifecycle by lazy { DevLauncherLifecycle() }
69
69
  private val pendingIntentRegistry by lazy { DevLauncherIntentRegistry() }
70
70
  private val installationIDHelper by lazy { DevLauncherInstallationIDHelper() }
@@ -121,7 +121,7 @@ private fun LoggedOut(authLauncher: ManagedActivityResultLauncher<AuthRequestTyp
121
121
  }
122
122
 
123
123
  NewText(
124
- "Login or create an account to view local\ndevelopment servers and more",
124
+ "Log in or create an account to view local\ndevelopment servers and more",
125
125
  style = NewAppTheme.font.sm.merge(
126
126
  lineHeight = 19.sp,
127
127
  textAlign = TextAlign.Center
@@ -79,7 +79,7 @@ private fun BranchBadge(
79
79
  }
80
80
 
81
81
  @Composable
82
- private fun NeedToSingInComponent(
82
+ private fun NeedToSignInComponent(
83
83
  onProfileClick: () -> Unit = {}
84
84
  ) {
85
85
  Box(
@@ -93,7 +93,7 @@ private fun NeedToSingInComponent(
93
93
  ) {
94
94
  Icon(
95
95
  painter = painterResource(R.drawable.log_in),
96
- contentDescription = "Sign In Icon",
96
+ contentDescription = "Log in icon",
97
97
  tint = NewAppTheme.colors.icon.info
98
98
  )
99
99
 
@@ -103,7 +103,7 @@ private fun NeedToSingInComponent(
103
103
  modifier = Modifier.fillMaxWidth()
104
104
  ) {
105
105
  NewText(
106
- "Sign in to view updates",
106
+ "Log in to view updates",
107
107
  style = NewAppTheme.font.lg.merge(
108
108
  fontWeight = FontWeight.SemiBold,
109
109
  lineHeight = 20.sp,
@@ -112,7 +112,7 @@ private fun NeedToSingInComponent(
112
112
  )
113
113
 
114
114
  NewText(
115
- "Sign in to your Expo account to see available\nEAS updates for this project.",
115
+ "Log in to your Expo account to see available\nupdates for this project.",
116
116
  style = NewAppTheme.font.sm.merge(
117
117
  lineHeight = 19.6.sp,
118
118
  textAlign = TextAlign.Center
@@ -122,7 +122,7 @@ private fun NeedToSingInComponent(
122
122
  }
123
123
 
124
124
  ActionButton(
125
- "Sign In",
125
+ "Log in",
126
126
  foreground = Color.White,
127
127
  background = Color.Black,
128
128
  fill = false,
@@ -154,7 +154,7 @@ fun BranchesScreen(
154
154
  }
155
155
 
156
156
  if (needToSignIn) {
157
- NeedToSingInComponent(onProfileClick)
157
+ NeedToSignInComponent(onProfileClick)
158
158
  return@Column
159
159
  }
160
160
 
@@ -43,7 +43,7 @@ fun ErrorScreen(
43
43
  )
44
44
  )
45
45
 
46
- NewText("This development build encountered the following error.")
46
+ NewText("This development build encountered the following error:")
47
47
  }
48
48
 
49
49
  StackTrace(
@@ -70,7 +70,7 @@ fun ErrorScreen(
70
70
  )
71
71
 
72
72
  ActionButton(
73
- "Go To Home",
73
+ "Go home",
74
74
  foreground = NewAppTheme.colors.buttons.secondary.foreground,
75
75
  background = NewAppTheme.colors.buttons.secondary.background,
76
76
  modifier = Modifier.padding(vertical = NewAppTheme.spacing.`2`),
@@ -152,7 +152,7 @@ private fun MenuGesturesSection(state: SettingsState, onAction: (SettingsAction)
152
152
  },
153
153
  content = {
154
154
  NewText(
155
- text = "3 fingers long press"
155
+ text = "Three-finger long-press"
156
156
  )
157
157
  },
158
158
  rightComponent = {
@@ -72,7 +72,7 @@ fun AccountSelector(
72
72
  }
73
73
 
74
74
  ActionButton(
75
- "Log Out",
75
+ "Log out",
76
76
  foreground = Color.White,
77
77
  background = Color.Black,
78
78
  modifier = Modifier.padding(NewAppTheme.spacing.`3`),
@@ -18,7 +18,7 @@ fun SignUp(
18
18
  verticalArrangement = Arrangement.spacedBy(NewAppTheme.spacing.`2`)
19
19
  ) {
20
20
  ActionButton(
21
- "Login",
21
+ "Log in",
22
22
  foreground = Color.White,
23
23
  background = Color.Black,
24
24
  modifier = Modifier.padding(NewAppTheme.spacing.`3`),
@@ -26,7 +26,7 @@ fun SignUp(
26
26
  )
27
27
 
28
28
  ActionButton(
29
- "Sign Up",
29
+ "Sign up",
30
30
  foreground = NewAppTheme.colors.text.secondary,
31
31
  background = NewAppTheme.colors.background.element,
32
32
  modifier = Modifier.padding(NewAppTheme.spacing.`3`),
@@ -1,8 +1,8 @@
1
1
  package expo.modules.devlauncher.services
2
2
 
3
3
  import androidx.core.net.toUri
4
+ import com.facebook.react.modules.network.OkHttpClientProvider
4
5
  import expo.modules.devlauncher.helpers.await
5
- import okhttp3.OkHttpClient
6
6
  import okhttp3.Request
7
7
  import org.json.JSONObject
8
8
 
@@ -32,7 +32,7 @@ data class DevelopmentSession(
32
32
  class HttpClientService() {
33
33
  private var currentSession: String? = null
34
34
 
35
- val httpClient = OkHttpClient.Builder()
35
+ val httpClient = OkHttpClientProvider.getOkHttpClient().newBuilder()
36
36
  .addInterceptor { chain ->
37
37
  val originalRequest = chain.request()
38
38
  val session = currentSession
@@ -41,6 +41,7 @@ NS_ASSUME_NONNULL_BEGIN
41
41
 
42
42
  @interface EXDevLauncherController : RCTDefaultReactNativeFactoryDelegate <RCTBridgeDelegate, EXUpdatesExternalInterfaceDelegate>
43
43
 
44
+ @property (nonatomic, weak) RCTBridge * _Nullable appBridge;
44
45
  @property (nonatomic, weak) EXAppContext * _Nullable appContext;
45
46
  @property (nonatomic, strong) EXDevLauncherPendingDeepLinkRegistry *pendingDeepLinkRegistry;
46
47
  @property (nonatomic, strong) EXDevLauncherRecentlyOpenedAppsRegistry *recentlyOpenedAppsRegistry;
@@ -196,6 +196,11 @@ static const NSTimeInterval EXDevLauncherDefaultRequestTimeout = 10.0;
196
196
  });
197
197
  };
198
198
 
199
+ if ([[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"] != nil) {
200
+ [self navigateToLauncher];
201
+ return;
202
+ }
203
+
199
204
  #if TARGET_OS_SIMULATOR
200
205
  BOOL hasGrantedNetworkPermission = YES;
201
206
  #else
@@ -228,6 +233,7 @@ static const NSTimeInterval EXDevLauncherDefaultRequestTimeout = 10.0;
228
233
  {
229
234
  NSAssert([NSThread isMainThread], @"This function must be called on main thread");
230
235
 
236
+ [_appBridge invalidate];
231
237
  [self invalidateDevMenuApp];
232
238
 
233
239
  self.networkInterceptor = nil;
@@ -507,7 +513,11 @@ static const NSTimeInterval EXDevLauncherDefaultRequestTimeout = 10.0;
507
513
 
508
514
  - (BOOL)isAppRunning
509
515
  {
510
- return [self.delegate isReactInstanceValid];
516
+ if([_appBridge isProxy]){
517
+ return [self.delegate isReactInstanceValid];
518
+ }
519
+
520
+ return [_appBridge isValid];
511
521
  }
512
522
 
513
523
  #if !TARGET_OS_OSX
@@ -54,7 +54,7 @@ public class EXDevLauncherUncaughtExceptionHandler: NSObject {
54
54
  // URL structure replicates
55
55
  // https://github.com/facebook/react-native/blob/0.69-stable/Libraries/Utilities/HMRClient.js#L164
56
56
  // but URLSessionWebSocketTask will crash if the scheme is not `ws` or `wss`
57
- guard let appUrl = controller.sourceUrl() else {
57
+ guard let appUrl = controller.appBridge?.bundleURL else {
58
58
  return nil
59
59
  }
60
60
  guard let socketUrl = URL.init(string: "hot", relativeTo: appUrl) else {
@@ -115,6 +115,7 @@ public class ExpoDevLauncherReactDelegateHandler: ExpoReactDelegateHandler, EXDe
115
115
  initialProps: self.rootViewInitialProperties,
116
116
  launchOptions: developmentClientController.getLaunchOptions()
117
117
  )
118
+ developmentClientController.appBridge = RCTBridge.current()
118
119
 
119
120
  let targetVC: UIViewController
120
121
  #if !os(macOS)
@@ -80,7 +80,7 @@ struct AccountSheet: View {
80
80
  viewModel.signOut()
81
81
  }
82
82
  label: {
83
- Text("Logout")
83
+ Text("Log out")
84
84
  .font(.headline)
85
85
  .fontWeight(.bold)
86
86
  .foregroundColor(.white)
@@ -126,7 +126,7 @@ struct AccountSheet: View {
126
126
  .transition(.scale.combined(with: .opacity))
127
127
  }
128
128
 
129
- Text("Log In")
129
+ Text("Log in")
130
130
  .font(.headline)
131
131
  .fontWeight(.semibold)
132
132
  }
@@ -147,7 +147,7 @@ struct AccountSheet: View {
147
147
  }
148
148
  }
149
149
  label: {
150
- Text("Sign Up")
150
+ Text("Sign up")
151
151
  .font(.headline)
152
152
  .fontWeight(.semibold)
153
153
  .foregroundColor(.black.opacity(0.7))
@@ -68,7 +68,7 @@ struct ErrorView: View {
68
68
  }
69
69
 
70
70
  Button(action: onGoHome) {
71
- Text("Go to home")
71
+ Text("Go home")
72
72
  .font(.headline)
73
73
  .foregroundColor(.black)
74
74
  .frame(maxWidth: .infinity)
@@ -116,8 +116,4 @@ struct NetworkPermissionsBanner: View {
116
116
  .cornerRadius(18)
117
117
  }
118
118
  }
119
-
120
- #Preview {
121
- HomeTabView()
122
- }
123
119
  // swiftlint:enable closure_body_length
@@ -119,7 +119,7 @@ struct SettingsTabView: View {
119
119
  .resizable()
120
120
  .frame(width: 24, height: 24)
121
121
  .opacity(0.6)
122
- Toggle("Shake Device", isOn: $viewModel.shakeDevice)
122
+ Toggle("Shake device", isOn: $viewModel.shakeDevice)
123
123
  }
124
124
  .padding()
125
125
 
@@ -271,8 +271,3 @@ struct SettingsTabView: View {
271
271
  }
272
272
  #endif
273
273
  }
274
-
275
- #Preview {
276
- SettingsTabView()
277
- .environmentObject(DevLauncherViewModel())
278
- }
@@ -13,12 +13,12 @@ struct NotSignedInView: View {
13
13
  .foregroundColor(.blue)
14
14
 
15
15
  VStack(spacing: 8) {
16
- Text("Sign in to view updates")
16
+ Text("Log in to view updates")
17
17
  .font(.title3)
18
18
  .fontWeight(.semibold)
19
19
  .multilineTextAlignment(.center)
20
20
 
21
- Text("Sign in to your Expo account to see available EAS Updates for this project.")
21
+ Text("Log in to your Expo account to see available EAS updates for this project.")
22
22
  .font(.system(size: 14))
23
23
  .multilineTextAlignment(.center)
24
24
  .foregroundStyle(.secondary)
@@ -27,7 +27,7 @@ struct NotSignedInView: View {
27
27
  Button {
28
28
  navigation.showUserProfile()
29
29
  } label: {
30
- Text("Sign in")
30
+ Text("Log in")
31
31
  .padding(.horizontal, 16)
32
32
  .padding(.vertical, 8)
33
33
  .background(Color.black)
@@ -39,7 +39,3 @@ struct NotSignedInView: View {
39
39
  .frame(maxWidth: .infinity, maxHeight: .infinity)
40
40
  }
41
41
  }
42
-
43
- #Preview {
44
- NotSignedInView()
45
- }
@@ -32,7 +32,3 @@ struct NotUsingUpdatesView: View {
32
32
  }
33
33
  }
34
34
  }
35
-
36
- #Preview {
37
- NotUsingUpdatesView()
38
- }
@@ -190,8 +190,4 @@ struct UpdatesListView: View {
190
190
  }
191
191
  }
192
192
  }
193
-
194
- #Preview {
195
- UpdatesListView()
196
- }
197
193
  // swiftlint:enable closure_body_length
@@ -25,7 +25,3 @@ struct UpdatesTabView: View {
25
25
  #endif
26
26
  }
27
27
  }
28
-
29
- #Preview {
30
- UpdatesTabView()
31
- }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "expo-dev-launcher",
3
3
  "title": "Expo Development Launcher",
4
- "version": "55.0.27",
4
+ "version": "55.0.29",
5
5
  "description": "Pre-release version of the Expo development launcher package for testing.",
6
6
  "repository": {
7
7
  "type": "git",
@@ -16,11 +16,11 @@
16
16
  "homepage": "https://docs.expo.dev",
17
17
  "dependencies": {
18
18
  "@expo/schema-utils": "^55.0.3",
19
- "expo-dev-menu": "55.0.22",
20
- "expo-manifests": "~55.0.15"
19
+ "expo-dev-menu": "55.0.24",
20
+ "expo-manifests": "~55.0.16"
21
21
  },
22
22
  "peerDependencies": {
23
23
  "expo": "*"
24
24
  },
25
- "gitHead": "26e10e1b1a947cf16202e7ae70a65b70a1c6ed35"
25
+ "gitHead": "e37e614d97c3ca53f16b91609a787675d044c284"
26
26
  }