Home | Send Feedback

Hot deploy updates with the cordova-hot-code-push plugin

Published: January 18, 2017  •  Updated: May 18, 2017  •  ionic3, cordova

DEPRECATED: Unfortunately cordova-hot-code-push is deprecated since 2018-09-30
https://github.com/nordnet/cordova-hot-code-push/issues/371



In the previous two posts about Ionic Deploy and CodePush we explored the possibilities of updating Cordova applications directly over the air without re-submitting them to the mobile app stores.

In this article we're going to look at another solution to send updates directly to a Cordova app. This time it's a Cordova plugin without any backend service. You have to host the updates on your own server or on a third party service that provides static file hosting (i.e. Amazon S3, Firebase Hosting).

The plugin is called cordova-hot-code-push and you find the source code on GitHub:
https://github.com/nordnet/cordova-hot-code-push

Initial Version

Let's start with the initial version of our demo app. We create an app based on the blank starter template, add the Android platform and install the plugin.

ionic start hotcodepush blank
cd hotcodepush
ionic cordova prepare android
ionic cordova plugin add cordova-hot-code-push-plugin
npm install -D cordova-hot-code-push-cli

The cordova-hot-code-push plugin needs some additional files with metadata information to work properly. The CLI we install with the last command helps create these files.

Then we add some code to the home template file. This allows us to check if the update process works.

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

home.html

Next we call the init command of the CLI to initialize the plugin. This command creates a file called cordova-hcp.json in the root of the project. The CLI is going to use this information later when it creates the metadata files for the update.

npx cordova-hcp init

The init command asks several questions.

Running init
Please provide: Enter project name (required):  hotcodepush
Please provide: Amazon S3 Bucket name (required for cordova-hcp deploy):
Please provide: Path in S3 bucket (optional for cordova-hcp deploy):
Please provide: Amazon S3 region (required for cordova-hcp deploy):  (us-east-1)
Please provide: IOS app identifier:
Please provide: Android app identifier:  
Please provide: Update method (required):  (resume)
Please provide: Enter full URL to the directory where the build is going to be uploaded:  https://demo.rasc.ch/hcp
Project initialized and cordova-hcp.json file created.
If you wish to exclude files from being published, specify them in .chcpignore
Before you can push updates you need to run "cordova-hcp login" in project directory

The CLI has built-in support for Amazon S3, but I'm going to host the update files on my own server, therefore I leave the answers to the Amazon S3 questions empty. When you don't have your own server you could start a local HTTP server and open a ngrok tunnel. The identifiers are optional too. Add them if you want to use the "Request application update through the store" feature.

The plugin supports three update methods:

The last question asks for an URL where the update is going to be located.

Next we need to configure the plugin. Open the file config.xml and add these lines. The URL should be the same we provided to the init command with the path /chcp.json attached.

<chcp>
 <config-file url="https://demo.rasc.ch/hcp/chcp.json"/>
</chcp>

The URL specifies the location where the plugin finds the update metadata file. We're going to create this file in the next step with the npx cordova-hcp build command. By default, the plugin does everything automatically. We don't have to add any TypeScript code. Everything is in place and we can build and install the initial version of our app.

ionic cordova build android --prod
npx cordova-hcp build
cordova run android
// cordova emulate android

It's important that we don't forget to call npx cordova-hcp build. This command creates the chcp.json and chcp.manifest files in the www folder and they need to be packaged together with our app. The chcp.json file contains information about the update, most importantly the release tag. This string has to change with every update.

{
 "name": "hotcodepush",
 "ios_identifier": "",
 "android_identifier": "",
 "update": "resume",
 "content_url": "https://demo.rasc.ch/hcp",
 "release": "2017.01.18-17.26.42"
}

The chcp.manifest file lists all the files in the www folder. The plugin only downloads files onto the device that have changed. To determine a change it compares the hash code of the stored file with the hash in the manifest file.

[
 {
   "file": "assets/fonts/ionicons.eot",
   "hash": "bdf1d30681cf87986c385eea78e8de9a"
 },
 {
   "file": "assets/fonts/ionicons.scss",
   "hash": "d500900a39e39e0b966a5ed077e97a66"
 },
.....
]

Update

Next we create an update in our app by changing the version string in the home page template file.

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

Now we build the app and run the cordova-hcp build command again. It's important that this command is called every time we want to release an update.

ionic cordova build android --prod
npx cordova-hcp build

Next we need to copy the complete www folder to the update location. To check if the paths are correct open the URL you entered in the config.xml in a browser. For this example it's the URL https://demo.rasc.ch/hcp/chcp.json. If you see the JSON in the browser, everything is okay.

The plugin checks for new updates every time the app launches or when the user resumes the app from the background. And because we set the update method to resume, an update is only going to be installed after a resume from the background. When the initial app is in the foreground press the home button and open it again. This triggers the update check and the plugin downloads the changed files. To install the update we need to send the app again back to background and open it again. You should see the changed string on the home screen (0.0.2).

If we change the update method to now ("update": "now") an update would be applied immediately after the download.

Manual mode

As seen in the example above, the plugin does everything automatically. We didn't have to write a single line of code for the update process to work. But if you need more control over the process you can disable the automatic mode and do everything manually in the TypeScript code.

To turn off the automatic update we add these lines to the config.xml file. We no longer need the config URL because we are setting the URL in the code.

<chcp>
 <auto-download enabled="false" />
 <auto-install enabled="false" />
</chcp>

As an example, in the file app.component.ts we add a call to fetchUpdate in the constructor and we add a resume listener that also calls fetchUpdate. So in this example, the app checks for updates when it starts up and each time the user resumes the app from the background.

@Component({
  templateUrl: 'app.html'
})
export class MyApp {
  rootPage: any = HomePage;

  constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) {
    platform.ready().then(() => {
      statusBar.styleDefault();
      splashScreen.hide();
    });

    // manual mode. remove this for automatic mode
  this.fetchUpdate();
    platform.resume.subscribe(() => this.fetchUpdate());
  }

  fetchUpdate() {
    const options = {
      'config-file': 'https://demo.rasc.ch/hcp/chcp.json'
    };
    chcp.fetchUpdate(this.updateCallback, options);
  }

  updateCallback(error, data) {
    if (error) {
      console.log('Failed to load the update with error code: ' + error.code);
      console.log(error.description);
    } else {
      console.log('Update is loaded');

      chcp.installUpdate(error => {
        if (error) {
          console.log('Failed to install the update with error code: ' + error.code);
          console.log(error.description);
        } else {
          console.log('Update installed!');
        }
      });
    }
  }

}

app.component.ts

The chcp.fetchUpdate method downloads an update if it's available. It throws an error with the code -1 if it does not find an update. After the plugin successfully downloaded an update, the code calls chcp.installUpdate to apply the update. The app immediately restarts and presents the new content.


You find the complete source code for this project on GitHub.