PWAs
The Store-free, Installable Native App
(Progressive Web Apps)
slides.com/brianrichins/pwa/live
Photos courtesy of Unsplash
Very stock, much exciting
Sorry, dinosaurs
*not under 'dinosaurs' on Unsplash
-
Benefits and Features
-
Browser / OS Support
-
Notable Adopters
-
Technical Requirements
-
Coding Demo
Outline
Benefits and Features
Quick History
-
Term Coined in 2015
-
HTML, CSS, JS
-
A combination of existing and emerging tools and standards
-
PWAs are websites - with "app-like characteristics"
"App-like" Characteristics
-
Progressive Enhancement
-
Responsive Layout
-
Offline functionality
-
Full / Splash Screen
-
App-style UI
-
Fresh content
-
Secure
-
Discoverable
-
Engagable (Push)
-
Installable
-
Linkable
Benefits
-
Improved Performance
-
Faster Loading
-
Less Data Use
-
SEO
-
Reliability
-
Cross Platform
-
User Retention
-
Cheaper to Build
Browser Support
Users Are Everywhere
PWAs install natively on:
Mobile (50%)
-
iOS
-
Android
-
Samsung
Internet -
UC Browser
Desktop (36%)
-
Windows
-
Mac
-
Linux
Smartphones: | 700 Million |
---|---|
Total Web: | 3,700 Million |
Top 10 Screen Sizes - July 2019
1 | 640x360 | 16.18% |
2 | 1920x1080 | 11.24% |
3 | 1366x768 | 9.68% |
4 | 1024x768 | 6.46% |
5 | 667x375 | 5.69% |
6 | 800x600 | 3.36% |
7 | 720x360 | 3.27% |
8 | 760x360 | 3.10% |
9 | 1440x900 | 2.71% |
10 | 736x414 | 2.30% |
Native Experience - iOS
-
Add to Homescreen
-
Removes Browser UI
-
No sharing tabs
-
No 'install' events
-
No derived browsers
Native Experience - Android
-
Install prompt
-
Removes Browser UI
-
No sharing tabs
-
'Install' events
-
beforeinstallprompt
-
prompt
-
appinstalled
-
-
OS / Store presence
- WebAPK
Native Experience - Windows
(And Linux)
Native Experience - Windows
Native Experience - Windows
Native Experience - MS Office
Native Experience - macOS
Normalized Experience
-
Several libraries exist for normalizing
and showing prompts (e.g. iOS) -
Custom prompt / button text
-
Align to browser control
love2dev.com/pwa/
add-to-homescreen-library/
Notable Adopters
-
75% increase in Tweets sent
-
20% decrease in bounce rate
-
65% increase in pages per session
Uber
-
Installable on 2G devices
-
3 second load time on 2G
- Core app of only 50kB
Starbucks
-
99.84% smaller than iOS app
-
2x number of web users who place orders each day
-
Desktop users now ordering at about the same rate as mobile users
AliExpress (China)
-
104% increase in conversions for new users
-
2x more pages visited per session
-
74% increase in time spent per session
Flipkart (India)
-
70% increase in conversions
-
40% higher
re-engagement -
3x more time spent on site
-
3x lower data usage
OLX (Global Classifieds)
-
23% decrease in time taken for a page to be interactive
-
250% increase in re-engagement
-
80% decrease in bounce rates
-
146% increase in CTR on Ads
-
40% increase in time spent on mobile web
-
60% increase in engagement
-
50% increase in ad click-throughs
-
44% increase in user-generated ad revenue
Spotify
-
54% increase in one-day plays
-
14% increase in daily active users
-
30% of logins came from churned users
More Examples
Example Deep Dive
-
2018 Chrome Dev Summit
https://youtu.be/Xryhxi45Q5M
PWAs vs
Native / Hybrid / Web
App Stores aren't Bad™
-
User Discovery
-
Ratings
-
Advertising platform
-
User trust and security
-
Resource hosting
-
Payment automation
PWA vs App Stores
-
No annual license or 15-30% purchase fees
-
External purchase restrictions
-
No 3rd-party approval process
-
Users don't install apps anyways
-
Average of 0 per month for last 5 years
-
60% of apps in the Play Store have never been downloaded
-
3/4 of users who start the install process don't finish it
-
PWA vs App Stores
-
PWAs now included in all major App Stores
-
Google Play
-
auto-creation of a WebAPK
-
-
Microsoft
-
auto-creation and Windows Store install / uninstall
-
-
Apple
-
...no store support! Waa waa waaah :(
-
Safari does allow PWA installs, but won't promote
-
-
PWA vs Native/Hybrid Apps
-
No platform-specific languages
-
(Swift, Objective-C, Java)
-
100% code reuse on all platforms
100% cross-platform install
-
Total control over updates / deprecation
-
Less ceremony than Hybrid frameworks
(PhoneGap, Ionic, Xamarin, Electron)
PWAs vs Native/Hybrid Apps
-
Nearly identical look and performance
-
Less install friction - 20% loss at each step
PWAs vs Native/Hybrid Apps
-
Less storage space and bandwidth
-
Less reason to uninstall
PWA vs 'Standard Web'
-
Standard web sites are becoming web apps
-
You're building a website anyways, app or not - why not start with best practices?
-
The web can (most likely) do everything you need anyways
PWA Interest
What can PWAs do?
What can PWAs do?
What can PWAs do?
-
W3C specifications & https://caniuse.com
-
Convenient feature grouping
-
Descriptions and <code snippets>
-
In-browser support checks
Courtesy of https://WhatWebCanDo.today
Accept All Input
Audio/Video Recording
Some advanced Output
Respond to Movement
Talk to other devices
Check device status
Access OS Resources
Send user updates
Provide seamless experiences
So what makes a PWA different from a Web App?
Technical Requirements
PWAs
must:
-
Originate from a secure origin
-
Have a png icon
(144 x 144 minimum)
-
Web manifest
-
Load while offline
-
HTTPS (TLS / SSL)
-
<app_logo>.png
-
manifest.json
-
Service Worker
Which they
do via:
The web manifest
Technical Requirements
manifest.json - Defines:
-
Name of the web app
-
Links to the icon(s) and other images
-
Preferred URL to launch on startup
-
Configuration data
-
Default orientation (portrait vs landscape)
-
Display mode preference, e.g. full screen
-
Other settings / preferences
manifest.json - Referencing
<head>
<link rel="manifest" href="/manifest.json" />
</head>
Note that "/manifest.json" is requested without any credentials, even on the same domain. If you require credentials:
<head>
<link rel="manifest" href="/manifest.json"
cossorigin="use-credentials" />
</head>
manifest.json - Implementation
{
"name": "Google Maps",
"icons": [
{
"src": "/images/icons-192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "/images/icons-512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": "/maps/?source=pwa",
}
manifest.json - Options
-
name
-
short_name
-
start_url
-
background_color
-
display
-
scope
-
orientation
-
theme_color
-
service_worker
manifest.json - Store Options
-
description
-
screenshots
-
categories
-
iarc_rating_id
-
native_applications
-
prefer_related_applications
The service worker
Technical Requirements
Service vs Web Workers
Web Workers |
Service Workers | |
---|---|---|
Tab control | Many per tab | One for all tabs |
Lifespan | Same as tab | Independent |
Good for | Parallelism | Offline |
Service Workers
-
Run in the background on own thread
-
Proxy network requests and caching
Service Workers - Gotchas
-
Run in a separate (global) context and thread from the window and UI
-
Can run in iframes
-
Have no direct DOM access
-
Can import scripts from any origin
-
Require HTTPS
Service Workers - Gotchas
-
Have a life cycle separate from the page - persistent background process
-
Are closed when not in use and all tasks have completed
-
Can take control of existing pages in same domain
-
There is no limit on how many SWs an origin can spawn
Service Workers - Registering
//check for functionality
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js')
.then(function(reg) {
// Registration was successful
console.log('Registered with: ', reg.scope);
}, function(err) {
// registration failed :(
console.error('Registration failed: ', err);
});
});
}
Service Workers - Lifecycle
-
Registration scope is set by location-based pattern matching
-
/sw.js has root scope
-
/subarea/sw.js matches:
-
/subarea/page1
-
/subarea/foo/bar
-
/other
-
SW Lifecycle
Service Workers - Using
-
Event driven / asynchronous
-
Make heavy use of Promises
-
Pass messages via:
-
push
-
fetch
-
Service Workers - APIs
Network Requests
-
XMLHttpRequest -
Fetch API
-
Server Sent Events (SSE / EventSource)
-
Web Sockets
Storage
-
Local Storage -
Session Storage Web SQL-
Cache
-
IndexedDB
Service Workers - Other Uses
-
Background sync with server
-
Handle / cache cross-origin requests
-
Centralized storage and sharing of 'expensive' operations
-
Client-side compilation of development assets
-
Hooks for background services
-
Pre-fetching assets (eager loading)
Coding Demo
Generator Examples
What is ?
-
"A set of libraries and Node modules that make it easy to cache assets and take full advantage of features used to build Progressive Web Apps."
- Precaching - Runtime caching - Pre-built Strategies - Request routing - Offline Analytics |
- Background sync - Helpful debugging - Successor to sw-precache and sw-toolbox |
---|
Workbox Caching Strategies
-
Cache Only
-
Cache first, falling back to network
-
Cache with network update
-
Network Only
-
Network first, falling back to cache
Chrome Devs' Offline Cookbook:
https://developers.google.com/web/fundamentals/
instant-and-offline/offline-cookbook/
Front-End Support
Live Coding Workshop
-
developers.google.com/web/
fundamentals/codelabs/
your-first-pwapp -
Glitch.com
-
DarkSky API - darksky.net/dev
(Not covered - push notifications)
Workshop Highlights
-
Create & register the manifest
<link rel="manifest" href="/manifest.json">
-
Inspect the contents in the Dev Tools Application Tab
-
Alternate iOS meta tags
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Weather PWA">
<link rel="apple-touch-icon" href="/images/icons/icon-152x152.png">
Workshop Highlights
-
Register the manifest within index.html
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js')
.then((reg) => {
console.log('Service worker registered.', reg);
});
});
}
Workshop Highlights
-
Configure caching in sw.js > install event
const FILES_TO_CACHE = [
'/offline.html',
];
evt.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
console.log('[ServiceWorker] Pre-caching offline page');
return cache.addAll(FILES_TO_CACHE);
})
);
Cache the 'offline' page
-
Maintain cache in sw.js > activate event
const CACHE_NAME = 'mycache.v2'
evt.waitUntil(
caches.keys().then((keyList) => {
return Promise.all(keyList.map((key) => {
if (key !== CACHE_NAME) {
console.log('[ServiceWorker] Removing old cache', key);
return caches.delete(key);
}
}));
})
);
Display the 'offline' page
-
Handle network failures in sw.js
if (evt.request.mode !== 'navigate') { //bail
return;
}
evt.respondWith(
fetch(evt.request)
.catch(() => {
return caches.open(CACHE_NAME)
.then((cache) => {
return cache.match('offline.html');
});
})
);
Tandem Cache/Network in app.js
// function getForecastFromCache()
const url = `${window.location.origin}/forecast/${coords}`;
return caches.match(url)
.then((response) => {
if (response) return response.json();
else return null;
}).catch((err) => {
console.error('Error getting data from cache', err);
return null;
});
// within updateData:
getForecastFromCache(location.geo)
.then((forecast) => {
renderForecast(card, forecast);
});
getForecastFromNetwork(location.geo)
.then((forecast) => {
renderForecast(card, forecast);
});
Precaching in sw.js
const CACHE_NAME = 'static-cache-v2'; //note updated name
const DATA_CACHE_NAME = 'data-cache-v1'; //new dynamic cache
const FILES_TO_CACHE = [
// root files and core scripts
'/',
'/index.html',
'/scripts/app.js',
'/scripts/install.js',
'/scripts/luxon-1.11.4.js',
'/styles/inline.css',
// common images
'/images/add.svg',
'/images/clear-day.svg',
'/images/clear-night.svg',
...
];
Precaching in sw.js > fetch
if (evt.request.url.includes('/forecast/')) { //data API calls
console.log('[Service Worker] Fetch (data)', evt.request.url);
evt.respondWith(
caches.open(DATA_CACHE_NAME).then((cache) => {
return fetch(evt.request)
.then((response) => {
// Clone & cache good results
if (response.status === 200) {
cache.put(evt.request.url, response.clone());
}
return response;
}).catch((err) => {
// Network request failed, try the cache.
return cache.match(evt.request);
});
}));
return;
} //else static asset; return cached or request if null
Adding Install
-
Use HTTPS
-
web.manifest has required elements
-
Register a SW that handles fetch events
Handle prompt event
-
Register install.js in index.html and sw.js
-
Intercept install prompt event
-
Attach event to a UI element & display it
<script src="/scripts/install.js"></script>
window.addEventListener('beforeinstallprompt', saveInstallPrompt);
// function installButton.onClick()
deferredInstallPrompt.prompt();
evt.srcElement.setAttribute('hidden', true); //hide after install
// function saveInstallPrompt()
deferredInstallPrompt = evt;
installButton.removeAttribute('hidden');
Handle install response
-
Respond to the user's choice in install.js
deferredInstallPrompt.userChoice
.then((choice) => {
if (choice.outcome === 'accepted') { // installed
console.log('Unicorns and kittens for all!', choice);
} else { // not installed
console.log('Release the hounds!', choice);
}
deferredInstallPrompt = null;
});
-
Users can also install from menu
window.addEventListener('appinstalled', (installEvt) => {
console.log("Magically delicious!", installEvt) });
Tweaks for installed apps
-
Adjusting for standalone mode
// CSS
@media all and (display-mode: standalone) {
body {
background-color: yellow;
}
}
// JS
if (window.matchMedia('(display-mode: standalone)').matches) {
console.log('display-mode is standalone');
}
// JS Safari
if (window.navigator.standalone === true) {
console.log('display-mode is standalone');
}
Push Notifications
// Ask the user for permissions
Notification.requestPermission((status) => {
console.log('Notification permission status:', status);
});
// Show a notification
const btn = document.getElementById('btn');
btn.addEventListener('click', () => {
if (Notification.permission == 'granted') {
navigator.serviceWorker.getRegistration()
.then(reg => reg.showNotification('Hello world!'))
}
})
Notifications Options
-
Images and animated icons
-
Custom Timestamps
-
Persistence Time
-
User Interaction events
-
Custom Device Vibration
References / Reading
Business Cases
Training and Examples
-
Chris Love
-
Google Developers
-
Mozilla Developer Network (MDN)
-
Microsoft
Tech Evaluation Resources
References
- infrequently.org/2016/09/what-exactly-makes-something-a-progressive-web-app/
- developers.google.com/web/fundamentals/
web-app-manifest/ - www.coredna.com/blogs/progressive-web-app
- chromium.googlesource.com/chromium/src/+/master/docs/security/service-worker-security-faq.md
- nolanlawson.github.io/cascadia-2016/
References
Questions?
PWAs - the Store-free Installable Native Web App
By Brian Richins
PWAs - the Store-free Installable Native Web App
The browser is becoming a powerful platform for delivery, and websites are now spilling directly into the OS. PWAs can be installed on mobile devices and the desktop alongside native apps, and can access hardware, send notifications, and make all your dreams come true.* * Wish fulfillment not guaranteed, your results may vary.
- 1,825