Writing web apps for Google Chrome using Boxcar Push Services

With the recent announcement of push notification support on Google Chrome for Android devices a new horizon of web applications is emerging. Even if both push and notification drafts are still going through continuous changes, we have published a beta version of our Boxcar SDK for Google Chrome browsers.

General summary of push notifications on Google Chrome

A month ago we read on the Chromium blog about the adoption of the emerging Push API standard and Notification API standard on Chrome 42 for Android devices. We recommend this detailed blog post on HTML5 Rocks explaining how to implement web applications for Chrome with support for push notifications.

One of the interesting features of web applications for Android Chrome is the capability to add these to your device home screen, emulating a native Android application. This feature is known as Add to Homescreen and looks promising in that it would allow to implement virtual multiplatform applications from a single web site, as long as Google Chrome features are available on all mobile platforms. So far push notifications aren’t yet available on Chrome for iOS.

A word of caution

Take into account that both Push and Notification APIs among other javascript standards are still drafts and as such they are constantly changing. On the other hand, not all browsers currently adopt these (currently Chrome and Firefox stand at the forefront on this subject) and usually they do it partially.

In the case of Google Chrome, it implements the Web API for supporting payloads, but the Chrome team decided to not ship it because they want to mandate encryption for any received push message that has a payload. In other words, Chrome browsers won’t receive any information in the data field of the incoming push. At the moment, a push from the Chrome application standpoint is just a signal that “something” happened.

Boxcar integration

Even with these limitations, we thought it was interesting to start an early SDK implementation to use Boxcar Push Service for Google Chrome web applications. You can check the source code at our GitHub repository.

Before starting your Chrome web application ensure you have already created a project into Boxcar Developer Console and at least a client application of Android type. For reference about how to perform this step please check our Projects Settings documentation.

The SDK consists of two javascript files but the API to interact with Boxcar is found at boxcar.js. The project is managed by Gulp: after configuring it you can issue the following command

1
gulp js

which generates a single minified file at sample/script/boxcar.bundle.min.js ready for use on your Chrome web application. In the sample/ folder of the source repository you can see a minimal example showing how to integrate it.

The following snippet taken from main.js on the sample application shows how the SDK can be integrated to enable pushes from Boxcar:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
window.addEventListener('load', function() {
    var pushButton = document.querySelector('.js-push-button'),
        credentials = new BoxcarCredentials(BOXCAR_CLIENT_KEY,
                BOXCAR_CLIENT_SECRET),
        pushHost = new BoxcarPushHost('boxcar-api.io'),
        config = new BoxcarConfig(credentials,'./service-worker.js',
                SAMPLE_APP_VERSION,pushHost),
        boxcar = __initBoxcar(config);

    /**
     * Add logic to register / unregister button
     */
    pushButton.addEventListener('click', function() {
      if (!boxcar.isPushEnabled()) {
          boxcar.register(BOXCAR_PROJECT_TAGS, BOXCAR_CLIENT_USERNAME).then(
              function() {
                  console.debug('Registration success');
                  updateButton(true);
              },
              function(err) {
                  console.error(err);
                  updateButton(boxcar.isPushEnabled());
              }
          );
      } else {
          boxcar.unregister().then(
              function() {
                  console.debug('Unregister success');
                  updateButton(false);
              },
              function(err) {
                  console.error(err);
                  updateButton(boxcar.isPushEnabled());
              }
          );
      }
    });
    
    updateButton(boxcar.isPushEnabled());
    
    /**
     * Retrieve the list of project tags from Boxcar service
     * and show it in the HTML page.
     * TODO: show a multiple selection list with each tag
     * and use this selection to send the registration.
     */
    boxcar.getTags().then(
            function(tags) {
                for (var i = 0; i < tags.length; i++) {
                    var tagsDiv = document.querySelector('.js-tags'),
                        str = '';
                    for (i = 0; i<tags.length; i++) {
                        str = str.concat(tags[i])
                                 .concat(';');
                    }
                    tagsDiv.textContent = str;
                }
            },
            function(err) {
                console.error(err);
            }
    );

    /**
     * Check if push is supported by this browser.
     * Disable registration button if it doesn't.
     */
    boxcar.checkSupported().then(function(){
        pushButton.disabled = false;
    }).catch(function(err){
        console.error(err);
    });
    
});

The first thing to do is to initialize the boxcar object, which receives a configuration instance holding the necessary parameters to connect with your Boxcar project. Here the constants BOXCAR_CLIENT_KEY and BOXCAR_CLIENT_SECRET hold the respective access key and secret for your Boxcar client application (of Android type).

You have probably noticed we pass a string with the value './service-worker.js' to the BoxcarConfig object. This is a relative URL pointing to your service worker. ServiceWorker API is another recent addition of Chrome browser and a requirement to support push notifications. Why? Because a ServiceWorker is a background task that runs isolated from your web application UI and has the responsibility to handle incoming pushes from GCM (in our scenario: pushes sent by Boxcar) even if the browser is not in foreground or there is no tab currently browsing your web application. Basically you will handle at least two different events: incoming pushes and user clicks on the notification widget.

The global function __initBoxcar returns an instance of the boxcar API which offers the following API. In most cases it relies on Promise instances which we will explain later:

boxcar.checkSupported()

This method returns a Promise which simply succeeds or fails depending on the capability of the browser to support service workers, notifications and pushes.

boxcar.register(tags, username)

Tries to register to Boxcar Push Service on the specified array of tag strings. Here username is just a freeform string identifying the user. This function returns a Promise that will succeed or fail according to the registration result.

Part of the job performed by this method is to retrieve a registrationId or token from Google GCM, the gateway that Chrome supports to receive pushes.

boxcar.unregister()

This method is the counterpart of the previous one, returning a Promise to unregister from both GCM and Boxcar Push Service.

boxcar.trackNotification(notificationId)

Currently this method is not useful due to the limitations of Chrome Push API implementation which doesn't support payloads in the incoming notification. However when available, this method would allow to track to the Boxcar server the action of a user opening an specific push.

boxcar.siteVisited()

Returns a Promise of tracking user activity on the web application to the corresponding Boxcar project.

boxcar.isPushEnabled()

Returns true if this browser is currently registered to Boxcar Push Service.

boxcar.getTags()

Returns a Promise which retrieves the list of tags available on the associated Boxcar project.

What? Promises?

You have probably noticed that most of the methods explained above return a Promise. Promises were natively included in ECMA Script 6, which is the engine used by latest Chrome browser. There are a lot of resources on the web explaining the advantages of this new pattern, but I would recommend to start with this article by Jake Archibald and this one which is older (prior to ECMA Script 6 adoption) but focuses on the sugar behind this new tool.

In short terms we could say promises are enhanced callbacks which ensure we will not miss an event, even if it did happen before you had the opportunity to register to it. They can also be chained and are less verbose than normal callbacks.

If you look the snippet shown above you will notice that after calling methods that return a Promise we call the method then or catch. This is why promises are also called thenables. A then() method expects one or two function callbacks. The first will be called if the operation went fine, the second if it did fail. However if you don’t care about every possible failure in a promise chain, you can use the catch() method. All these subtleties are explained in the referenced documentation.

Definig a manifest.json for your web application

Manifests are the way used by modern browsers to describe a web applications. It is a text file with in JSON format describing metadata about the application. We recommend this article by Paul Kinlan for further reading. Chrome requires the use of a manifest file to support push notifications.

The next snippet shows the manifest.json file defined for the sample application:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
  "name": "Boxcar Push",
  "short_name": "BP",
  "icons": [{
        "src": "images/icon-192x192.png",
        "sizes": "192x192"
      }],
  "start_url": "/index.html?homescreen=1",
  "display": "standalone",
  "gcm_sender_id": "<YOUR PROJECT NUMBER>",
  "gcm_user_visible_only": true,
  "permissions": ["storage"]
}

The key properties here are start_url, gcm_user_id and gcm_user_visible_only. start_url is used by the container (i.e. Google Chrome) to know what is the home screen of your web application. If you add this web application to your Android desktop and then click on the application launch icon, this will be the first screen shown when open. gcm_user_id is the senderId of your Google project, explained here. gcm_user_visible_only indicates that you promise to show a notification whenever you receive a push.

Then, on your web application page you must link the manifest file:

1
<link rel="manifest" href="manifest.json">

On the other hand, allowing the web application to be added to your device desktop requires this element too:

1
<meta name="mobile-web-app-capable" content="yes">

What's next?

We recommend to check the sample application at our GitHub repository in the sample folder for integration details. This sample was based mainly on this one published by Google Chrome, with the obvious exception that we also show how to register to Boxcar Push Service.

There are plenty of things that are happening in the web application world, we hope this article is just the start of a series showing the progress on the adoption of rich features like push notifications. In the meantime, we hope you have fun coding web applications!.