# 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.

  1. Fast and Reliable
  2. Installable
  3. 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.

  1. 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.
  2. A user engagement heuristic, which means that the user has spent some time on the website and interacted with it.
  3. It has a registered Service Worker.
  4. 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"
}
1
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" />
1

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)

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" />
1
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
});
1
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');
    }
  });
}
1
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);
});
1
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

Last Updated: 3/30/2021, 11:34:21 AM