Using CodePush with NativeScript

Would you like to be able to dynamically update your NativeScript app that is already deployed to a phone and/or a tablet? Think about it for a moment… You could just push a quick update without having to go through Google Play or Apple's App Store.

This is where the nativescript-code-push project comes in. It enables your apps to communicate with CodePush services allowing you to push updates to the app and dynamically update the code of the app.

In this article, I'm going to explain what the CodePush service allows (and its current limitations), and show you how to use the nativescript-code-push in a NativeScript app.

What Is CodePush

CodePush allows you to push JS, HTML/XML and CSS file updates together with all of the images and other resources that get bundled at the time of tns build.

The main limitation of CodePush has to do with pushing native code updates that would require the app to be recompiled. These would be changes such as updating the NativeScript, tns-android or tns-ios versions. In these cases, you would still need to rebuild the app and publish the update via the usual channels (app stores).

CodePush Demo

The CodePush Workflow

  1. Create a CodePush app – this is a placeholder where you push all your updates.
  2. Build an app with NativeScript and push the new changes to your CodePush app.
  3. Your app checks for updates. If an update is available, then the app downloads the changes and updates its code.
  4. The user restarts the app.
  5. The app contains all the updates –> The app checks for updates.

CodePush Setup

In order to make CodePush work we need to first setup CodePush CLI:

npm install -g code-push-cli  

Then, using the code-push CLI, create a new CodePush account:

code-push register  

If you already have an account with CodePush, just run the below command that will let you authenticate with your web browser.

code-push login

Preparing your project for CodePush

Now that the code-push CLI is up and running, it is time to add it to your project. Let's start by adding the nativescript-code-push module to our NativeScript project with:

tns plugin add nativescript-code-push

Next, we need to use the CodePush CLI to register our app. Please note that we need to do it for each platform. This is because of all the shared modules (such as tns-core-modules) have different implementations for iOS and Android – this is how we share the iOS and Android code.

It is up to you how you name your CodePush app projects, but the way I prefer to do it is as projectName-ios and projectName-android. For example:

code-push app add footy-ios
code-push app add footy-android

Calling each of these command will provide you with two keys, one for production and one for staging. In this tutorial we will use the staging keys. If you forget your keys you can retrieve them by using the following command:

code-push deployment ls footy-ios -k

Enabling CodePush in a Project

Now comes the fun part. First, let's start with importing the necessary  CodePush bits and isIOS (which helps us deliver the right key):

import { CodePush, SyncStatus } from 'nativescript-code-push';
import { isIOS } from 'platform';

Now we just need call CodePush from within our app to see if there are any updates available.

The Basic Call

The easiest way to do it is with the following code:

public syncWithCodePushServer() {
  const syncKey = (isIOS) ? 'codepush-key-here-IOS' : 'codepush-key-here-ANDROID';
  CodePush.sync({ deploymentKey: syncKey });
}

This is enough to call the Service to look for updates, download them and update the app. Use the staging key if you want to try it out with your project.

Adding the Callback

You can also provide a callback function that gets triggered at each stage of the CodePush update cycle. The callback will provide you with a sync status that you can use to call an alert to see what is happening.

public syncWithCodePushServer() {
  const syncKey = (isIOS) ? 'codepush-key-here-IOS' : 'codepush-key-here-ANDROID';

  CodePush.sync(
    { deploymentKey: 'codepush-key-here' }, 
    (syncStatus: SyncStatus) => {
      alert('Sync Status:' + syncStatus);
    }
  );
}

This approach is useful when you are adding CodePush to your project in order to make sure that it all works as expected. However, for a production ready app, you shouldn't overwhelm your users with a ton of messages that they might not be interested in.

You should, however, look for SyncStatus.UP_TO_DATE to prompt the user to restart the app.

public syncWithCodePushServer() {
  const syncKey = (isIOS) ? 'codepush-key-here-IOS' : 'codepush-key-here-ANDROID';

  CodePush.sync(
    { deploymentKey: syncKey }, 
    (syncStatus: SyncStatus) => {
      console.log('Sync Status:' + syncStatus);
      if (syncStatus === SyncStatus.UPDATE_INSTALLED) {
        alert("CodePush: update installed, kill/restart your app to see the changes");
      }
  });
}

It is entirely up to you how you notify the user to restart the app. You could even use the nativescript-toast plugin to make it look even better.

Triggering the Sync Process

Depending on when and how often you would want to your app to look for updates you might want to hook to different event. You could simply add a button in the UI to let the user check for updates, which would call codePushSync.nOr you could try to tie in to some default events.

For example you could use the resumeEvent to trigger the process each time the app is resumed.

import * as application from 'application';

application.on(application.resumeEvent, () => {
  this.syncWithCodePushServer();
});

Angular

In Angular the most common location for a quick CodePush.sync call would be at ngOnInit of your first view component. This way your app would run the sync process just at the beginning.

export class HomeComponent implements OnInit {
  // the contents of the component

  ngOnInit() {
    this.syncWithCodePushServer();
  }
}

Deploying the app

First we need to deploy the app to a device. To deploy the app you should build it with the --no-watch flag. This is needed as NativeScript livesync might conflict with the CodePush sync.

tns run android --no-watch
tns run ios --no-watch

Once the app is deployed you can kill your current tns run process.

Pushing the updates

Now is the moment of truth. Let's make some changes to the app and rebuild it.

tns build android --clean
tns build ios --clean

To push updates we need to go the respective Android and iOS build folders and push all the assets from there, using the release command:

code-push release code-push-app-name-here app "*"

Android

Go to platforms/android/src/main/assets/ and run:

cd platforms/android/src/main/assets/
code-push release footy-android app "*"

iOS

Go to platforms/ios/<nativescript-appname></nativescript-appname> and then:

cd platforms/ios/nativescriptfooty/
code-push release footy-ios app "*"

CodePush CLI release iOS

CodePush in action

Now go back to your device. Make the app check for updates and when the updates are ready, kill the app and restart it.

CodePush Demo

Conclusion

You shouldn't try to use CodePush as a replacement for Google Play or Apple AppStore. Also please make sure that the updates that you are providing don't change the domain of the app (i.e. changing your app from picture gallery to fitness tracker), as described in Section 3.3.2 of iOS Program Information.

Nonetheless, using CodePush isn't difficult. Following this guide you should be able to have it working in less than 20 minutes. It offers you a great way to streamline the way that you provide your users with the latest updates.

Header image courtesy of Susan Blase

Comments