Life of a Push Notification

Convert to Slide Deck

Talk plan for SDC Length 15-15mins, with demo. This is the short version

Introduction

I’m Ada Edwards from the Financial Times, I’m here to talk about Progressive Web Apps.

Note to self: Speak slowly no one can understand your accent.

I’d like to ask some questions before I start.

Progressive Web Apps

Life of a push notification

Ada Rose Edwards - Financial Times

Follow along at home

There are a lot of code heavy slides.

I don’t have time to step through them in detail but you can read along at home.

Notes and slides:

Progressive Web Apps

Progressive Web Apps are not new. The Financial Times chose to use a Web App for digital content delivery on mobile devices since 2012.

Moving to a Web App enabled the same app to ship across platforms using a single distribution channel.

It also allowed us to bypass app stores.

First Version of the FT Web App

Web App Demo

https://81.ada.is

Installing a Web App

The most important difference between a web app and a regular app is that

A web app is not installed via an app store

You install a web app straight from the browser.

What is a progressive web app?

Note to self: Remember to read the descriptions don’t just rush through!

A progressive web app is a type of website which exhibits certain app-like properties.

(Mind map slide)

http://labs.ft.com/2012/06/what-exactly-is-an-app/

*In this talk I intend to cover in detail creating push notifications.

Appiness🐵📱💻http://👉🔔e 🍏

Topics I am going to cover

But before we can run we need to walk so there is lots to cover.

The Web App Manifest

The Web App Manifest

Web App Manifest

This is an example Web App manifest for a demo I built for this talk.

The top part of the manifest down to the line break is standard.

But the last three properties are Chrome specific, the one pertinent to push notifications is gcm_sender_id without this the app won’t even try prompting for push notifications.

The GCM in gcm_sender_id stands for Google Cloud Messaging.

Google cloud messaging handles all of the push notifications for Chrome behind the scenes.

So we need to set this up before we can begin.

client/manifest.json:

{
  "name": "81 Emoji",
  "icons": [
    {
      "src": "launcher-icon-4x.png",
      "sizes": "192x192",
      "type": "image/png"
    }
  ],
  "start_url": "/?standalone",
  "display": "standalone",
 "orientation": "portrait",

 "gcm_sender_id": "90157766285",
  "background_color": "lavenderblush",
  "theme_color": "lavenderblush"
}

Google Cloud Messaging

https://console.developers.google.com

Google Cloud Messaging, in the dashboard

Setting up a Service Worker

A worker is a piece of JavaScript which runs in a separate thread to your main page.

The service worker is a special shared worker.

It acts like a proxy between browser tabs and the larger web allowing one to rewrite requests and responses on the fly.

If often gets compared to AppCache but that is doing it a disservice.

It is a programmable proxy, right there in your borwser.

The service worker (and by extension push notifications) are unavailble on iOS but you can progressively enhance offline support by using AppCache.

The service worker will displace AppCache when it is installed.

Setting up the Service Worker

Offlining, and so much more!

The Service

Once it is installed:

The service worker can outlive your open tabs and the browser will start your service worker when it receives a push notification.

One can use Service Workers to make your Web App work offline, by intercepting requests and serving them from the cache whilst quietly updating the cache in the background.

This will make your site robust on poor wireless networks, e.g. conferences or whilst roaming and speed up access to Cached content.

The service worker is only available over https because it’s so powerful. On http a malicious third-party could change your site and cache it forever

                +-------------------------------------------------+
                |                               (Cats)            |
                |    The Internet         )\._.,--....,'``.       |
                |                        /;   _.. \   _\  (`._ ,. |
                |                       `+--+(,_..'++(,_..'`+.;.' |
                +---------------------^--+------------------------+
                                      |  |
+-------------+ +---------------------+--v------------------------+
|             +->                                                 |
|  Cache API  | |   Service Worker                                |
|             <-+                                                 |
|             | +---------+--^-----------------------^--+---------+
|             |           |  |                       |  |
|             | +---------v--+---------+    +--------+--v---------+
|             | |                      |    |                     |
|             +->         Tab 1        |    |        Tab 2        |
|             <-+                      |    |                     |
+-------------+ +----------------------+    +---------------------+

Starting the service worker is different from starting a regular worker, and it provides a promise for when it’s ready to use.

We will delve into the contents of the service worker later when we discus handling push notifications.

client/scripts/lib/sw.js

function setUpServiceWorker() {

	if ('serviceWorker' in navigator) {

		if (navigator.serviceWorker.controller) {

			// service worker registered.
			return navigator.serviceWorker.ready;
		} else {

			// service worker not registered
			return navigator.serviceWorker.register('/sw.js')
			.then(() => navigator.serviceWorker.ready);
		}
	}
}

Requesting permission to receive push notifications.

Now we have API access and a service worker we can delve into the code and start asking the user to receive the notification.

Requesting Permissions

To the user requesting push notification permission is a two-part process.

The first request needs to come from a user interaction such as a ‘click’ event.

The browser then shows it’s own “request permission” box to the user.

Ask In App Then Ask In Chrome

Subscribing

Subscribing is more complex but it’s not that ugly.

  1. The user has requested a push notification subscription via a click event.
  2. We show a spinner on the banner to show something is going on.

  3. Firstly we trigger subscribe on the service worker registration.
  4. This has an optional parameter. Where optional means include it if you want it to work.
  5. “userVisibleOnly indicates that the push subscription will only be used for push messages whose effect is made visible to the user, for example by displaying a Web Notification. “

  6. At this point if we haven’t already got push permission the browser will request it
  7. if all was successful great, we’re done. Hide the banner.
  8. We can post the notification details to the server for using later.

  9. Otherwise something went wrong, either the user refused to allow push notifications.
  10. Or we missed a step setting everything up.

  11. Finally remove the spinner from the banner

// get the serverWorkerRegistration object from the earlier instantiation promise
swPromise
.then(
	serviceWorkerRegistration =>
		serviceWorkerRegistration
		.pushManager
		.subscribe({
          // Required parameter
			userVisibleOnly: true
		})
)
.then(function(subscription) {

	// Success we're done so hide the banner
	pushBanner.style.display = 'none';
	return sendSubscriptionToServer(subscription);
})
.catch(function(e) {
	if (Notification.permission === 'denied') {

		// User says no
	} else {

		// A problem occurred with the subscription; common reasons
		// include network errors, and lacking gcm_sender_id and/or
		// gcm_user_visible_only in the manifest.
	}
});

The subscription details.

The subscription details provide an endpoint which the server side can query to send a push notification.

Send this information to your server so it can send push notifications later.

https://developer.mozilla.org/en-US/docs/Web/API/PushSubscription

{
 "endpoint": "http://example.com/some/uuid"
}

In this case the url looks something like:

"https://android.googleapis.com/gcm/send/ cz6YgbRXHAc:APA91bGWtm35_kAQsZEn_Ye…EVXj1 MDXGulbtBWJYw4AGcIWXq7p5BjhFhnDhMQoqOHRzY 9jI_OeOn_DQ5W_cYD5tCDDdjOD7d"

Sending a push notification

Now we have an endpoint we can use to fire off notifications when ever we want.

Each endpoint will provide a different address, for the case of Chrome and the Samsung browser it’s google cloud messaging.

There is spec for people to host their own

This is where we break out the API key we got when setting up our project for Google Cloud Messaging.

Sending a push notification

Sending a push notification Details

The google one is handled a little differently you can’t just fire a request at that URL to get a push notification.

You have to form a post request with your API key in the header and all of the final part of the url in the body.

Here it is as a cURL request.

POST request to https://android.googleapis.com/gcm/send

 	Content-Type: "application/json",
 	Authorization: "key=AIzaSyAc228MeZHA5NfhPANea01wnyeQD7uVY0c" (not a real API key)

Post Body:

{
  "registration_ids": [
    "APA91bE9DAy6_p9bZ9I58rixOv-ya6PsNMi9Nh5VfV4lpXGw1wS6kxrkQbowwBu17ryjGO0ExDlp-S-mCiwKc5HmVNbyVfylhgwITXBYsmSszpK0LpCxr9Cc3RgxqZD7614SqDokwsc3vIEXkaT8OPIM-mnGMRYG1-hsarEU4coJWNjdFP16gWs"
  ]
}

As a curl

cURL

curl --header "Authorization: key=AIzaSyAc2e8MeZHA5NfhPANea01wnyeQD7uVY0c" --header "Content-Type: application/json" https://android.googleapis.com/gcm/send -d "{\"registration\_ids\":[\"APA91bE9DAy6\_p9bZ9I58rixOv-ya6PsNMi9Nh5VfV4lpXGw1wS6kxrkQbowwBu17ryjGO0ExDlp-S-mCiwKc5HmVNbyVfylhgwITXBYsmSszpK0LpCxr9Cc3RgxqZD7614SqDokwsc3vIEXkaT8OPIM-mnGMRYG1-hsarEU4coJWNjdFP16gWs\"]}"

(not a real API key)

Receiving push notifications

When the client receiving the response from the Push Notification Server the browser

fires an event on the serviceworker, even if there are no windows open!!

Receiving Push Notifications

Receiving push notifications service worker (code).

So this code runs in the service worker to handle push notifications.

Sorry this is the last long chunk of code.

Then I can show it to you working.

  1. This takes place in the Event Listener for push events.
  2. When the phone receives a push notification for this app it fires this event on the service worker.
  3. We haven’t sent any information with the notification we will get the latest message from the api.
  4. We will then format the message as a notification.

  5. The final part is what to do when the user interacts with the click
  6. Here we close the notification (you don’t have to)
  7. try to focus an existing window
  8. or open a new one

In the service worker:

// Setup Event Listener
self.addEventListener('push', function(event) {

	// The event is completed once
  // we have requested the lates post from the API
  // used it to display the notification to the user
	event.waitUntil(
		getMessageDetails()
		.then(function (details) {
			self.registration.showNotification(details.title, {
				body: details.message,
				icon: '/my-app-logo-192x192.png'
			}));
		})
	);
});

// We can even handle how it behaves on click.
self.addEventListener('notificationclick', function(event) {

	// Close the notification
	event.notification.close();

	// This looks to see if any windows are open and tries to focus one.
	// Otherwise it just opens a new window.
	event.waitUntil(clients.matchAll({
		type: 'window'
	}).then(function(clientList) {
		for (let i = 0; i < clientList.length; i++) {
			const client = clientList[i];
			if ('focus' in client) {
				return client.focus();
			}
		}
		if (clients.openWindow) return clients.openWindow('/');
	}));
});

Push Demo

Notice the notification arrives when the phone is off and the app is closed.

Summary of what we covered

  1. The Web now supports making Apps. (It’s fun)
  2. Making a Web App requires https because the APIs are powerful.
  3. A web app manifest describes your app.
  4. A service worker or AppCache can make it work offline.
  5. A service worker additionally allows you to send push notifications.

Thanks

Thank you for listening I hope you have a successful app

Thank you for listening

M. Night Shyamalan twist ending!!

These slides were a progressive web app all along!!

Good Reading

Loads on MDN, this covers lot though: https://developer.mozilla.org/en-US/docs/Web/API/Push_API/Using_the_Push_API

Image Sources