We recently finished working on our latest project which is Lukit. Lukit is an app for foraging wild plants. The app is a React Native developed using the Expo framework. We want to share our experience with the Expo framework now that we have finished working on the project.
What is Expo and why did we decide to use it?
Expo is a framework that allows you to develop React Native apps. Expo provides you with inbuilt APIs and tools to make the development process easier and faster.
After experimenting with Expo, we understood what it has to offer and it's limitations and we decided to try it out due to it's apparent strengths. We know that at some point we want to implement push notifications using Firebase Cloud Messaging (FCM) and that's something that expo doesn't fully support. Regardless we knew that we could eject from the managed workflow whenever we wanted to and continue without issues. Also, we did not posses a usable Mac device which is typically required for iOS React Native development and Expo provides a solution to this which we detail later on in the article.
The development process
The development of the app was rather smooth. Expo provides extensive documentation regarding their APIs and that helps make the development process very smooth.
Eventually, we reached the point where we wanted to implement FCM push notifications and we knew that it was time to eject to the bare workflow. Unfortunately, this is where we ran into a massive obstacle. We ejected to the bare workflow and suddenly the app would crash upon startup without any specific details that could point us at the right direction.
This issue happened late in development and we wanted to deploy the app to the stores as fast as possible but we just did not know how long it would take to figure out what was the reason behind the constant crashes. Instead of going on a wild goose chase, we found an alternative way to deal with the issue that allowed us to stay in the managed workflow and not spend any more time on research. Now came the time to deploy the project to the stores.
The deployment process and our struggle with Apple
After we finished developing the initial version of the app. We deployed a version to the Google Play Store. They approved us without any issues and we made sure to make use of Expo's Over The Air (OTA) updates feature.
The real problem started when we wanted to deploy the app to the Apple App Store. The app offers a subscription option to client who want access to some of its content and we implemented this feature using an internal mechanism we developed on our own. We were aware of the policy of Apple that requires you to provide In App Purchase option to iOS users. But, we decided to employ the strategy that Spotify used to deal with this issue. The payment feature was removed from the iOS version of the app and users were provided a web page that they could pay from.
This wasn't enough for Apple, they constantly rejected the builds we provided them with very vague reasoning. We considered implementing the In App Purchase feature but then we realized that the managed workflow of Expo does not support this. As mentioned before, we did not want to waste time dealing with the side effects of ejecting to a bare workflow so we ended up deciding to hide all subscription content from iOS users in order to get Apple to approve us after many rejections.
Should we have used Expo in the first place?
Many compromises were made, but we did end up deploying the app to both stores and the users are very satisfied with the product. We are relatively happy with the result but we are not interested in using the managed workflow of Expo in future projects due to the nature of how quickly the needs of our clients grow.
We want to highlight the most important strengths and weaknesses of the Expo managed workflow in the following sections so that you can have an easier time knowing if you should use Expo to develop your React Native app.
The strengths of Expo
Expo has extensive documentation regarding the vast amount of APIs that they provide and good documentation reduces a lot of guesswork and experimentation which, in turn, cuts down a lot of development time when you're already in a rush to deliver the product to your audience.
iOS development without a Mac
The Expo framework allows you to connect to your local code environment with an iPhone using the Expo app. iPhone simulators can typically only be run on Macs so this could be a deal-breaker for developing in React Native if you don't possess a Mac. This fact is the biggest reason we decided to use the Expo managed workflow because we did not possess a usable mac for development at the time and due to the situation with the pandemic, we couldn't purchase one immediately. This is a big boon to small companies and developers who want to work private projects or experiment with Expo. I still do recommend having a mac so that you can use an iPhone simulator during development.
Over The Air (OTA) updates
The Expo framework has inbuilt functionality that allows you to send updates to production builds that you've already published to Google Play store and/or Apple App Store. This feature is extremely powerful and allows you to apply vital bugfixes without having to wait for the review process in the stores which can sometimes take a day or even longer in the case of Apple App Store due to their strict and rigid review process.
Although this is noted as a pro, OTA updates have very important limitations that the documentation does not put enough emphasis on.
- Breaking changes are very dangerous. By default, when an expo app starts it attempts to fetch OTA updates within 30 seconds before falling back on a caches version of the app. This feature comes with its own pros and cons that we will not delve into in this article but the important thing to note is that if you publish a breaking change and you require your users to immediately use the newly published version of the app and the app ends up falling back to a cached version, your users might end up running into unexpected errors. Either increase the fallbackToCacheTimeout value in your app.json configuration or better yet publish your changes on a new release channel and upload the version to the stores.
- You should absolutely make use of Expo's advanced release channels when staging your app or deploying to the stores. We also recommend splitting the production version of the apps that are deployed to the stores into different release channels. Keep your deployment process explicit and avoid mistakes.
- Expo's current update service supports updates which are at most around 50MB. This is mentioned in the limitations section of their docs.
- Text changes, simple logic changes, and small asset changes are generally OK in terms of using OTA updates but as a rule, We encourage you to test the OTA updates using a staging release channel as mentioned above.
The weaknesses of Expo
Limited support for third-party packages and native modules
Some NPM packages for React Native require you to perform a link which is something Expo does not support. Do your research and make sure that the React Native packaged you want to use on your projects are supported in the Expo managed workflow which means they openly tell you that or a link is not required.
Some native APIs aren't supported such as Bluetooth, In-App Purchases, and push notification services besides the Expo notification service. Make sure that the native API your app requires is supported as this could be a big deal-breaker.
When we started the publishing process of the app to the Apple App Store we ended being rejected multiple times due to the way the app subscription feature was displayed to the users. We then had to consider if we should eject to the bare workflow to implement In-App Purchases. We tried doing so but we encountered problems that will be mentioned later in this article. In the end, we decided to stay in the managed workflow and we found a way to deal with the issue.
Eventually, after you're done with the development you will naturally need to create standalone builds so that you can publish them to the stores. Expo makes it easy for you by handling all the gritty details for you. But, this comes with a price.
Outside the Expo framework, you have absolute control over the process, all it takes is dealing with the configuration and then you can generate builds using Android Studio for Android builds and Xcode for iOS builds. But, as mentioned before, Expo handles that for you by generating builds on their own servers and this might seem like an awesome thing at first but know that you lose some control over the deployment process. Your builds could be queued and you could end up having to wait a few hours for it to finish processing. Also, there are some configurations that you simply do not have control over such as changing the target API level of your app.
The other potential issue is the app build size. The managed workflow, by default, includes a whole bunch of APIs even if you're not using them. This lets you push over-the-air updates to use new APIs. This ends up making your app sizes larger than you might expect. If the size of your app is important to you then consider if you want to make a compromise in this department by reading about how you can manage the size of your app in their documentation.
Ejecting is risky
We came to a point where we had to eject from the managed workflow but after ejecting we ran into some unexpected issues that resulted in the app crashing immediately upon startup without any meaningful explanation regarding the source of the crash. This was the most difficult point in the project as we did not foresee this situation as the Expo docs do not really mention this as a possibility. We decided to avoid ejecting as time was of the essence.
If you cannot afford to spend time trying to make the app work and then spend the extra time testing all the features for their integrity then you should either try your best to avoid ejecting from the managed workflow or not using it altogether.
Do your research! Expo is a great tool overall but as with every tool, you need to research and understand if the tool matches your needs. Our research told us that Expo would do an adequate job for the purposes of the project but during development, we ran into some obstacles that Expo didn't have an answer for so we had to find some creative alternatives which allowed us to continue to use the managed workflow due to the inherent problems that come with ejecting. Unfortunate compromises were made but the benefits that Expo has provided us in return cannot be denied.
Our story with this project has only started and we will probably keep using Expo for a least a few more versions until we hit a dead end. If we have any more conclusions then we will definitely create a follow-up to this story but until then we hope our experience allows you to make a more educated decision regarding the choice to use Expo in your React Native development.
If you want more details about our experience using Expo you can feel free to contact us at firstname.lastname@example.org