Ship OTA (Over-The-Air) When in an Emergency

Authors
Szymon Rybczak
Software Developer
@
Callstack
Tomasz Misiukiewicz
Software Engineer
@
Callstack
Issue: Traditional ways of updating apps are too slow and you lose your precious time on them.

The traditional model of sending updates on mobile is fundamentally different from the one we know from writing JavaScript applications for other platforms. Unlike the web, mobile deployment is much more complex and comes with better security out-of-the-box. We discuss this topic in greater detail in our article about Continuous Deployment in mobile apps.

What do OTA updates (or lack of them) mean to your business?

Every update, no matter how quickly shipped by your developers, is usually going to wait some time while the App Store and PlayStore teams review your product against their policies and best practices.

This process is particularly challenging in all Apple platforms where apps are often taken down or rejected, because of not following certain policies or meeting the required standard for the user interface. Thankfully, the risk of your app being rejected with React Native is reduced to a minimum as you're working on the JavaScript part of the application. The React Native CoreTeam ensures that all the changes done to the framework have no impact on the success of your application's submission.

As a result, the submission process takes a while. And if you're about to ship a critical update, every minute counts.

Fortunately, with React Native, it is possible to dynamically ship your JavaScript changes directly to your users, skipping the AppStore review process. This technique is often referred to as an over-the-air update. It lets you change the appearance of your application immediately, for all the users, following the technique you have selected.

When critical bugs happen, minutes and hours can be crucial

Don't wait to fix your end users' experience. If your application is not OTA-ready, you risk being left with a critical bug on many devices, for as long as Apple/Google reviews your product and allows it to be distributed. Even though the review times have gotten much better over the years, it is still a good escape hatch to be able to immediately recover from an error that slipped through the testing pipeline into production.

How to implement OTA updates with App Center/CodePush or EAS Update

Solution: Implementing OTA updates with App Center/CodePush or EAS update

As mentioned earlier, React Native is OTA-ready. It means that its architecture and design choices make such updates possible. However, it doesn't ship with the infrastructure to perform such operations. To do so, you will need to integrate a 3rd-party service that carries its infrastructure for doing so.

These are the popular ways to implement OTA into your app:

  • CodePush: A service that is part of Microsoft's App Center suite. 
  • EAS Update: A service that is created by Expo and is part of the EAS suite.

App Center/CodePush

Configuring the native side

To integrate CodePush into your application, please follow the required steps for iOS and Android respectively. We decided to link to the official guides instead of including the steps here as they include additional native code to apply and that is very likely to change in the months to come.

Configuring the JavaScript side

Once you set up the service on the native side, you can use the JavaScript API to enable the updates and define when they should happen. One of the ways that enable fetching updates on the app startup is to use the CodePush wrapper and wrap your main component.

import React from 'react';
import { View } from 'react-native';
import codePush from 'react-native-code-push';
const MyApp = () => <View />;
export default codePush(MyApp);

That’s it! If you have performed all the changes on the native side, your application is now OTA-ready. For more advanced use cases, you can also change the default settings on when to check for updates and when to download and apply them. For example, you can force CodePush to check for updates every time the app is brought back to the foreground and install updates on the next resume.

The following diagram code snippet demonstrates such a solution:

import React from 'react';
import { View } from 'react-native';
import codePush from 'react-native-code-push';
const MyApp = () => <View />;
export default codePush({
updateDialog: true,
checkFrequency: codePush.CheckFrequency.ON_APP_RESUME,
installMode: codePush.InstallMode.ON_NEXT_RESUME,
})(MyApp);

Shipping updates to the application

After configuring CodePush on both JavaScript and the native side of React Native, it is time to launch the update and let your new customers enjoy it. We can do this from the command line by using the App Center CLI:

npm install -g appcenter-cli
appcenter login
view raw gistfile1.txt hosted with ❤ by GitHub

Then we continue with a release command to bundle React Native assets and files and send them to the cloud:

appcenter codepush release-react -a <ownerName>/<appName>
view raw gistfile1.txt hosted with ❤ by GitHub

Once these steps are complete, all users running your app will receive the update using the experience you configured in the previous section.

Before publishing a new CodePush release, you will have to create an application in the App Center dashboard. That will give you the ownerName and appName that you’re looking for. As said before, you can either do this via UI by visiting the App Center or by using the App Center CLI.

EAS Update

EAS Update is an EAS service for delivering Over the Air Updates. It provides first-class support for instant updates in React Native applications and is especially user-friendly if you're already using Expo.

It serves updates from the edge with a global CDN and uses modern networking protocols like HTTP/3 for clients that support them. Furthermore, it implements the Expo Updates protocol, which is an open standard specification for instant updates.

Like other Expo products, EAS Update provides a superior Developer Experience, making it a preferred choice for many developers. It also enhances developer workflows with features like:

Using EAS Update

To use EAS Update in your project, you'll need to install the eas-cli package and log in to your Expo account using eas login.

To get EAS Update working in your project with the bare React Native workflow, you need to set up Expo in your project. See the guide to make that work correctly.

Setting up EAS Update

Start by installing expo-updates library in your project:

npx expo install expo-updates

Next, initialize your project with EAS Update:

eas update:configure

Then, set up the configuration file for builds:

eas build:configure

After running these commands, eas.json is created at the root of your project. Inside it, you will notice that there are two different build profiles (preview and production), each with a channel property. A channel allows us to point updates at builds for a profile.

Creating a build

Create a build of your app using EAS Build or another tool of your choice. The new build will include the expo-updates native module, which will be responsible for downloading and launching your updates.

You can set up an internal distribution build using the preview build profile and after the build completes, install it on your device or an emulator or simulator. See internal distribution for more information.

Creating an update

After installing the new build on your device, you're ready to send an update to it! Make a small, visible change to the JS of the app.You can also confirm this change by running the development server with npx expo start locally on our machine.

After you've confirmed our changes, let's run the command to create and publish an update with EAS Update:

eas update --branch preview --message “Fixed a bug.”

This command creates an update. Each update contains details about the branch that is linked to the build profile, the commit, and information about platform-specific (Android and iOS) details. See the conceptual overview of how EAS Update works for more information.

Running an update

After this step is completed, all users running our app will receive an update with the changes. By default, expo-updates checks for the updates in the background when the app launches. When testing with an internal distribution, to see the update in the app you'll need to force close and reopen the app up to two times to see the changes.

You can expand on the default functionality and implement your own strategy using the useUpdates() hook from expo-updates, which allows you to:

  • Fetch information on available updates
  • Fetch information available on currently running updates 
  • Check for changes manually using Updates.checkForUpdateAsync() 
  • Download and run updates using Updates.fetchUpdateAsync()

EAS Update can be very useful for deploying changes to production and is equally beneficial during the development phase. It offers a convenient and rapid method for sharing your work with team members.

Benefits of OTA updates

Benefit: Ship critical fixes and some content instantly to users

With OTA updates integrated into your application, you can send your JavaScript updates to all your users in a matter of minutes. This possibility may be crucial for fixing significant bugs or sending instant patches.

For example, it may happen that your backend will stop working and cause a crash at the startup. It may be a mishandled error – you never had a backend failure during the development and forgot to handle such edge cases. You can fix the problem by displaying a fallback message and informing users about the whole thing. While the development will take you around one hour, the actual update and review process can take hours, if not days. With OTA updates set up, you can react to this in minutes without risking that bad UX will affect the majority of users.

Insights

Learn more about CI/CD

cover

Aug 21Article

Automating React Native Evals With Airflow

How Airflow coordinates React Native Evals, runs solver and judge models separately, and keeps every result traceable through human review.

cover

Jun 19Article

Caching React Native Builds on S3 and R2 With Rock

Learn how to cache your React Native builds and store them on Amazon S3 or R2 to reuse across your team.

cover

Oct 29Podcast

How to Handle App Center Retirement

Discover App Center alternatives for React Native. Explore migration tips and Expo's role in the transition from CodePush & other App Center functionalities.

cover

Aug 4Article

Mastering Fastlane: Overcoming Challenges in Passing Build Properties to Native Layers

Master passing build properties with Fastlane for efficient application development.

cover

May 10Article

Shipping Fast With Continuous Deployment

Continuous Deployment (CD) lets you ship new features or check for critical bugs fast. Find out more about developing apps with a CD setup.

cover

Dec 13Article

How Can Continuous Integration (CI) Improve Your React Native Apps?

Read the article and find out how Continuous Integration (CI) can improve the building, testing, and distribution of your React Native apps.

cover

Apr 24Article

Continuous Delivery - Integration for Everyone

A quick guide on how to tame Continuous Delivery and Integration. Dive into a real-world scenario from a Callstack client project.

cover

Mar 19Tutorial

The Hard Way vs. The React Native AI SDK Way: Stop Writing Custom Modules for Every Model

Learn how React Native AI uses a unified provider model to switch between cloud and on-device LLMs without rewriting application logic, enabling reliable AI experiences online and offline.

cover

Mar 12Tutorial

Build Smarter Apps: Tool Calling & AI Orchestration Explained

Learn how on-device models in React Native can call tools, interact with external APIs, and return structured outputs you can use directly in application logic.

cover

Mar 5Tutorial

MLC LLM + React Native: On-Device AI Without the Pain

Learn how to run third-party on-device LLMs in React Native using MLC LLM. Choose your models, run them efficiently across platforms, and keep a unified JavaScript API with React Native AI.

cover

Feb 24Tutorial

Intelligent Fallbacks with Apple Intelligence in React Native

Learn how to use Apple’s built-in Foundation Models in React Native apps, reduce memory usage, and enable fast, fully on-device AI with React Native AI Apple.

cover

Feb 10Tutorial

What Is the React Native AI SDK? A Complete Intro & Quickstart

Learn why on-device AI matters for React Native apps, how local LLMs behave offline, and what problems React Native AI is designed to solve from day one.

cover

Jan 29Tutorial

Implementing an Android TurboModule with Kotlin

Learn how to implement a React Native TurboModule on Android, from the TypeScript spec and Codegen to the Kotlin implementation and package registration.

cover

Jan 19Tutorial

Working with Different Threads in Swift TurboModules

Learn how JavaScript, native module, background, and main threads interact in React Native TurboModules, and how to use GCD queues to run async work safely in Swift.

cover

Jan 9Tutorial

How to Add Type-Safe Constants to Swift TurboModules

Learn how to expose typed, immutable constants from a Swift TurboModule using Codegen, and get full end-to-end type safety between native and JavaScript.

cover

Dec 30Tutorial

Adding Event Emitters to Your TurboModule in Swift

Learn how to emit native events from a Swift TurboModule and subscribe to them in JavaScript, including proper memory management and Codegen integration.

cover

Dec 22Tutorial

Writing Your First TurboModule in Swift

Learn how to build a fully working TurboModule in Swift, integrate it with Codegen, and bridge it through Objective‑C inside an Expo app.