Implementing in-app updates for React Native apps
Tags: reactnative, webdev
Published: 2024-03-07 19:22:30

**Written by [Nelson Michael
](https://blog.logrocket.com/author/nelsonmichael/)✏️**

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.

## What are over-the-air updates?

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.

## What are in-app updates?

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:

1.  **Immediate updates**: These require users to install the update immediately, potentially disrupting their current app session
2.  **Flexible updates**: These allow users to continue using the app while an update is downloaded in the background. Users are prompted to install the update when convenient

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.

## Why is OTA important?

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.

## OTA ban history

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](https://developer.apple.com/support/terms/apple-developer-program-license-agreement/#b331) 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.

## Understanding the EAS Update service

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.

## How does EAS Update work?

Your application is split into two layers: ![Graphic Demonstrating React Native's Two App Layers — The Update Layer And The Native Layer](https://blog.logrocket.com/wp-content/uploads/2024/02/React-Native-app-update-layer-native-layer.png) 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:

*   If a more recent update than the currently running one is detected, the library proceeds to download and execute the newer update
*   In the absence of a newer update, the library falls back to the update embedded within the app at build time

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.

## Implementing in-app updates for a demo React Native app

Let’s build a sample [React Native app using Expo](https://blog.logrocket.com/getting-started-with-react-native-and-expo-sdk/) that demonstrates how to implement `eas-updates`. The first step is to make sure we have [`eas-cli`](https://docs.expo.dev/build/introduction/) installed globally:

```bash
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:

```bash
eas login
```

Next, we need to install the EAS Update library and configure our project:

```bash
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:

```json
// app.json

"plugins": [
  [
    "expo-updates",
    {
      "username: "account username"
    }
  ]
]
```

Next, we initialize our project with `eas update`:

```bash
eas update:configure
```

After, you should see a message in the terminal as shown below: ![Screenshot Of A Message In The Terminal Confirming That All Builds Of Your App Moving Forward Are Eligible To Receive Updates Published With Eas Update](https://blog.logrocket.com/wp-content/uploads/2024/02/Terminal-message-EAS-Update.png) Now we’re ready to [configure our builds](https://docs.expo.dev/build/eas-json/):

```bash
# 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:

```json
{
  "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 we need to set this up? Let's consider a scenario where you [set up a GitHub Action](https://blog.logrocket.com/react-native-ci-cd-using-github-actions/) to publish changes upon merging into the `production` Git branch:

*   With the `channel` property set to `production` in the production build profile, every commit to the `production` branch triggers the GitHub Action
*   This, in turn, publishes an update that is accessible to builds identified with the `production` channel
*   The `channel` serves as a linkage mechanism, ensuring that updates are directed to the appropriate builds based on the specified profile

Our 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](https://docs.expo.dev/versions/latest/sdk/updates/) into your file:

```javascript
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](https://blog.logrocket.com/useeffect-react-hook-complete-guide/) 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: ![Demo Popup Modal In React Native App Informing Users Of An Available Update](https://blog.logrocket.com/wp-content/uploads/2024/02/Update-Available-popup-modal.png) 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:

*   **What this means**: Think of the app version number as a compatibility label for your app's building blocks
*   **Why this is important**: The app version number ensures that updates match the underlying structure of your app, preventing crashes

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](https://blog.logrocket.com/version-management-changesets/) to communicate the nature of changes:

```json
{
  "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.

## Publishing updates to our React Native app

When we make an update, we can publish that update like so:

```bash
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 update

[This Expo guide](https://docs.expo.dev/eas-update/updating-your-app/) demonstrates how to replicate the same process in a project that follows the bare React Native workflow.

## Best practices for implementing in-app updates in React Native

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.

## Conclusion

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: Instantly recreate issues in your React Native apps

[![LogRocket Signup](https://blog.logrocket.com/wp-content/uploads/2021/10/react-native-plug_v2-2.png)](https://lp.logrocket.com/blg/react-native-signup)

[LogRocket](https://lp.logrocket.com/blg/react-native-signup)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](https://lp.logrocket.com/blg/react-native-signup).