Progressive Web Apps

6.1 Offline First and Strategies

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

# WaitUntil, SkipWaiting, Claim

The install, activate, fetch, and message events are all ExtendableEvents. That means we can use the ev.waitUntil() method to delay the completion of our event listener function until after a Promise has resolved.

ev.waitUntil(
  new Promise((resolve, reject) => {
    //function that runs and does something that could take some time
    //when we are done we call:
    let result = Math.random();
    resolve(result);
    //which will pass our result back to the waitUntil method
    // as a resolved Promise object.
  })
);
1
2
3
4
5
6
7
8
9
10

The self.skipWaiting() method tells the service worker that it does not need to wait to be activated. It should skip the waiting phase and immediately start activating. This will trigger the activate event listener function.

It is important to remember that the web page still will not be communicating with the new service worker. It still thinks that the old service worker is running.

This brings us to the clients.claim() method, which, if run inside the activate event listener function, will find all the windows and tabs that are currently running and tell them that the new service worker is activated and should be referenced now.

# Offline First

The window object has an online and offline event that we can listen for.

//listen for on and off line events
window.addEventListener('online', (ev) => {
  //tell the service worker about the change to online
});
window.addEventListener('offline', (ev) => {
  //tell the service worker about the change to offline
});
1
2
3
4
5
6
7

The online and offline event is fairly dependable for telling us that a connection to the wi-fi network has been established or lost. However, it does not tell us about failures for the wi-fi network to actually get online. It also does not tell us if our connection is really poor. A one-bar connection to a network is still considered "online".

The default value for this can be found in the navigator object.

let isOnline = 'onLine' in navigator && navigator.onLine;
1

The example above, checks for existence of the onLine property and then find it's Boolean value.

# More Accuracy Through Testing.

We can make a fetch call from either the webpage or our Service Worker that uses the HEAD method instead of GET or POST, just to see if we get a response.

Use an endpoint that is guaranteed to be available, or use a test of your own API. Have a health check route that returns minimal data or accepts the HEAD method. If you get a response from the server then that can be used as proof of being online.

async function isConnected() {
  let req = new Request('/api/alive', {
    method: 'HEAD',
  });
  return await fetch(req)
    .then((response) => {
      return true;
    })
    .catch((err) => {
      return false;
    });
}
1
2
3
4
5
6
7
8
9
10
11
12

You will have to edit the above code sample to use a proper URL.

# Launching Native App Alternative

If you have a true Native application and you want to have the user be prompted to install a platform specific native version as an alternative to your PWA then you can update the web manifest file to have the mobile device prompt the user with their choices.

We have to add the following two settings in the web manifest file.

{
  "prefer_related_applications": true,
  "related_applications": [
    {
      "platform": "play",
      "url": "https://play.google.com/store/apps/details?id=com.example.app1",
      "id": "com.example.app1"
    },
    {
      "platform": "itunes",
      "url": "https://itunes.apple.com/app/example-app1/id123456789"
    }
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

If there is no native alternative then omit the prefer_related_applications setting or have it set to false.

# What to do this week

TODO

Things to do before next week

Last Updated: 1/13/2022, 10:45:59 AM