Conventional methods of updating mobile applications often prove time-consuming, causing delays that hinder overall productivity. However, React Native apps have a notable advantage that mitigates this problem: over-the-air updates, a feature that significantly streamlines the update process.
In this article, we’ll focus on implementing OTA updates — specifically, in-app updates — by utilizing the Expo Application Services (EAS) Update service. We’ll go through some example code to better understand how this works, but keep in mind that this code is meant to be explanatory rather than a fully functional project.
Over-the-air updates, often referred to as OTA updates, allow developers to update an app remotely without going through the traditional app store update process. The OTA update approach brings several benefits, such as a quick response time and an improved user experience.
Developers can swiftly address critical bugs or security vulnerabilities and push updates directly to users. The best part? Users don’t have to bother with manual updates, ensuring they always have access to the latest features and improvements.
The two prominent facets of OTA updates are CodePush and in-app updates. We’ll be using in-app updates in this tutorial, so let’s start by exploring what this means.
In-app updates refer to a mobile app’s ability to prompt users to download and install updates directly from within the app itself or silently make minor fixes rather than redirecting users to an external app store. The goal is to provide a seamless and user-friendly experience for updating the app.
There are generally two types of in-app updates:
The type you use may depend on your update’s priority level. Immediate updates are best for critical updates or breaking changes that affect the app’s security and functionality. Flexible updates are less disruptive, making them useful for minor feature, performance, and aesthetic improvements.
If you have experience deploying web applications, you know that in contrast to the web, mobile deployment is notably complex. When there’s a problem even as little as a typo, developers would have to go through the whole deployment process including waiting for approval from the App Store or Google Play.
The mobile app store teams need to ensure your product aligns with their policies and best practices. This evaluation process poses specific challenges within the Apple ecosystem, where apps may be removed or rejected due to policy non-compliance or failure to meet UI standards.
Consequently, the submission process is time-consuming. But when gearing up to release a critical update, every passing minute becomes crucial.
These are the problems that OTA aims to solve. We can make updates available to users without having to wait for a lengthy review process.
The risk of rejection is also minimized when using React Native, particularly as development primarily revolves around the JavaScript aspect of the application. The React Native core team ensures that any framework modifications made have minimal impact on the successful submission of your application.
With CodePush, you can push code directly to user devices, which allows you to bypass the app store review process. This led to a common misconception in the past that Apple was banning certain types of apps that use CodePush.
In reality, the reason Apple was rejecting apps using CodePush was that they weren’t following the guidelines for making updates to the application. Paragraph 3.3.1 of the Apple developer program license agreement clearly states Apple’s guidelines:
“Except as set forth in the next paragraph, an Application may not download or install executable code. Interpreted code may be downloaded to an Application but only so long as such code: (a) does not change the primary purpose of the Application by providing features or functionality that are inconsistent with the intended and advertised purpose of the Application as submitted to the App Store, (b) does not create a store or storefront for other code or applications, and (c) does not bypass signing, sandbox, or other security features of the OS.”
This simply means that your app would not be rejected as long as you’re performing over-the-air updates of JavaScript and assets.
EAS Update is a service provided by Expo that allows developers to implement OTA updates in their applications using the expo-updates
library. This convenient alternative to CodePush enables you to update your existing app without the hassle of building and submitting a brand new version to the App Store.
EAS Update streamlines the process by automatically updating your app. It even works with vanilla React Native apps — the only prerequisite is that you install expo
, a lightweight npm package.
Your application is split into two layers:
The native layer is built into your app’s binary and includes the app icon, app config, native code inside binary, and more. The update layer includes the parts of your app that you can swap when making updates, such as JavaScript and TypeScript code, image assets, etc.
As previously stated, we can only make updates to the update layer of our application. When an update is made, the library initiates an update check when the application is opened:
The timing of update checks and downloads is customizable.
In an Expo project that incorporates the expo-updates
service, the native Android and iOS code included in the project assumes responsibility for overseeing, fetching, parsing, and validating updates.
expo-updates
executes updates in a two-phase download process. During phase one, it retrieves the most recent update manifest. This manifest contains information about the update, including a list of required assets such as images, JavaScript bundles, and font files.
Subsequently, in phase two, the library downloads the assets specified in the manifest that haven’t been previously acquired from earlier updates. For example, if an update introduces a new image, the library fetches the new image asset before implementing the update.
If the library successfully fetches the manifest (phase one) and all necessary assets (phase two) within the specified fallbackToCacheTimeout
setting, the new update is immediately executed upon application launch.
In cases where the library cannot retrieve the manifest and assets within the fallbackToCacheTimeout
period, it continues to download the new update in the background and applies it during the subsequent application launch.
Let’s build a sample React Native app using Expo that demonstrates how to implement eas-updates
. The first step is to make sure we have eas-cli
installed globally:
npm install -g eas-cli
eas-cli
is a tool provided by Expo to help manage our application, from handling builds to submitting apps to the app store. Make sure to log into your EAS account:
eas login
Next, we need to install the EAS Update library and configure our project:
npx expo install expo-updates
Now, navigate to the app.json
file located at the root of your app. Add a plugins
key with the expo-updates
plugin and include your Expo username in the configuration. You can get your username from Expo after logging in:
// app.json "plugins": [ [ "expo-updates", { "username: "account username" } ] ]
Next, we initialize our project with eas update
:
eas update:configure
After, you should see a message in the terminal as shown below:
Now we’re ready to configure our builds:
# Set up the configuration file for builds - eas build:configure
Running the command above will create an eas.json
file in the root directory of your project. We can add some config there:
{ "build": { "preview": { "channel": "staging" // ... }, "production": { "channel": "production" // ... } } }
For each build profile — staging and production — we added a channel
property. This property helps direct updates to builds associated with a specific profile.
So, why do need to set this up? Let’s consider a scenario where you set up a GitHub Action to publish changes upon merging into the production
Git branch:
channel
property set to production
in the production build profile, every commit to the production
branch triggers the GitHub Actionproduction
channelchannel
serves as a linkage mechanism, ensuring that updates are directed to the appropriate builds based on the specified profileOur setup should be done at this point. Now, in your app.js
file, import the updates
function from the expo-updates
package and copy the onFetchUpdateAsync
function from the docs into your file:
import * as Updates from 'expo-updates'; function App() { async function onFetchUpdateAsync() { try { const update = await Updates.checkForUpdateAsync(); if (update.isAvailable) { await Updates.fetchUpdateAsync(); await Updates.reloadAsync(); } } catch (error) { alert(`Error fetching latest Expo update: ${error}`); } } useEffect(() => { onFetchUpdateAsync() }, []) // rest of your code }
We call that function inside a useEffect
Hook to check for updates every time the app loads. Note that this is just a barebones implementation. You could also abstract the update logic into a Hook and then use it in your app.js
file if you wanted to do something like showing a modal to users that informs them of an update:
Now you’re ready to use EAS Update for subsequent changes. However, we need to be aware of one more thing before we can subsequently run updates, which is always updating our app version number:
In the app.json
file, we have a version
key. We can update our app version in that key when we make a new update. The version should follow semantic versioning (SemVer) conventions to communicate the nature of changes:
{ "expo" : { "version": "1.0.0" } }
We’ve successfully set up eas update
in our application to check for updates on the app load. Now, let’s see how to publish our updates.
When we make an update, we can publish that update like so:
eas update --branch [your branch name] --message [message] eas update --branch staging --message "fix typo"
Here’s a breakdown of the code above:
eas update
: Initiates the update process--branch staging
: This flag specifies the branch for which you want to trigger the update. In our case, it’s set to staging
, indicating that the update is intended for the staging
branch--message "fix typo"
: This flag allows you to include a message that describes the purpose or changes associated with the updateThis Expo guide demonstrates how to replicate the same process in a project that follows the bare React Native workflow.
There are a few things you should always pay attention to when implementing in-app updates.
Before launching any update, conduct thorough testing across various devices and platforms to guarantee a smooth and bug-free user experience. User satisfaction hinges on the dependability of your app.
Should an update result in unforeseen issues, it’s crucial to have a rollback plan in place. Enable users to revert to the previous version until any encountered problems are successfully addressed.
Most importantly, keep your users informed about the latest updates through in-app notifications or pop-ups. Let them know about new features and improvements. Establishing transparency fosters trust and encourages users to stay actively involved.
Dynamically shipping your JavaScript changes directly to users via over-the-air updates allows you to skip the meticulous App Store and Google Play review processes. This lets you update your application immediately for all the users, significantly cutting down on the time it would usually take to do this.
There have been gradual improvements in official app store review times over the years. Even so, the ability to roll out OTA updates is a valuable contingency plan, especially for situations like promptly addressing an error that slips through the testing pipeline and reaches production.
Embracing OTA-ready practices provides a more agile and responsive approach to bug resolution, leading to quicker recovery time. If you have any remaining questions about implementing in-app updates for React Native apps, feel free to comment them below.
LogRocket is a React Native monitoring solution that helps you reproduce issues instantly, prioritize bugs, and understand performance in your React Native apps.
LogRocket also helps you increase conversion rates and product usage by showing you exactly how users are interacting with your app. LogRocket's product analytics features surface the reasons why users don't complete a particular flow or don't adopt a new feature.
Start proactively monitoring your React Native apps — try LogRocket for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.