It’s alive! Get your Ionic app to update automatically (Part 2)

March 22, 2016Woody Rousseau5 min read

thumbnail

In part 1 of this tutorial, we learned how you can get your Ionic app to self-update, to deploy new code whenever needed, instead of having to wait up to two weeks for Apple to review your new versions.

Deploy channels

As with web applications, you may want to first deploy features on a test application (often called staging), to also have a preproduction application, or even to have specific versions to test latest large features (A/B testing for instance).

Ionic Deploy handles this need with channels. You can manage your channels on the Ionic Platform Dashboard's Deploy section. By defaut, the “Production” channel is used and a few others were also already created, but you can create a lot more.

Pushing environment specific updates

To push only to a certain environment, just add the --deploy flag. For instance, if you a have a staging channel, you can simply use

ionic upload --deploy staging

I recommend against using the “Production” channel as it is the one used by default when uploading a new version without specifying a deploy value.

Fetching environment specific updates

Let’s say you have an angular constant channelTag being the tag of the channel. Fetching updates only from this specific channel can be done by adding a single line to the code from the first part of the tutorial. Check this out.

.run(function($ionicPopup, channelTag) {
  var deploy = new Ionic.Deploy();
  deploy.setChannel(channelTag);
  deploy.watch().then(function() {}, function() {}, function(updateAvailable) {
    if (updateAvailable) {
      deploy.download().then(function() {
        deploy.extract().then(function() {
          deploy.unwatch();
          $ionicPopup.show({
            title: 'Update available',
            subTitle: 'An update was just downloaded. Would you like to restart your app to use the latest features?',
            buttons: [
              { text: 'Not now' },
              {
                text: 'Restart',
                onTap: function(e) {
                  deploy.load();
                }
              }
            ],
          });
        });
      });
    }
  });
};

One codebase, several applications

Using a single codebase and being able to hold all the versions of your app simultaneously on your phone can be achieved in a few extra steps:

  • You need to be able to generate specific cordova config.xml files for the different versions
  • You need to be able to generate a different channelTag constant for each version of your app

Let’s start by building a config.tpl.xml file, which is to be the template of the cordova config file. Place it on the root of your project.

config.tpl.xml

<widget xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0" id="<%=appId%>" version="<%=version%>">
  <name><%=appName%></name>
  <description>Updaty is a great app which self updates</description>
  <author email="dev@theodo.fr" href="http://theodo.fr">Theodo</author>
  <content src="index.html"/>
</widget>

A few values are to be injected in the file:

  • The app id (which looks like a reverse url such as a java package name and must match your apple developper app id)
  • The application’s version
  • The application’s name (which will appear below your app icon on the phone). I usually use the application’s real name for the production version, and shortened names containing the environment for other versions of the app.

Let’s now create a config.json file (also placed at the root of your project) which will define those values for each environment:

config.json

{
  "staging": {
    "appId": "fr.theodo.updaty-staging",
    "appName": "Updaty Staging"
  },
  "prod": {
    "appId": "fr.theodo.updaty",
    "appName": "Updaty"
  }
}

Generating the environment specific files

A simple gulpfile is enough to generate all the files you need. Pick up the following libraries to start off:

npm install --save-dev gulp-ng-constant gulp-ionic-channels yargs

gulpfile.js

var gulp = require('gulp');
var ionicChannels = require('gulp-ionic-channels');
var ngConstant = require('gulp-ng-constant');

var args = require('yargs').default('channelTag', 'staging').argv;

gulp.task('config', function() {
  gulp.src('./config.json')
  .pipe(ionicChannels({
    channelTag: args.channelTag
  }))
  .pipe(ngConstant())
  .pipe(gulp.dest('./www/js'));
});

Simply running gulp config will generate ./config.xml and ./www/js/config.js for the staging channel tag, and gulp config --channelTag prod will do the same for the prod channel tag.

config.xml (output)

<widget xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0" id="fr.theodo.updaty-staging" version="0.0.1">
  <name>Updaty Staging</name>
  <description>Updaty is a great app which self updates</description>
  <author email="dev@theodo.fr" href="http://theodo.fr">Theodo</author>
  <content src="index.html"/>
</widget>

www/js/config.js (output)

angular.module("config", [])

.constant("appId", "fr.theodo.updaty-staging")

.constant("appName", "Updaty Staging")

.constant("version", "0.0.1")

.constant("channelTag", "staging")

;

gulp-ionic-channels

I made it all easy for you with this gulp plugin I developed, which takes the config.json file as source, adds to it the version from your package.json file as well as the channelTag passed as an argument, and uses it to:

  • Pass the enriched (and environment filtered) configuration to the next gulp pipe
  • Generate from the template (by default it looks for ./config.tpl.xml) a cordova configuration file (by default ./config.xml).

gulp-ng-constant

This useful plugin will generate a javascript file which contains all the angular constants you need from the enriched configuration returned by gulp-ionic-channels, such as the channelTag constant.

Final modifications

You finally need to edit

  • your ./www/index.html file to include the file generated by gulp-ng-constant:
<!-- your app's js -->
<script src="js/config.js"></script>
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
<script src="js/services.js"></script>
  • your ./www/js/app.js file to include the module generated by gulp-ng-constant:
angular.module('updaty', ['config', 'ionic', 'ionic.service.core', 'updaty.controllers', 'updaty.services'])

You should be good to go!

Woody Rousseau

Woody Rousseau

Web Developer at Theodo