5.1 Web Manifests and PWA Installs
# What is a PWA
Here is the full playlist about Progressive Web Apps (opens new window)
Video from Google I/O conference introducing PWAs and a Intro by Steve about PWAs.
Google maintains a web.dev
website with a huge amount of developer resources. Here is their section about PWAs (opens new window).
# Manifest Files
A web manifest file is a JSON file that is used with Progressive Web Apps by the browser to understand settings like the name and version of a web app, the icon to use if it gets "installed", fullscreen mode, orientation, and theme values. It can have a .manifest
or a .json
file extension.
The file needs to include a name for the app and a launcher icon set. It needs to have instructions on how to display (or not display) the browser chrome. It needs to know what url is the start page for the app when launched from the home screen.
Here is a sample manifest file. While a few of these properties are optional, why skip things that improve the app experience?
{
"name": "Exercise Three",
"short_name": "EX3",
"description": "This app suggests you know what you are doing with PWA",
"icons": [
{
"src": "/img/favicon-16x16.png",
"sizes": "16x16",
"type": "image/png"
},
{
"src": "/img/favicon-32x32.png",
"sizes": "32x32",
"type": "image/png"
},
{
"src": "/img/apple-touch-icon.png",
"sizes": "128x128",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/img/mstile-150x150.png",
"sizes": "150x150",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/img/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/img/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"orientation": "portrait-primary",
"theme_color": "#bada55",
"background_color": "#eeeeee",
"display": "standalone",
"start_url": "/index.html"
}
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
The orientation
property gives us a starting orientation for the app when it opens. For the full list look at the list on MDN (opens new window).
The display
property can be standalone
, fullscreen
, browser
or minimal-ui
. browser
makes it look like a normal browser... so why do that? standalone
makes it look like a native app by removing all the menus. fullscreen
removes some of the elements at the top of the screen. minimal-ui
is like standalone
but it is allowed to remove some navigation controls... so again, why?
We need to link all our HTML files, in our app, to the manifest file. We do this by adding a <link>
tag inside the <head>
.
<link rel="stylesheet" href="/mainfest.json" />
It should be found at the root of the domain. The same place as your service worker.
For more properties in your manifest file see the full reference on MDN (opens new window)
# Head Meta and Link Elements
The web manifest file does provide all the requirements outlined by the standard. Unfortunately, iOS Safari has lagged behind in support for PWAs and there are a number of additions that we need to add to our web pages inside the <head>
to keep the experience decent for iOS.
Older Android can also use a couple of these.
<!-- pwa manifest -->
<link rel="manifest" href="manifest.json" />
<!-- older android support (manifest uses theme_color)-->
<meta name="theme-color" content="#bada55" />
<!-- ios support (manifest theme_color and icons)-->
<link rel="apple-touch-icon" href="/img/apple-touch-icon.png" />
<meta name="apple-mobile-web-app-status-bar" content="#bada55" />
2
3
4
5
6
7
# Launcher Icons
One of the most tedious parts of building your web manifest can be the creation of all those icons.
Here is a site that you can use for building them.
Real Favicon Generator (opens new window)
# Making a PWA Installable
To be installable a Progressive Web App needs to meet some basic requirements.
- It must have a web manifest JSON file, which includes some minimal properties: a name or short_name to provide the name of the app; an icons array for launcher icons; a start_url for the home page location; and a display property that controls the appearance of the browser's chrome when installed.
- A user engagement heuristic, which means that the user has spent some time on the website and interacted with it.
- It has a registered Service Worker.
- It is not already installed.
For Chrome, the icons must include a 192px and a 512px icon.
# New Requirement Coming
Starting with Chrome 89, in March 2021, the fetch event listener needs to return a valid response if the app is offline. If it doesn't then the LightHouse test will fail. With Chrome 93, in August 2021, this will be an absolute requirement for installation.
# Handling Install Events in Chrome
If your user is visiting your website on any non-iOS mobile device using Chrome, Edge, or Opera then they will have access to an event that is the trigger for installing your PWA.
We can use this event to interrupt the process and create our own install experience.
On iOS we can create a similar install experience but we will have less control.
The event that we get on the Chrome/Chromium/Blink-based browsers is called beforeinstallprompt
. When this event happens we know that the browser thinks that all requirements, including the heuristic engagement, have been met. This will be a trigger for us to save the event to be used later, in our own enhanced install experience.
//listen for Chrome install prompt
window.addEventListener('beforeinstallprompt', (ev) => {
// Prevent the mini-infobar from appearing on mobile
ev.preventDefault();
// Save the event in a global property
// so that it can be triggered later.
APP.deferredPrompt = ev;
console.log('deferredPrompt saved');
// Build your own enhanced install experience
// use the APP.deferredPrompt saved event
});
2
3
4
5
6
7
8
9
10
11
Later on, when we DO want to install our app at a more appropriate time, we can get that saved APP.deferredPrompt
event and trigger displaying the install mini-infobar.
//check if we have the event saved
if (APP.deferredPrompt) {
//trigger the display of the install prompt
APP.deferredPrompt.prompt();
//wait for the resulting Promise from the install prompt
APP.deferredPrompt.userChoice.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
//user says yes
console.log('User accepted the install prompt');
APP.deferredPrompt = null; //we will not need it again.
} else {
//user says not now
console.log('User dismissed the install prompt');
}
});
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
In the Chrome/Chromium/Blink-based browsers we can also listen for an event that tells us that the App was installed, in case you want to trigger some action, like saving more files to the cache.
//listen for sign that app was installed
window.addEventListener('appinstalled', (evt) => {
console.log('app was installed');
//tell the service worker that the app has been installed
//this can be the trigger to fetch and cache more assets
let msg = {
appInstalled: true,
};
navigator.serviceWorker.controller.postMessage(msg);
});
2
3
4
5
6
7
8
9
10
# Resources
Here is a great series by @netNinjaUK about creating a PWA. He walks through most things that we need for our PWA project, However he uses Firebase for his data storage instead of indexed DB and has no API fetch calls.
Net Ninja YouTube Series on PWAs (opens new window)
Steve Griffith Progressive Web Apps playlist (opens new window)
# What to do this week
TODO
Things to do before next week
- Read and Watch all the course content for
5.1
,5.2
, and6.1
- Start working on Hybrid Three