Ability to work in Expo client after detaching
complete
Sébastien Lorber
I have successfully been able to work inside Expo client after detaching and adding Intercom + OneSignal.
This Canny document a bit the process and also asks the Expo team for a better support of this kind of workflow, as it would certainly make detaching process less scary for many other developers.
----------------------------------
If the app is programmed defensively when trying to import/use native libraries that are not part of Expo SDK, it is possible to share the whole JS codebase and make it work fine both in Expo client, and in detached app.
The idea is that in Expo client, the native js calls would simply be ignored/stubbed, while in XCode/Android studio built apps, they would just work fine.
It is possible to import native libraries conditionally with a babel codegen plugin (kentcdodds/babel-plugin-codegen => see screenshot) according to an environment variable. If the app is running in Expo client, I won't import native modules but would simply replace them with undefined, and if the native module is undefined, I would simply not call it (see OneSignal screenshot)
By using such trick and restoring some modifed things in app.json/package.json after ejection (react-native-scripts), I am able to use the exact same codebase for running in Expo client and building with XCode/Android Studio. I just have to toggle an environment variable to switch between Expo and Native.
----------------------------------
I think this kind of workflow should be encouraged and supported by the Expo team as it really simplifies life to still be able to use Expo client after ejecting. For sure there are limitations, but most of the time if you just add libraries like Intercom, OneSignal, Segment, Mixpanel, Amplitude or whatever, it's a burden to leave Expo client development workflow completely just for that usecase.
I suggest that:
- Expo does not necessarily remove the react-native-scripts on eject
- Expo ejection does generate an exemple of this workflow by adding automatically an example RN library
- Expo ensures that the Chrome debugger still opens after ejection when running in Expo client (which seems prevented by "app.json->detached" attribute presence)
- Expo does add a new package.json script to use for running in native mode, for exemple: "dev-native": "EXPO_DETACHED=true exp start --lan",
- Expo does keep the Expo entry point (which I think triggers the QRCode scanning stuff right?)
This could be a new eject options. I have no idea how to deal with the differences between CRNA and Expo however.
Evan Bacon
complete
As Sébastien Lorber said, this was completed in SDK 35. I'll note that client usage did change a bit in SDK 41 https://github.com/expo/fyi/blob/master/expo-extension-migration.md
Sébastien Lorber
This is now officially possible in the bare workflow: https://docs.expo.io/versions/v35.0.0/bare/using-expo-client
Keith Kurak
We use this workflow all the time despite the lack of support. It’s incredibly useful to us. On any module that breaks if the native code isn’t present, we check for it in NativeModules before importing it. Beyond that, we just have an npm script that sets isDetatched to false in app.json and it works just fine. I don’t care that it’s not the entire app. We did the same thing back in Cordova, basically. We’d do 98% of development in Chrome without native plugins and only do the full build when we needed to test everything. If I didn’t offer my team this, they might try to go back to Cordova ;-)
B
Brad
I work at OneSignal, we've recently updated the OneSignal react-native SDK (3.2.0) so that it will no longer cause a crash if you run it in the Expo client.
Essentially, we've made it so that the SDK will check to make sure the native bridge exists before attempting to use it.
Unfortunately, the Expo client can't let us run native code (iOS restriction), so we can't actually use our iOS/Android SDK's. This means that when you call OneSignal functions like sendTags() from within an app running in the Expo client, nothing will happen. But at least it's better than instantly crashing when the app launches.
The SDK should start functioning correctly once you detach from expo and link. If you have any questions, please feel free to contact our support (I will see your ticket) by going to the onesignal dashboard and clicking the ? button.
Daniel K.
Well, I suppose this can work for modules for analytics and similar which your app does not need to work. However, there is a bunch of other modules that are essential (eg. background geolocation). With your solution, it would mean that app would work only partially while on Expo. You would probably need to have some sort of a mock module, but that's a rather unfortunate solution.
I am going to pass on this, it's not worth it for me since I have already fully functional pure RN app and the workflow is not really that different from Expo. I would even say it's more reliable in some cases.
Sébastien Lorber
Daniel K.: it can be useful on a large team where only you have to deal with the background geolocation and others are working on simpler subjects not related to native libs. If you are a single developer, already got your RN project working it's not very useful but if you are working with "RN integrators", it would be quite annoying for them to have to install XCode, download 5go of Android stuff with Gradle, just to be able to change some text colors and paddings.
Daniel K.
Sébastien Lorber: Size of the team does not matter that much. As long as app needs some special native module for it to be working, this solution of yours is not viable. For example react-native-navigation is a native solution for a navigation. You cannot use it with your workaround as everyone on the team needs it.
Also, some people don't even realize, that once you have a native development build, you don't even need access to Android Studio / XCode. Just install created app on your device/emulator and you are good to go. That's the way we are doing it and it works just fine.
Sébastien Lorber
Daniel K.: that's true for native navigation for sure. However if you build the app, the js bundle location is hardcoded so if it's Expo you need to publish to see your changes on mobile, and if it's local lan, you need to ensure the ip remains the same and probably be on same network so I wouldn't say this workflow is as easy as you describe it, however it's certainly not the worst. I'd be happy if you could write a blog post about your development workflow to learn more about it.
Daniel K.
Sébastien Lorber: I would say you are highly misinformed about this. The packager that Expo is using is not their invention, it comes from React Native itself and it's called metro-bundler. That means the workflow is exactly the same as with Expo. The only difference is that you have your own native app installed on mobile which talks with the packager same way as Expo does.
The workflow is more or less described in my article here. https://medium.com/@DanielKrejci/how-i-ditched-expo-for-pure-react-native-fc0375361307. Check "Starting from the scratch" passage.
Sébastien Lorber
Daniel K.: the phone app has to fetch the bundle from the RN packager with 192.168.0.x ip, which runs on your computer (it's what I describe as "local lan" in previous answer). If you change wifi network your computer ip change and you have to rebuild the app (not a big deal) because there's no way the phone/app can guess the new packager ip, and all your teammates still have to build their app so are required to install a lot of stuff that they wouldn't have to with Expo, you can't just send them the APK that targets your own local computer.
I persist that if you are a small team and all members are able to install of these dependencies easily on their own it's not a big problem and you certainly have a successful workflow for your own case, but if you are a large team with different skill levels, turnover, and need teammates to get started fast, there's no necessity to ask every team member to install gigabytes of tooling and read long readme just for changing colors, wordings and font sizes.
I agree with you that installing new RN libraries might be harder with an ejected app on ExpoKit and that you might only use a very small part of all what ExpoKit provides you. That's the case for OneSignal and ExpoKit because they use different versions of Google Play Services and you have to downgrade/fix the version used by OneSignal to make them work nicely together. Even with pure RN you might encounter such dependencies conflicts that are quite common in Java world that's different from NPM world regarding transitive dependencies. You can generally fix these conflicts by forcing a specific version (if you are lucky).
About background location, you can easily develop your app in Expo without it inside Expo too. Just use foreground location for dev, it will be largely enough to build an Uber-like app, and assume the background location will be tracked correctly on production because you (or someone else) made it work with Xcode/Android Studio.
Daniel K.
Sébastien Lorber: You are partially mistaken. The Android build actually has dev settings (upon shake gesture) which allow specifying IP:PORT where to connect to the packager. iOS does not have this luxury. I suppose it's generally easier to setup XCode and build an app on your own. It shouldn't be that hard to implement the same thing that Android has.