Do I need a separate expo-dev-client per app?

I have an app that mostly differs in terms of some skinning and base API endpoint. So I have a single code base with a dynamic app.config.js where I can simply swap a couple of values to switch between apps.

I recently added @config-plugins/android-jsc-intl - npm since I wanted to leverage date-fns which requires Intl.

I can probably tweak it so it only builds if app.config.js / package.json gets updated later to save time. What I was wondering is whether I need to create a new dev-client for each app ID though?

No, you should be able to create one dev client with the superset of dependencies for both of your apps. The only issue is if you want to test something that depends on the ios.bundleId / android.package or e.g. if some dependencies in one app require static compilation while some dependencies from the other app require dynamic compilation or otherwise have some compatibility problems…

not the ios.bundleId nor android.package but I do depend on the a value I would read from .env file using dotenv package that specifies the theme ID to load.

In that case you might have to adjust how you do things in the dev client vs. the production app or maybe provide a different way to set the theme in the dev client. Imagine that Expo Go has all of your dependencies built in. Would you be able to use Expo Go for developing both apps? It’s the same for the dev client.

e.g. maybe you could extend the dev menu to allow you to choose a theme ID?

1 Like

We’ve actually been using Expo Go for both apps for a while, but the introduction of the use of Intl forced the need for a custom dev client build which is not be done per Add Intl support | Voters | Expo (canny.io)

I am sitll unable to get expo run:android working on Windows, but I was finally able to get it started on a mac.

However… it seems that the values from .env are not being passed on anymore. I have a value BASE_URL

import {
  BASE_URL,
} from '@env';

Which now yields undefined

But was working with expo start before I did run:android.

If you just want something like Expo Go, you do not need to use npx expo run:android. I personally avoid expo run because it basically turns your app into a bare app by running npx expo prebuild in the background after which I need to expo.fyi/prebuild-cleanup to get back to a managed app.

There is no need to run npx expo run:android. It would be useful if you are writing your own native code under the android directory or something like that. But for a workflow like you’re used to with Expo Go, you build a dev client and use that like you would use Expo Go.

So I would suggest reverting your app to the managed workflow (unless you actually need the Bare workflow) and building the dev client. Then run npx expo start and open the dev client on your device/Simulator/emulator.

I do wish I can TBH. But the decision made in Add Intl support | Voters | Expo (canny.io) to require the users to create a dev-client rather than adding Intl (which kinda seems fundamental) especially if you have to deal with date-fns. I guess we can always try to put back momentjs as a work around.

Still though I am unable to get the dev-client to properly work

When I say “revert to the managed workflow” I mean basically delete ios and android (and optionally revert the other changes to package.json etc. made by npx expo prebuild.)

Installing Intl and building a dev client is still the managed workflow as long as you do not have android and ios directories.

I think they didn’t want to add Intl support directly to Expo Go because of the size.

What happens if you do the following:

  • Create a new app (npx create-expo-app trajanoDevClient or yarn create expo-app trajanoDevClient)
  • Install all the dependencies needed by both apps, including expo-dev-client
  • Update the app.json to include the config plugin needed for Intl support:
{
  "expo": {
[...]
    "plugins": ["@config-plugins/android-jsc-intl"]
  }
}
  • Do not run “npx expo run:android” or “npx expo prebuild
  • Don’t bother changing App.js
  • Run `eas build -p android --profile development

Does it build?

If so, try running it, and run npx expo start in this new app and see that you can connect.
Then, kill that, change to the directory containing one of your real apps and run npx expo start and try connecting to that one from the dev client.

Thanks I was actually thinking about that next because of what I saw. Let me try that out. So the idea is basically having a “dev-client” that’s just a “dev-client” with all the config stuff put in then start it up and plop the URL (since the link hooks won’t work as the app will not be listening to those URLs).

If that works well that would be something nice that’s documented explicitly (if it isn’t done yet).

Also if it does work, maybe we can have a “hook” to allow us to add additional app IDs that the dev client would listen to so the URLs would still work without manually copy and pasting.

UPDATE:
Also add any expo-* dependency from my original app + sentry-expo + @config-plugins/*

On Android you can scan the QR code printed by npx expo start or if you’re logged in to your Expo account on you computer and in the dev client you should be able to see the running dev servers in the dev client.

1 Like

Hmm no luck yet…

when I do http://192.168.0.19:19000/_expo/loading?platform=android

Error: Unable to resolve module ./_expo/loading from C:\dh\dh-mobile/.:

None of these files exist:
  * _expo\loading(.native|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx|.android.js|.native.js|.js|.android.jsx|.native.jsx|.jsx|.android.json|.native.json|.json|.android.svg|.native.svg|.svg)
  * _expo\loading\index(.native|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx|.android.js|.native.js|.js|.android.jsx|.native.jsx|.jsx|.android.json|.native.json|.json|.android.svg|.native.svg|.svg)
    at ModuleResolver.resolveDependency (C:\dh\dh-mobile\node_modules\metro\src\node-haste\DependencyGraph\ModuleResolution.js:136:15)
    at DependencyGraph.resolveDependency (C:\dh\dh-mobile\node_modules\metro\src\node-haste\DependencyGraph.js:231:43)
    at C:\dh\dh-mobile\node_modules\metro\src\lib\transformHelpers.js:129:24
    at Server._resolveRelativePath (C:\dh\dh-mobile\node_modules\metro\src\Server.js:1107:12)
    at Server.requestProcessor [as _processBundleRequest] (C:\dh\dh-mobile\node_modules\metro\src\Server.js:450:37)
    at Server._processRequest (C:\dh\dh-mobile\node_modules\metro\src\Server.js:406:9)

If I do exp://192.168.0.19:19000 or http://192.168.0.19:19000

Might just be missing something

Tried again
Slightly different


But just stuck there. Looking into logcat I see this…

2022-08-28 00:01:04.158 13560-22718/co.iblum.devclient I/okhttp.OkHttpClient: Callback failure for call to http://192.168.0.19:19000/...
2022-08-28 00:01:04.159 13560-22718/co.iblum.devclient I/okhttp.OkHttpClient: java.net.ProtocolException: Expected leading [0-9a-fA-F] character but was 0xd
2022-08-28 00:01:04.159 13560-22718/co.iblum.devclient I/okhttp.OkHttpClient:     at okhttp3.internal.http1.Http1ExchangeCodec$ChunkedSource.readChunkSize(Http1ExchangeCodec.kt:436)
2022-08-28 00:01:04.159 13560-22718/co.iblum.devclient I/okhttp.OkHttpClient:     at okhttp3.internal.http1.Http1ExchangeCodec$ChunkedSource.read(Http1ExchangeCodec.kt:408)
2022-08-28 00:01:04.159 13560-22718/co.iblum.devclient I/okhttp.OkHttpClient:     at okhttp3.internal.connection.Exchange$ResponseBodySource.read(Exchange.kt:276)
2022-08-28 00:01:04.159 13560-22718/co.iblum.devclient I/okhttp.OkHttpClient:     at okio.RealBufferedSource.read(RealBufferedSource.kt:189)
2022-08-28 00:01:04.159 13560-22718/co.iblum.devclient I/okhttp.OkHttpClient:     at com.facebook.react.devsupport.MultipartStreamReader.readAllParts(MultipartStreamReader.java:136)
2022-08-28 00:01:04.159 13560-22718/co.iblum.devclient I/okhttp.OkHttpClient:     at com.facebook.react.devsupport.BundleDownloader.processMultipartResponse(BundleDownloader.java:177)
2022-08-28 00:01:04.159 13560-22718/co.iblum.devclient I/okhttp.OkHttpClient:     at com.facebook.react.devsupport.BundleDownloader.access$100(BundleDownloader.java:34)
2022-08-28 00:01:04.161 13560-22718/co.iblum.devclient I/okhttp.OkHttpClient:     at com.facebook.react.devsupport.BundleDownloader$1.onResponse(BundleDownloader.java:147)
2022-08-28 00:01:04.161 13560-22718/co.iblum.devclient I/okhttp.OkHttpClient:     at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
2022-08-28 00:01:04.161 13560-22718/co.iblum.devclient I/okhttp.OkHttpClient:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
2022-08-28 00:01:04.161 13560-22718/co.iblum.devclient I/okhttp.OkHttpClient:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
2022-08-28 00:01:04.161 13560-22718/co.iblum.devclient I/okhttp.OkHttpClient:     at java.lang.Thread.run(Thread.java:920)

Not sure what that’s about ut that’s the only thing suspicious

This is another try can’t seem to get consistency as of yet. Here’s the logcat around when the error appeared

2022-08-28 00:11:09.686 23401-23637/co.iblum.devclient I/ReactNativeJS: Running "main
2022-08-28 00:11:09.695 23401-23630/co.iblum.devclient E/unknown:ReactNative: Exception in native call from JS
    com.facebook.react.devsupport.JSException: Unexpected token ')'. Expected an opening '(' before a function's parameter list.
        at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:27)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:228)
        at java.lang.Thread.run(Thread.java:920)
     Caused by: com.facebook.jni.CppException: Unexpected token ')'. Expected an opening '(' before a function's parameter list.
    
    no stack
    	... 8 more
2022-08-28 00:11:09.705 23401-23630/co.iblum.devclient E/ReactNativeJNI: Attempting to call JS function on a bad application bundle: HMRClient.setup()
2022-08-28 00:11:09.722 552-2295/? I/ActivityTaskManager: START u0 {cmp=co.iblum.devclient/expo.modules.devlauncher.launcher.errors.DevLauncherErrorActivity} from uid 10149
2022-08-28 00:11:09.757 23401-23630/co.iblum.devclient E/ReactNativeJNI: Attempting to call JS function on a bad application bundle: AppRegistry.runApplication()
2022-08-28 00:11:09.760 552-2295/? I/ActivityTaskManager: START u0 {cmp=co.iblum.devclient/expo.modules.devlauncher.launcher.errors.DevLauncherErrorActivity} from uid 10149
2022-08-28 00:11:09.999 552-2295/? W/ActivityTaskManager: Tried to set launchTime (0) < mLastActivityLaunchTime (106387245)
2022-08-28 00:11:10.015 23401-23631/co.iblum.devclient E/unknown:ReactContextBaseJavaModule: Unhandled SoftException
    java.lang.RuntimeException: Catalyst Instance has already disappeared: requested by FrescoModule
        at com.facebook.react.bridge.ReactContextBaseJavaModule.getReactApplicationContextIfActiveOrWarn(ReactContextBaseJavaModule.java:66)
        at com.facebook.react.modules.fresco.FrescoModule.invalidate(FrescoModule.java:202)
        at com.facebook.react.bridge.ModuleHolder.destroy(ModuleHolder.java:110)
        at com.facebook.react.bridge.NativeModuleRegistry.notifyJSInstanceDestroy(NativeModuleRegistry.java:108)
        at com.facebook.react.bridge.CatalystInstanceImpl$1.run(CatalystInstanceImpl.java:368)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:27)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:228)
        at java.lang.Thread.run(Thread.java:920)

Anyway I’ll try tunnel maybe that would help since the previous error I had was talking about some HTTP thing.

Went a bit further now, but now I end up in the exact same space as I had with the other dev client I built using the build servers. It appears that data that is in the .env files are not being propagated to the app. The same behaviour is not present in Expo Go.

OK finally took a while, but I still can’t get it working on “normal” but works finally on “tunnel” mode. I had to include most of my dependencies at least the ones that are publically available (since the private ones do not do any low level code I was fine to exclude them.

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.