Talk plan for SDC Length 15-15mins, with demo. This is the short version
I’m Ada Edwards from the Financial Times, I’m here to talk about Progressive Web Apps.
I’d like to ask some questions before I start.
Progressive Web Apps
Life of a push notification
Ada Rose Edwards - Financial Times
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 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.
https://81.ada.is
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.
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 🍏
But before we can run we need to walk so there is lots to cover.
- Describing your web app with the Web App Manifest
- Offline support with a Service Worker
- Sending and Receiving Push Notifications
Specifically we are looking at producing a web app for the Chromium 44 based Samsung browser.
The Web App Manifest defines various properties of a Web App, e.g.
The 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
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!
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); } } }
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.
Then
Subscribing is more complex but it’s not that ugly.
We show a spinner on the banner to show something is going on.
“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. “
We can post the notification details to the server for using later.
Or we missed a step setting everything up.
// 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 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"
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
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" ] }
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)
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
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.
We will then format the message as a notification.
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('/'); })); });
Notice the notification arrives when the phone is off and the app is closed.
- The Web now supports making Apps. (It’s fun)
- Making a Web App requires https because the APIs are powerful.
- A web app manifest describes your app.
- A service worker or AppCache can make it work offline.
- A service worker additionally allows you to send push notifications.
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
- Wikimedia
- https://www.flickr.com/photos/dreynolds/6930648214