# Week 10 - PWA Core
# Progressive Web Apps (PWA)
Progressive Web Apps offer a number of advantages over basic websites or even Single Page Applications. Most importantly are their improved performance and ability to function, at least partially, offline.
This video from the Google/IO conference from 2016 shows Jake Archibald demonstrating how PWAs work and explaining the concepts.
Web.dev resources for building PWA (opens new window)
# Lighthouse Audits for PWA
Lighthouse is an Audit tool built into the Chrome Web Dev tools which let's you know if your website meets all the requirements for being a Progressive Web App.
Open the Chrome Dev tools and look for the Lighthouse
tab (previously 'Audit'). It can run a number of tests. However, the category we want is the Progressive Web App
one. Only select that checkbox and then click the Generate Report button.
It will take a minute or two to run the audit.
The results are split into three groups.
- Fast and Reliable
- Installable
- PWA Optimized
We want green checkmarks || circles for everything!
Just like Service Workers, PWAs need https
to work but they are allowed to run on localhost
or 127.0.0.1
without it.
# 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
orshort_name
to provide the name of the app; anicons
array for launcher icons; astart_url
for the home page location; and adisplay
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.
# Web Manifest Files
The manifest file provides details about your web app so it can be installed. It is a JSON file that 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": "Suggest-A-Film",
"short_name": "Suggesta",
"description": "This app suggests movies using TMDB API",
"icons": [
{
"src": "/img/icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/img/icon-96x96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "/img/icon-128x128.png",
"sizes": "128x128",
"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
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 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)
# Adding a Service Worker
See last week's notes about Service Workers
# Adding IndexedDB
See last week's notes about Service Workers and Indexed DB
# 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)
TODO
- Read the content and watch the videos for Weeks 9 and 10
- Watch the video series on Service Workers (opens new window)
- Start working on the 3rd Project - PWA