React Native over-the-air app update with CodePush

Last updated 22 October 2021

CodePush is an App Center cloud service that enables Apache Cordova and React Native developers to deploy mobile app updates directly to their users' devices. It works by acting as a central repository that developers can publish certain updates to (e.g. JS, HTML, CSS and image changes), and apps can query for updates from (using the provided client SDKs). This allows you to have a more deterministic and direct engagement model with your end users while addressing bugs, adding small features that don't require you to rebuild a binary, or redistributing it through any public app stores.

React Native has four core sections: the React code you write (which is very similar to its web counterpart), the JavaScript that gets interpreted from what you write, a series of elements collectively known as "The Bridge", and the Native side. In this article, we will talking about how do we update the React/JavaScript code using CodePush. Any changes in native side will require you to go through actual app store update process.

How do we update the app?

There are 2 ways of updating React Native app: over-the-air and store update.

Over-the-air update process is pretty much similar to updating a web application. You send a new update out, the user downloads it, and the app updates its content. Over-the-air (OTA) update is a strong selling point of React Native. Since React Native developers usually write their logic in JavaScript, they can just send out a new JavaScript bundle via OTA update. And, once the user downloads the new bundle, they will have the updated logic.

However, you must use this extreme flexibility with caution. You really should not make any significant changes to your app via OTA. For example, you don't want to change the functionality of your app by changing how things work or adding features. Doing so can get you in trouble with Apple/Google as you might violate their store rules.

Developers sometime need to release the new app via store update. For example, when you make some changes related to native modules, or when you import third-party libraries which require a "linking" with native codes. Store updates require developers to submit a new app bundle to the store (e.g. Google Play and App Store). By submitting to the store, it will require the app to go through a full store update process (e.g. store review).

Get started

CodePush is a tool developed by Microsoft which allows developers to quickly and easily manage and update React Native JavaScript bundles over the air. It is relatively easy to setup, easy to use, and most importantly it is free!

Without further due, visit App Center and log in (sign up if you don't have an account) with your account.

You can enable multiple features on App Center. But, this tutorial will focus on CodePush. Click Add new > Add new app to create a new app, and you will see the following dialog.

For this tutorial, make sure you select production for the release type, iOS for the operating system, and React Native for the platform. You can name your app whatever you want.

Once you create the app, you will see the following page.

Click Distribute > CodePush from the left menu. And then click Create standard deployments. This will generate two deployments for you: production and staging.

Install the App Center CLI

You manage most of CodePush's functionality using the App Center CLI. To install the CLI, open a terminal window or command prompt and execute the following command:

npm install -g appcenter-cli

After successfully installing the App Center CLI, execute the appcenter login command to configure the CLI for your App Center account details.

Integrate CodePush

You can start integrating CodePush in your React Native app by running the following command from within your app's root directory:

npm install --save react-native-code-push

As with all other React Native plugins, the integration experience is different for iOS and Android, so follow the setup steps depending on the platform(s) you target for your app. Note, if you're targeting both platforms it's recommended to create separate CodePush applications for each platform. But, in this tutorial we will focus on the iOS integration.

For React Native version 0.60 and above, run npx pod-install from within your app's root directory to install all the necessary CocoaPods dependencies.

Open up the AppDelegate.m file, and add an import statement for the CodePush headers:

#import <CodePush/CodePush.h>

Find the following line of code, which sets the source URL for bridge for production releases:

return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];

Replace it with this line:

return [CodePush bundleURL];

This change configures your app to always load the most recent version of your app's JS bundle. On the first launch, this will correspond to the file that was compiled with the app. However, after an update has been pushed via CodePush, this will return the location of the most recently installed update.

To let the CodePush runtime know which deployment it should query for updates against, open your app's Info.plist file and add a new entry named CodePushDeploymentKey, whose value is the key of the deployment you want to configure this app against. You can retrieve this value by running appcenter codepush deployment list --app <ownerName>/<appName> -k with the CodePush CLI and copying the value of the Deployment Key column that corresponds to the deployment you want to use.

Implement CodePush

With the CodePush plugin downloaded and linked, and your app asking CodePush where to get the right JS bundle from, the only thing left is to add the necessary code to your app to control the following policies:

  • When (and how often) to check for an update? (for example app start, in response to clicking a button in a settings page, periodically at some fixed interval)
  • When an update is available, how to present it to the end user?
  • The simplest way to do this is to attach CodePush in your app's root component. Wrap your root component with the codePush higher-order component.

    import React from 'react';
    import codePush from 'react-native-code-push';
    
    const App: React.FC = () => {
      ...
    };
    
    export default codePush(App);

    By default, CodePush will check for updates on every app start. If an update is available, it will be silently downloaded, and installed the next time the app is restarted (either explicitly by the end user or by the OS), which ensures the least invasive experience for your end users. If an available update is mandatory, then it will be installed immediately, ensuring that the end user gets it as soon as possible.

    If you would like your app to discover updates more quickly, you can also choose to sync up with the CodePush server every time the app resumes from the background.

    import React from 'react';
    import codePush from 'react-native-code-push';
    
    const App: React.FC = () => {
      ...
    };
    
    export default codePush({
      checkFrequency: codePush.CheckFrequency.ON_APP_RESUME,
    })(App);

    Release updates

    Once your app is configured and distributed to your users, and you have made some JS or asset changes, it's time to release them. The recommended way to release them is using the release-react command in the CodePush CLI, which will bundle your JavaScript files, asset files, and release the update to the CodePush server.

    appcenter codepush release-react -a <ownerName>/<appName> -d Production

    Conclusion

    Over-the-air updates are very convenient for developers as they can deliver updates quickly and easily to users. However, you must use this extreme flexibility with caution. You really should not make any significant changes to your app via OTA. For example, you don't want to change the functionality of your app by changing how things work or adding features. Doing so can get you in trouble with Apple/Google as you might violate their store rules.

    To learn more about CodePush, you can visit its official documentation here.