1 of 88

Progressive Web Apps — An Introduction

July 4, 2018

Thomas Steiner

Developer Advocate

Google Hamburg

@tomayac

Confidential + Proprietary

Confidential + Proprietary

2 of 88

Progressive Web Apps — An Introduction to Some of the Concepts of PWA

July 4, 2018

Thomas Steiner

Developer Advocate

Google Hamburg

@tomayac

Confidential + Proprietary

Confidential + Proprietary

3 of 88

Source: comScore

Global web users (millions)

4 of 88

vs.

Source: comScore Mobile Metrix, U.S., Age 18+, June 2017

13%

87%

Mobile web

Apps

5 of 88

78%

Source: comScore Mobile Metrix (Custom), U.S., Age 18+, June 2017

of time spent is in�users’ top 3 apps

6 of 88

ZERO

Source: comScore MobiLens, U.S., Age 13+, 3 Month Average Ending June 2017

Number of apps the average user installs per month

7 of 88

So, what exactly is a progressive web app?

Fancy name for "Modern Website" that takes advantage of new APIs and builds experiences users expect

Reliable

Fast

Engaging

  • Loads instantly (< 3s TTMP)
  • Works offline
  • Works on flaky connections
  • Interactive quickly (< 5s)
  • Responsive to any input (<100ms)
  • Repeat visits are near instant (<1s)
  • Can be added to Home Screen
  • Launch in Fullscreen
  • Consistent UI
  • Re-engage with Push Notifications

Confidential + Proprietary

8 of 88

Add to Homescreen

Make it easy for users to come back

Trivago: engagement for users who add to homescreen has increased by 150%

9 of 88

Push Notifications

Re-engage users

10 of 88

Offline

Never see the dinosaur again

11 of 88

Enable reliable experiences with the service worker

Web server

The old way...

Confidential + Proprietary

12 of 88

Enable reliable experiences with the service worker

Web server

The old way...

Confidential + Proprietary

13 of 88

Enable reliable experiences with the service worker

Web server

Service worker

Cache API

Client Device

… the new way

Confidential + Proprietary

14 of 88

Advanced caching strategies with service worker

“Cache then network”

Confidential + Proprietary

15 of 88

App Shell Model

Keep your app reactive, predictable and in control

Avoid overly “web-like” design

  • Content shouldn’t jump as the page loads
  • Screen transitions shouldn’t feel slow due to blocking on the network

Sites aren’t reactive�

Sites aren’t predictable

The user is not in control�

Confidential + Proprietary

16 of 88

App Shell Model

Keep your app reactive, predictable and in control

Benefits

  • Reliable performance that is consistently fast. Static assets and the UI are cached on the first visit so that they load instantly on repeat visits.
  • Native-like interactions. Native-application-like navigation and interactions, complete with offline support.
  • Economical use of data. Design for minimal data usage and be judicious

Confidential + Proprietary

17 of 88

App Shell Model

Keep your app reactive, predictable and in control

App shell model

Content placeholders

No shifting content

Confidential + Proprietary

18 of 88

App Shell Model

Keep your app reactive, predictable and in control

Download only what’s needed with PWAs

Android App

30 MB+

0.6 MB

iOS App

PWA

100 MB+

Confidential + Proprietary

19 of 88

Fast

Engaging

Reliable

Integrated

20 of 88

Fast

21 of 88

Cache, fall back to network

4:00pm

Web server

Service worker

Local Cache

Client Device

1

Confidential + Proprietary

22 of 88

Cache, fall back to network

4:00pm

Web server

Service worker

Local Cache

Client Device

1

2

Confidential + Proprietary

23 of 88

Cache, fall back to network

4:00pm

Web server

Service worker

Local Cache

Client Device

1

2

3

Confidential + Proprietary

24 of 88

Cache, fall back to network

4:00pm

Web server

Service worker

Local Cache

Client Device

1

2

Confidential + Proprietary

25 of 88

Cache, fall back to network

4:00pm

Web server

Service worker

Local Cache

Client Device

1

2

3

Confidential + Proprietary

26 of 88

Cache, fall back to network

4:00pm

Web server

Service worker

Local Cache

Client Device

1

2

3

4

Confidential + Proprietary

27 of 88

Cache, then network

4:00pm

Web server

Service worker

Client Device

Leaderboard

Local Cache

Last update: 3:00pm

Confidential + Proprietary

28 of 88

Cache, then network

4:00pm

Web server

Service worker

Client Device

1

1

Leaderboard

Local Cache

Last update: 3:00pm

Confidential + Proprietary

29 of 88

Cache, then network

4:00pm

Web server

Service worker

Local Cache

Client Device

1

1

2

2

Local Cache

Last update: 3:00pm

Leaderboard

Last updated: 3:00 pm

Joe 2000

Sally 3000

Mark 4000

Confidential + Proprietary

30 of 88

Cache, then network

4:00pm

Web server

Service worker

Local Cache

Last update: 4:00pm

Client Device

1

1

2

2

3

3

Leaderboard

Last updated: 4:00 pm

Sally 5000

Joe 2000

Mark 4000

Confidential + Proprietary

31 of 88

Integrated

32 of 88

Add to�Home Screen

33 of 88

80%

Source: comScore Custom Survey, U.S., Age 18+, 2017 Wave

intentionally moved apps to their home screen

34 of 88

Broken Experience

Required user interaction�Buried deep in menus

Where would it start?�Dependent on bookmark

Would it work offline?�Users didn't expect offline

35 of 88

Web App Manifest

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

36 of 88

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

{

"name": "CNET Tech Today",

"short_name": "Tech Today",

"icons": [{

"src": "icon-512x512.png",

"sizes": "512x512",

"type": "image/png"

}, {...}],

"start_url": "/index.html",

"display": "standalone",

"orientation": "portrait",

"background_color": "#FF0000",

"theme_color": "#FF0000"

}

37 of 88

Home Screen Name & Icons

{

"name": "CNET Tech Today",

"short_name": "Tech Today",

"icons": [{

"src": "icon-512x512.png",

"sizes": "512x512",

"type": "image/png"

}, {...}],

"start_url": "/index.html",

"display": "standalone",

"orientation": "portrait",

"background_color": "#FF0000",

"theme_color": "#FF0000"

}

38 of 88

Splash Screen

{

"name": "CNET Tech Today",

"short_name": "Tech Today",

"icons": [{

"src": "icon-512x512.png",

"sizes": "512x512",

"type": "image/png"

}, {...}],

"start_url": "/index.html",

"display": "standalone",

"orientation": "portrait",

"background_color": "#FF0000",

"theme_color": "#FF0000"

}

39 of 88

Start Up Options

{

"name": "CNET Tech Today",

"short_name": "Tech Today",

"icons": [{

"src": "icon-512x512.png",

"sizes": "512x512",

"type": "image/png"

}, {...}],

"start_url": "/index.html",

"display": "standalone",

"orientation": "portrait",

"background_color": "#FF0000",

"theme_color": "#FF0000"

}

40 of 88

Task Switcher Colors

{

"name": "CNET Tech Today",

"short_name": "Tech Today",

"icons": [{

"src": "icon-512x512.png",

"sizes": "512x512",

"type": "image/png"

}, {...}],

"start_url": "/index.html",

"display": "standalone",

"orientation": "portrait",

"background_color": "#FF0000",

"theme_color": "#FF0000"

}

41 of 88

Good, but what about

Prompt to Install?

42 of 88

Prompt to Install

43 of 88

Prompt to Install

  • Web App Manifest

44 of 88

Prompt to Install

  • Web App Manifest
  • Offline support�(with service worker)

45 of 88

Prompt to Install

  • Web App Manifest
  • Offline support�(with service worker)
  • Engaged User

46 of 88

Baseline PWA

  • Site is served over HTTPS
  • Pages are responsive on tablets & mobile devices
  • The start URL (at least) loads while offline
  • Metadata provided for Add to Home screen
  • First load fast even on 3G
  • Site works cross-browser
  • Page transitions don't feel like they block on the network
  • Each page has a URL

47 of 88

Exemplary PWA

Indexability & Social

  • Site's content is indexed by Google
  • Schema.org metadata is provided where appropriate
  • Social metadata is provided where appropriate
  • Canonical URLs are provided when necessary
  • Pages use the History API

User Experience

  • Content doesn't jump as the page loads
  • Pressing back from a detail page retains scroll position on the previous list page
  • When tapped, inputs aren't obscured by the on screen keyboard
  • Content is easily sharable from standalone or full screen mode
  • Site is responsive across phone, tablet and desktop screen sizes
  • Any app install prompts are not used excessively
  • The Add to Home Screen prompt is intercepted

Performance

  • First load very fast even on 3G

Caching

  • Site uses cache-first networking
  • Site appropriately informs the user when they're offline

Push Notifications

  • Provide context to the user about how notifications will be used
  • UI encouraging users to turn on Push Notifications must not be overly aggressive.
  • Site dims the screen when permission request is showing
  • Push notifications must be timely, precise and relevant
  • Provides controls to enable and disable notifications

Additional Features

  • User is logged in across devices via Credential Management API
  • User can pay easily via native UI from Payment Request API.

48 of 88

Why are the requirements

so strict?

49 of 88

A Promise to the User

Fast

Engaging

Reliable

Integrated

50 of 88

51 of 88

Service Workers are a client-side proxy

Service worker

(written in JavaScript)

Cache

Web server

52 of 88

Service Worker Lifecycle

Activated

Error

Idle

Active

Terminated

Install

Register

  • Adds app-like lifecycle to a page
  • Wakes up only when the OS says
  • Only responds to system events

53 of 88

Service Worker Lifecycle

Activated

Error

Idle

Active

Terminated

Install

Register

  • Adds app-like lifecycle to a page
  • Wakes up only when the OS says
  • Only responds to system events

54 of 88

Service Worker Lifecycle

Activated

Error

Idle

Active

Terminated

Install

Register

  • Adds app-like lifecycle to a page
  • Wakes up only when the OS says
  • Only responds to system events

55 of 88

Service Worker

is for the second load.

56 of 88

Lifecycle of a Service Worker

Installing the Service Worker

1st Page Load

install

idle

57 of 88

Lifecycle of a Service Worker

Installing the Service Worker

2nd Page Load

activated

check for update

58 of 88

Lifecycle of a Service Worker

Installing the Service Worker

Close Page

idle

terminated

activated

59 of 88

Lifecycle of a Service Worker

Updating a Service Worker

Load Page with Service Worker

activated

check for update

60 of 88

Lifecycle of a Service Worker

Updating a Service Worker

Load Page with Service Worker

activated

check for update

install

idle

61 of 88

Lifecycle of a Service Worker

Updating a Service Worker

Load Page with Service Worker

activated

check for update

install

idle

62 of 88

Implementing a Simple Service Worker

63 of 88

Register the Service Worker

Activated

Error

Idle

Active

Terminated

Install

Register

64 of 88

Register the Service Worker

if ('serviceWorker' in navigator) {

navigator.serviceWorker.register('/service-worker.js')

.then(function(reg) {

console.log('Service Worker Registered', reg);

})

.catch(function(err) {

console.log('Error registering Service Worker', err);

});

}

65 of 88

Register the Service Worker

if ('serviceWorker' in navigator) {

navigator.serviceWorker.register('/service-worker.js')

.then(function(reg) {

console.log('Service Worker Registered', reg);

})

.catch(function(err) {

console.log('Error registering Service Worker', err);

});

}

66 of 88

Add an install Event Handler

Activated

Error

Idle

Active

Terminated

Install

Register

67 of 88

Add an install Event Handler

self.addEventListener('install', function(event) {

return self.skipWaiting();

});

68 of 88

Pre-fetch the App Resources

Activated

Error

Idle

Active

Terminated

Install

Register

69 of 88

Pre-fetch the App Resources

var cacheName = 'app-shell-cache-v1';

var filesToCache = ['/', '/index.html', ...];

self.addEventListener('install', function(event) {

event.waitUntil(

caches.open(cacheName).then(function(cache) {

return cache.addAll(filesToCache);

}).then(function() {

return self.skipWaiting();

})

);

});

70 of 88

Pre-fetch the App Resources

var cacheName = 'app-shell-cache-v1';

var filesToCache = ['/', '/index.html', ...];

self.addEventListener('install', function(event) {

event.waitUntil(

caches.open(cacheName).then(function(cache) {

return cache.addAll(filesToCache);

}).then(function() {

return self.skipWaiting();

})

);

});

71 of 88

Pre-fetch the App Resources

var cacheName = 'app-shell-cache-v1';

var filesToCache = ['/', '/index.html', ...];

self.addEventListener('install', function(event) {

event.waitUntil(

caches.open(cacheName).then(function(cache) {

return cache.addAll(filesToCache);

}).then(function() {

return self.skipWaiting();

})

);

});

72 of 88

Pre-fetch the App Resources

var cacheName = 'app-shell-cache-v1';

var filesToCache = ['/', '/index.html', ...];

self.addEventListener('install', function(event) {

event.waitUntil(

caches.open(cacheName).then(function(cache) {

return cache.addAll(filesToCache);

}).then(function() {

return self.skipWaiting();

})

);

});

73 of 88

Add an activate Event Handler

Activated

Error

Idle

Active

Terminated

Register

Install

74 of 88

Add an activate Event Handler

self.addEventListener('activate', function(e) {

e.waitUntil(

caches.keys().then(function(keyList) {

return Promise.all(keyList.map(function(key) {

if (key !== cacheName) {

return caches.delete(key);

}

}));

})

);

return self.clients.claim();

});

75 of 88

Add an activate Event Handler

self.addEventListener('activate', function(e) {

e.waitUntil(

caches.keys().then(function(keyList) {

return Promise.all(keyList.map(function(key) {

if (key !== cacheName) {

return caches.delete(key);

}

}));

})

);

return self.clients.claim();

});

76 of 88

Not Done Yet…

Activated

Error

Idle

Active

Terminated

Install

Register

77 of 88

78 of 88

Add a fetch Event Handler

self.addEventListener('fetch', function(e) {

e.respondWith(

caches.match(e.request).then(function(response) {

return response || fetch(e.request);

})

);

});

79 of 88

Add a fetch Event Handler

self.addEventListener('fetch', function(e) {

e.respondWith(

caches.match(e.request).then(function(response) {

return response || fetch(e.request);

})

);

});

80 of 88

Add a fetch Event Handler

self.addEventListener('fetch', function(e) {

e.respondWith(

caches.match(e.request).then(function(response) {

return response || fetch(e.request);

})

);

});

81 of 88

Add a fetch Event Handler

self.addEventListener('fetch', function(e) {

e.respondWith(

caches.match(e.request).then(function(response) {

return response || fetch(e.request);

})

);

});

82 of 88

Ready to Go!

Activated

Error

Idle

Active

Terminated

Install

Register

83 of 88

Scope

if ('serviceWorker' in navigator) {

navigator.serviceWorker.register('/service-worker.js')

.then(function(reg) {

console.log('Service Worker Registered', reg);

})

.catch(function(err) {

console.log('Error registering Service Worker', err);

});

}

84 of 88

Scope

if ('serviceWorker' in navigator) {

navigator.serviceWorker.register('/service-worker.js')

.then(function(reg) {

console.log('Service Worker Registered', reg);

})

.catch(function(err) {

console.log('Error registering Service Worker', err);

});

}

85 of 88

Precaching static resources

Precaching (SW)

Precaching (Workbox)

86 of 88

Runtime caching (Workbox)

Runtime caching (SW)

87 of 88

tomayac.github.io/wikibattle/

Basic offline functionality.

Implements different cache strategies for different kinds of assets.

Need to deal with navigation and non-navigation fetch events.

88 of 88

Q & A

Thomas Steiner

Developer Advocate

Google Hamburg

@tomayac