Home | Send Feedback

Hot deploy updates to Ionic / Cordova apps with CodePush

Published: January 16, 2017  •  Updated: December 07, 2017  •  ionic, cordova

In previous posts, I showed you an example of updating Ionic / Cordova apps on the fly with Ionic Deploy and with the cordova-hot-code-push plugin.

In this post, we are going to look at a similar offering from Microsoft: CodePush. CodePush is part of App Center. I'm not exactly sure how many updates are covered by the free plan. I couldn't find any information on the pricing page. Maybe somebody can clarify and send me a message.

App Center Setup

Before we start coding, open App Center and sign in. If you don't already have an account, create one first.

In App Center, we have to create an app. Click on Add new app and enter a name and description. Select Cordova as platform and choose an OS. I use Android for this tutorial, CodePush also works for iOS apps.

step 1 step 2

I think the recommendation from CodePush is to create two applications if you target iOS and Android.

Create the app with Add new app.

Open the menu Distribute and click on CodePush

step 3

on this screen click on Create standard deployments

step 4

This creates a Production and Staging deployment. You can rename, add, and delete these deployments to adjust them to your specific deployment workflow.

You can close the App Center web console. From here on, we can control everything with the App Center command-line tool.

You can install these tools globally on your computer with

npm install -g appcenter-cli

I skip this step and install the CLI locally into my test project. If you manage many projects with App Center, it makes sense to install the tools globally. I only use it for one test application, so I instead prefer to install them locally.

Project Setup

The application is an Ionic / Cordova application based on the blank starter application.

To bootstrap the application I run these commands

ionic start codepush blank
cd codepush
npm install -D appcenter-cli
npm install code-push
ionic cordova prepare android
ionic cordova plugin add cordova-plugin-code-push@latest

As mentioned before, I install the appcenter-cli locally into my project. I also add the code-push JavaScript library and the cordova-plugin-code-push Cordova plugin. This is the library that checks for new updates, downloads, and installs them on the fly.

Next, we need to login to App Center from the shell.

npx appcenter login

This command opens a browser and displays a token, copy this token and paste it into the shell.

Open config.xml and check if your file contains the following configuration. If it does, you don't have to change anything.

<access origin="*" />`

But if you restricted internet access in your app to certain servers you have to add the following three addresses, so your app is allowed to communicate with the CodePush servers

<access origin="https://codepush.azurewebsites.net" />
<access origin="https://codepush.blob.core.windows.net" />
<access origin="https://codepushupdates.azureedge.net" />

Open src/index.html and add the following meta tag to the head section. This allows access to the CodePush server on CSP-compliant platforms.

<meta http-equiv="Content-Security-Policy" content="default-src https://codepush.azurewebsites.net 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *" />

Sync Code

In this section, we add code to our application that initiates the sync functionality. But before we dive into the code, we need to figure out the deployment key.

You find them in the App Center web console or get them from the command line. To get the key to my test application, I run this command.

npx appcenter codepush deployment list -a ralphschaer/TestApp --displayKeys
┌────────────┬───────────────────────────────────────┐
│ Name       │ Key                                   │
├────────────┼───────────────────────────────────────┤
│ Staging    │ Z_F0kSey7z0lrIWRr7rjobgU0qwnSJ8SY5DJV │
├────────────┼───────────────────────────────────────┤
│ Production │ x8cua6U1-BCYr2BUjlfqoSZ6Hl0fBJISY9DkE │
└────────────┴───────────────────────────────────────┘

The CodePush Cordova plugin does not automatically check and install new updates. You have to add code that controls this from your application.

Open src/app/app.component.ts and add the following code to the initializeApp() method.

  initializeApp() {
    this.platform.ready().then(() => {

      const deploymentKey = 'Z_F0kSey7z0lrIWRr7rjobgU0qwnSJ8SY5DJV';
      codePush.sync(null, {deploymentKey});

      this.platform.resume.subscribe(() => codePush.sync(null, {deploymentKey}));

      this.statusBar.styleDefault();
      this.splashScreen.hide();
    });
  }

sync() connects to the CodePush server and looks for new updates. If an update is available, it downloads the changes and either installs and restarts the app immediately (mandatory update) or only downloads the update and install it the next time the user starts the app from fresh (non-mandatory update).
This is the default behavior that you can customize with syncoptions (2nd parameter).

The first call to sync is issued when you start the app for the first time. When you resume the app from the background, the resume event is emitted, and in this example, we also call sync. There are many possibilities for how to check for new updates. You could also add a check for update button that triggers the check.

For more information about the API, visit the official documentation page:
https://docs.microsoft.com/en-us/appcenter/distribution/codepush/cordova

Instead of adding the deployment key into the code you can paste the keys into config.xml

<platform name="android">
    <preference name="CodePushDeploymentKey" value="YOUR-ANDROID-DEPLOYMENT-KEY" />
</platform>
<platform name="ios">
    <preference name="CodePushDeploymentKey" value="YOUR-IOS-DEPLOYMENT-KEY" />
</platform>

This is a more static way to configure the deployment key. Adding the keys to the code allows a more dynamic approach where users can change the deployment channel in the app.

Initial version

To test the on the fly update, we add the following code to the homepage template and install the app on a device or in an emulator. We're going to increase the version number for the update and are then able to verify if the update was successfully installed.

  <ion-card>
    <ion-card-header>
      Version
    </ion-card-header>
    <ion-card-content>
      0.0.1
    </ion-card-content>
  </ion-card>

home.page.html

Build the app and install it

ionic cordova run android --prod

You should now see this on your device or emulator. step 5

Update

Now we create an update. To simulate a change, we change the version number in the home page template.

    <ion-card-content>
      0.0.2
    </ion-card-content>

Now build the app and push the update to App Center

ionic cordova build android --prod
npx appcenter codepush release-cordova -m -a ralphschaer/TestApp -d Staging

The -m flag denotes a mandatory update. In our example, this is going to force a restart of the app immediately after the CodePush plugin downloaded the update.

To trigger the update, we need to close and open the app. When the app is in the foreground, press the home button, and open the app again. After a few seconds, you should see the updated home page.

step 6

Advanced sync() calls

If you need more insight into the update process you can provide two callback function as first and third argument to the codePush.sync() function.

codePush.sync(syncCallback?, syncOptions?, downloadProgress?);

The first callback is called when the sync task changes (from checking to downloading, from downloading to installing). The downloadProgress callback is called periodically when the plugin downloads the update package.

codePush.sync(syncStatus, null, downloadProgress);

function syncStatus(status) {
    switch (status) {
        case SyncStatus.DOWNLOADING_PACKAGE:
            // do something here
            break;
        case SyncStatus.INSTALLING_UPDATE:
            // do something here
            break;
    }
}

function downloadProgress(downloadProgress) {
    if (downloadProgress) {
      console.log("Downloading " + downloadProgress.receivedBytes + " of " +    
                   downloadProgress.totalBytes);
    }
}

With the second argument, you configure the sync() call. See the official documentation about the sync() method. In our example, we used it to provide the deploymentKey.

Note that you should not set the updateDialog option to true if your app is running on iOS. This option tells sync() to show a dialog when a new update is available.

codePush.sync(null, { updateDialog: { title: "An update is available!" } });

While Apple allows on the fly updates for Cordova apps, it is against their policy for an app to display an update prompt. There is no such restriction on the Google Play store.

Advanced workflows

sync() is the most convenient way to use the CodePush API, but if you need more control over the update process, you can call the methods that are involved in the update process individually. The API provides these following methods.

Method Description
checkForUpdate Sends a request to the CodePush server and checks if an update is available
getCurrentPackage Retrieves the metadata about the currently installed package.
getPendingPackage Retrieves the metadata for an update (if one exists) that was downloaded and installed, but hasn't been applied yet via a restart.
notifyApplicationReady Notifies the CodePush runtime that an installed update is considered successful
restartApplication Immediately restarts the app and applies a pending update.

CodePush has many additional feature. We did not cover them in this post. For example, there is an option to release an update only to a specific percentage of users, and it supports the rollback of updates. See the CodePush documentation for a complete description:
https://docs.microsoft.com/en-us/appcenter/distribution/codepush/


You find the code for this example on GitHub.