Easily update your React Native App with CodePush.

Easily update your React Native App with CodePush.

Why CodePush?

The CodePush service, a part of App center is a cloud service that enables Cordova and React Native developers to deploy mobile app updates directly to their users' devices.

App center is Microsoft's solution for building, testing, distributing and monitoring apps all in the cloud.

(TLDR') There is something that happens when you start building mobile apps. You realize that at some point the owner of the project will want to see how far you have gone with the work. Unlike a web app where you could just send across a url to the client to check, for mobile(e.g android) you have to build what we call a release apk and send across to the client. Sometimes the apk can be upto 40mb+ in size. What we usually do here is to upload the apk to Google drive and the client can download from there (Of course there are other fine-grained methods like hosting as beta in Playstore).

The problem now is that for everytime you make a new change (e.g fix a typo, change colors) that the client wants to see, you have to make a fresh build, then upload to Google drive again. Consequently, the project owner has to uninstall the previous app from his phone and download the new build of say another 40mb+. And let's hope you didn't make another mistake else you have to start afresh.

One morning a client wanted to demo an App to his investors and we had to upload and download more 9x while fixing very tiny tiny changes.

Another thing is that once the app is released via playstore(Android) or App store(ios), updating the JavaScript code (e.g., fixing bugs or adding new features) or image assets, requires recompilation and redistribution of the entire binary, which includes, of course, any review time associated with the store(s) on which it is published. Scary I must say.

In simple terms, what CodePush does for you is to ensure that the user doesn't have to delete his app and install a fresh 40mb App everytime, instead he is told that there's a new update and if he accepts it, the new/required change will be downloaded while he is still using the App. Only the size of the required change will be downloaded, say 3mb, 2kb etc and not the whole App of 40mb+.

Another beautiful thing about CodePush is that it maintains a copy of every version of the update you release. The advantage of this is that if you release a new update and that update crashes the app, you can roll back to the previous working update.

There's so much but this is just a bit. I believe you get the big picture now. So let's see how we can integrate CodePush in our React Native App.

Note however that CodePush does not update any changes that affects native code such as modifying your AppDelegate.m/MainActivity.java file, adding a new plugin. These ones must be updated via the appropriate store(s).

Create a React Native project

If you already have a React Native project, that's fine else navigate to your directory of choice terminal and type

 npx react-native init RNTutorials

This creates a new React native project called RNTutorials. Ensure it runs etc for android and iOS.

Create an App center account

Head over to Microsoft Appcenter to create a free account. Together with the CodePush service, App center provides other stuff such as building app binary (aab, apk, ipa) , app distribution management, app testing on real devices, crash and usage analytics.

Create your App in App Center

Once you are signed up and logged in to your dashboard, the next thing is to create your App in App center, your App can be either ios or android.

From your dashboard, click the "Add New" button.

dashboard.png

This opens a new page to create your App, we will be doing this twice, first for iOS, then for android.

fig1.png

Fill the form like this for ios:

  • App name: Is your your app name, mine is RNTutorials-ios,

  • Icon: Your app icon,

  • Release Type: Select Production,

  • OS: Select iOS,

  • Platform: Select React Native,

Now click on Add new app button to complete the process.

It will take you to the overview screen of the newly created app with information incase you want to add analytics and app center crashes.

Go back to previous page or Click the Add New button again on the dashboard to create for android like so:

  • App name: Is your your app name, mine is RNTutorials-android,

  • Icon: Your app icon,

  • Release Type: Select Production,

  • OS: Select Android,

  • Platform: Select React Native,

Now click on Add new app button to complete the process.

On your dashboard you should now see the newly created Apps.

fig2.png

Click on any of them, lets start with the android, so click on

RNTutorials-android > Distribute > CodePush > Create standard deployments.

This way, App center will create two environments for you: Staging and production. For this guide, we will use Production.

Also go to ios App and do the same

RNTutorials-ios > Distribute > CodePush > Create standard deployments.

fig3.png

Implement CodePush SDK in React Native client App

Install the appcenter-cli globally by running this on the command line

 npm install -g appcenter-cli

After a successful global installation, you have to login to appcenter via terminal by runnning

appcenter login
  • This will open a browser and generate a new API token.

  • Copy the generated token and paste in the same terminal where you are logging in.

  • On success, you should see Logged in as {Username}

Next install appcenter in your project

# with npm
npm install appcenter 

# or with yarn 
yarn add appcenter

Still in your app, install react native CodePush

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

#  or with yarn 
yarn add react-native-code-push

Most React Native plugins usually have different integration process for Android and iOS. Let's go ahead and do the set up separately.

Android Set up

From your app,

  • Go to android/settings.gradle and add the following line of code;
include ':app', ':react-native-code-push'
project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app')
  • Go to android/app/build.gradle, look for this piece of code
apply from: "../../node_modules/react-native/react.gradle"

then add this one under it

apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"

Everything should now look like this:

...
apply from: "../../node_modules/react-native/react.gradle"
apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"
...
  • Go to android/app/src/main/java/MainApplication.java

First import the plugin class

import com.microsoft.codepush.react.CodePush;

Then update here

    public class MainApplication extends Application implements ReactApplication {
    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        ...
        // Override the getJSBundleFile method to let
        // the CodePush runtime determine where to get the JS
        // bundle location from on each app start
        @Override
        protected String getJSBundleFile() {
            return CodePush.getJSBundleFile();
        }
    };
}
  • Finally Let's add the deployment key to android/app/src/main/res/values/strings.xml

To get the deployment key, type this in the terminal

appcenter codepush deployment list -a <ownerName>/<appName> -k

Mine looks this way

appcenter codepush deployment list -a dexiouz/RNTutorials-android -k

You should have something like this

┌────────────┬───────────────────────────────────────┐
│ Name       │ Key                                   │
├────────────┼───────────────────────────────────────┤
│ Staging    │ opoGVuUethk2uZlFsWX5Oq1upZeSoiBLcYAjc │
├────────────┼───────────────────────────────────────┤
│ Production │ ZrgDzqIHGennFQ3fhDw3U915LmudyRcXSZswW │
└────────────┴───────────────────────────────────────┘

The deployment key lets CodePush know which runtime it should always query for (update from) whether Staging or Production. We'll be using production.

Another way to find the deployment key is to click the options icon on the CodePush dashboard at yourApp/Distribute/CodePush. it's at the top right corner, looking like a spanner haha.

fig4.png

To make use of the Deployment key in android/app/src/main/res/values/strings.xml, create a new string called CodePushDeploymentKey, whose value is the key of the deployment you want to configure this app against (like the key for the Production deployment). Your strings.xml should look like this, ensure you copied the deployment key for Production.

<resources>
    <string name="app_name">RNTutorials</string>
    <string moduleConfig="true" name="CodePushDeploymentKey">ZrgDzqIHGennFQ3fhDw3U915LmudyRcXSZswW</string>
</resources>

Wrap your root component with CodePush.

In my case,the root component is App.js.

// App.js
...
import codePush from "react-native-code-push";

let codePushOptions = { 
    checkFrequency: codePush.CheckFrequency.ON_APP_RESUME, 
    updateDialog: { appendReleaseDescription: true }
};

...
const App = () => {
    return (
        ...
    )
};

export default codePush(codePushOptions)(App);

Notice that we added "codePushOptions", You can check the list of codePushOptions here . The option updateDialog: { appendReleaseDescription: true } means that the user gets update dialog when an update is available.

Now rebuild your app and check the metro logs and you should see something like this.

 LOG  Running "RNTutorials" with {"rootTag":1}
 LOG  [CodePush] Checking for update.
 LOG  [CodePush] Reporting binary update (1.0)
 LOG  [CodePush] App is up to date.

Awesome!

Release an android version to CodePush

Do this by running

appcenter codepush release-react -a <owner_name>/<app_name> -d Production

In my case I did

appcenter codepush release-react -a dexiouz/RNTutorials-android -d Production

On process and completion, my terminal looked this

chidera@Chideras-MacBook-Pro RNTutorials % appcenter codepush release-react -a dexiouz/RNTutorials-android -d Production

Detecting android app version:

Using the target binary version value "1.0" from "android/app/build.gradle".

Running "react-native bundle" command:

node node_modules/.bin/react-native bundle --assets-dest /var/folders/0n/n74jzbv13t5269c8zqm7hwl00000gn/T/code-push2021715-16893-1gyz5o7.htyy/CodePush --bundle-output /var/folders/0n/n74jzbv13t5269c8zqm7hwl00000gn/T/code-push2021715-16893-1gyz5o7.htyy/CodePush/index.android.bundle --dev false --entry-file index.js --platform android
Welcome to Metro!
              Fast - Scalable - Integrated

info Writing bundle output to:, /var/folders/0n/n74jzbv13t5269c8zqm7hwl00000gn/T/code-push2021715-16893-1gyz5o7.htyy/CodePush/index.android.bundle
info Done writing bundle output
info Copying 3 asset files
info Done copying assets

Releasing update contents to CodePush:


Your target-binary-version "1.0" will be treated as "1.0.X".

Successfully released an update containing the "/var/folders/0n/n74jzbv13t5269c8zqm7hwl00000gn/T/code-push2021715-16893-1gyz5o7.htyy/CodePush" directory to the "Production" deployment of the "RNTutorials-android" app.

On success, go back to your App's CodePush dashboard in App center, reload and ensure your new release is there.

To confirm that everything works, run the App again, look at the metro server logs and notice that you can download the latest version from CodePush.

fig5.png

Once we click install, we get this on the metro server logs

 LOG  Running "RNTutorials" with {"rootTag":21}
 LOG  [CodePush] Sync already in progress.
 LOG  [CodePush] Checking for update.
 LOG  [CodePush] Awaiting user action.
 LOG  [CodePush] Downloading package.
 LOG  [CodePush] Installing update.
 LOG  [CodePush] Update is installed and will be run on the next app restart.

It says the new update will be run on the next app restart. But if you want to run it on this same session instead of when you restart the App, then, click on the Released update on App center, click on the options icon, then check the "Required Update" button to true. Then click "Done".

fig6.png

iOS Set up

For iOS set up do the following since you've installed react-native-codePush

cd ios && pod install && cd ..

From your app,

  • Go to ios/your-app-name/AppDelegate.m and import codePush header
#import <CodePush/CodePush.h>

Still in AppDelegate.m, find this line of code

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

and replace it with

return [CodePush bundleURL];

This change configures your app to always load the most recent version of your app’s JS bundle.

  • Go to ios/your-app-name/info.plist

Remember that Deployment key?, let's get the one for iOS and add it here.

We will make a new entry named CodePushDeploymentKey, whose value is the key of the deployment you want to configure this app against (in our case, it is Production).

To get the deployment key, type this in the terminal

appcenter codepush deployment list -a <ownerName>/<appName> -k

Mine looks like this

appcenter codepush deployment list -a dexiouz/RNTutorials-ios -k

You should have something like this

┌────────────┬───────────────────────────────────────┐
│ Name       │ Key                                   │
├────────────┼───────────────────────────────────────┤
│ Staging    │ Xheg3rfeO7VyKtEoGSRnRh3Lrh0lgwVs2j4rt │
├────────────┼───────────────────────────────────────┤
│ Production │ QbQuDUS4hSELnN4BMrVos1pWsvX6k6MnFFedc │
└────────────┴───────────────────────────────────────┘

The deployment key lets CodePush know which runtime it should always query for (update from) whether Staging or Production. We'll be using production.

Another way to find the deployment key is to click the options icon on the CodePush dashboard at yourApp/Distribute/CodePush. it's at the top right corner, looking like a spanner haha.

Now copy the deployment key for Production and add that key in info.plist

...
    <key>CodePushDeploymentKey</key>
    <string>QbQuDUS4hSELnN4BMrVos1pWsvX6k6MnFFedc</string>
...

Because we have wrapped our root component with CodePush, let's go ahead and re-build our ios App.

yarn ios

Check the metro server logs and we get

 LOG  Running "RNTutorials" with {"rootTag":1,"initialProps":{}}
 LOG  [CodePush] Sync already in progress.
 LOG  [CodePush] Checking for update.
 LOG  [CodePush] App is up to date.

Release an iOS version to CodePush

Do this by running

appcenter codepush release-react -a <owner_name>/<app_name> -d Production

In my case I did

appcenter codepush release-react -a dexiouz/RNTutorials-ios -d Production

My terminal looks like this

chidera@Chideras-MacBook-Pro RNTutorials % appcenter codepush release-react -a dexiouz/RNTutorials-ios -d Production
Detecting ios app version:

Using the target binary version value "1.0" from "ios/rntutorials/Info.plist".

Running "react-native bundle" command:

node node_modules/.bin/react-native bundle --assets-dest /var/folders/0n/n74jzbv13t5269c8zqm7hwl00000gn/T/code-push2021715-21428-tdqvv2.pgc5/CodePush --bundle-output /var/folders/0n/n74jzbv13t5269c8zqm7hwl00000gn/T/code-push2021715-21428-tdqvv2.pgc5/CodePush/main.jsbundle --dev false --entry-file index.js --platform ios
Welcome to Metro!
              Fast - Scalable - Integrated

info Writing bundle output to:, /var/folders/0n/n74jzbv13t5269c8zqm7hwl00000gn/T/code-push2021715-21428-tdqvv2.pgc5/CodePush/main.jsbundle
info Done writing bundle output
info Copying 3 asset files
info Done copying assets

Releasing update contents to CodePush:


Your target-binary-version "1.0" will be treated as "1.0.X".

Successfully released an update containing the "/var/folders/0n/n74jzbv13t5269c8zqm7hwl00000gn/T/code-push2021715-21428-tdqvv2.pgc5/CodePush" directory to the "Production" deployment of the "RNTutorials-ios" app.
chidera@Chideras-MacBook-Pro RNTutorials %

To confirm that everything works, run the App again, look at the metro server logs and notice that you can download the latest version from CodePush.

fig7.png

Once we click install, we get this on the metro server logs

 LOG  Running "RNTutorials" with {"rootTag":1,"initialProps":{}}
 LOG  [CodePush] Sync already in progress.
 LOG  [CodePush] Checking for update.
 LOG  [CodePush] Awaiting user action.
 LOG  [CodePush] Downloading package.
 WARN  Sending `CodePushDownloadProgress` with no listeners registered.
 LOG  [CodePush] Installing update.
 LOG  [CodePush] Update is installed and will be run on the next app restart.

It says the new update will be run on the next app restart. But if you want to run it on this same session instead of when you restart the App, then, click on the Released update on App center, click on the options icon, then check the "Required Update" button to true. Then click "Done".

fig6.png

You can check your ios release if you go to

yourapp/Distribute/CodePush/Production(From the options icon - top right).

And that's it. Congratulations, you have succeeded in setting up CodePush service in your React Native App.

If you found this article useful please like , drop your comments and share with your friends.

You can also Follow me on twitter: @talk2dera