When developing with Expo, one of the challenges developers typically encounter is finding a compatible npm package. This is because the Expo package cannot add native modules - a huge obstacle for some projects. Yet, as there aren’t many pure-JS react-native packages around at the moment, the only way to continue your development would be to move the whole app back to a pure React-Native environment. Still, doing this can prove problematic if your code has already been built based on Expo specific features because you would have to remove a whole bunch of code and/or replace it with equivalent modules.
Luckily, Expo provides a way out of this sticky situation. A feature exists called Detach giving you what is essentially a simplified version of Expo client that you can tinker about with and build by yourself. Since you still have access to ExpoKit, everything else should still be running the same as before (with some exceptions, which we will discuss later).
If you’ve been enjoying the convenience of an Expo set up and you’ve never worked on a React-Native/mobile project before, the native client build process can seem terrifying. In particular because this approach can introduce a whole can of new bugs. Follow these simple steps for success:
Disclaimer: the assumption is you are developing for both Android/iOS
What do you need before detaching:
Exp-cli, which can be install using `npm install -g exp`
If you developed exclusively using Exp IDE before, then you may not have exp-cli installed.
A Macbook, or at least a computer with macOS.
Latest Xcode version
Latest Android Studio installation with Build Tools 26.0.1
Normally, Gradle will always make sure that you have all SDK-related packages installed, and you may have to restart Android Studio a few times, but it may fail to install this specific Build Tools, hence manual method is preferred.
A properly populated app.json (https://docs.expo.dev/distribution/building-standalone-apps/)
While you are at it, maybe it’s worthwhile preparing a proper icon and splash screen as well, so that the detaching process can help you bundle them with respective mobile clients. Otherwise, you need to do it manually later.
A backup push notification solution (if you’re using Expo Push Notification, as it doesn’t work with detached app)
Firebase Cloud Messaging / OneSignal can be a good candidate
Technically, you can detach the Expo app without Xcode/macOS but this approach is not recommended because it can render the detached iOS client crippled. It would be better to use a Mac, or at least one of the macOS Virtual Machines/Services available for the initial build.
What happens now?
Assuming you’ve prepared everything correctly, the whole detaching process should be quite seamless. After running `exp detach`, you will see two new folders named `ios` and `android` in your project root folder just like a normal React-Native application. By building the two native clients in those folders and deploying them to your phones, you should be set.
Warning: these 2 new folders should be committed and checked into your source control.
How do I develop from now on?
You will need to follow these steps so that an ExpoKit project can work properly:
Start the bundler using `exp start`
Build the native (ios/android) clients and deploy them to your phone.
Verify that your application is running as before.
Verify that your changes in JS code is served properly to your phone through the bundler.
The native clients on your phones will behave much like Exp client itself. What you need to be aware of is each time you add a new native module (described here: https://reactnative.dev/docs/linking-libraries-ios), you need to rebuild the native clients and deploy them to your phone again, otherwise your JS code will crash due to the absence of its native module counterpart.
Please be advised that even though your app is now technically a React-Native app, it’s still using ExpoKit, which means you still need to use Expo Bundler, as opposed to React-Native bundler. And standard React-Native-cli commands like `react-native start-ios` won’t work.
My push notification works but it opens to another instance of my app!
As we discussed before, Expo push notification won’t work with your app. In fact, you need to remove anything related to Expo push notification in your code and server-side code to avoid your backend code “accidentally” breaking the end-customer’s client. Any push notification send to new client with old Expo push token will render the app useless.
After you’re done with purging your code base, consider implementing a third party push notification solution like OneSignal or Firebase Cloud Messaging through a wrapping library such as react-native-fcm or react-native-onesignal. None of these libraries has the simplicity of Expo push notification, but they get quite close.
I get this error message “com.android.dex.DexException: Too many classes in --main-dex-list, main dex capacity exceeded” while building app for debug, how to fix it?
It is a known issue. For some reasons, Expo client for Android supports API level 19 for debugging. One way around it (for now) is to remove the dev19 flavor and use API level 21 as your testing baseline. It doesn’t happen for the actual release build with API level 19, so you can rest assured that your app will still support Android 4.4 devices.
More work around: https://github.com/expo/expo/issues/1166
“React/RCTEventEmitter.h file not found” or any of “file not found” message.
It is likely that the native modules you have added have dependencies on React, and was installed directly without using CocoaPod. There are a lot of issues when a non-Cocoa library has dependencies on Cocoa library, so the safest solution would be turning your non-Cocoa library to a CocoaPod one. Most React-Native native module packages should support CocoaPod.
Keep an eye out for my next blog post, in which I’ll reveal the best way to move from Expo/ExpoKit to a pure React-Native application.