Go Make Things: Service Workers

Chris Ferdinandi has been writing an excellent post series on Service Workers. After first explaining what Service Workers are and how to create one, he — as per usual on Go Make Things — also provides us with practical scenarios such as making pages available offline, caching web fonts to improve rendering, etc.

Go Make Things: Service Workers →

☝️ You should really follow Chris’s blog as his Daily Developer Tips — in which this Service Workers series appeared — are always very good.

Smaller HTML Payloads with Service Workers

Philip Walton on how to progressively enhance your site by leveraging Service Workers to fetch partial HTML content and replace it in the DOM:

On this site, after a user visits once and the service worker is installed, that user will never request a full HTML page again. Instead the service worker will intercept requests for pages and just request the contents of those pages—everything inside the <main> element—and then the service worker will combine that content with the rest of the HTML, which is already in the cache.

By only requesting the contents of a page, the networks payloads become substantially smaller, and the pages can load quite a bit faster. For example, on this site over the past 30 days, page loads from a service worker had a 47.6% smaller network payloads, and a median First Contentful Paint (FCP) that was 52.3% faster than page loads without a service worker (416ms vs. 851ms).

The code is implemented using Workbox:

import {cacheNames} from 'workbox-core';
import {getCacheKeyForURL} from 'workbox-precaching';
import {registerRoute} from 'workbox-routing';
import {CacheFirst, StaleWhileRevalidate} from 'workbox-strategies';
import {strategy as composeStrategies} from 'workbox-streams';

const shellStrategy = new CacheFirst({cacheName: cacheNames.precache});
const contentStrategy = new StaleWhileRevalidate({cacheName: 'content'});

const navigationHandler = composeStrategies([
  () => shellStrategy.handle({
    request: new Request(getCacheKeyForURL('/shell-start.html')),
  }),
  ({url}) => contentStrategy.handle({
    request: new Request(url.pathname + 'index.content.html'),
  }),
  () => shellStrategy.handle({
    request: new Request(getCacheKeyForURL('/shell-end.html')),
  }),
]);

registerRoute(({request}) => request.mode === 'navigate', navigationHandler);

Smaller HTML Payloads with Service Workers →

🔗 Related: Unpoly, which uses HTML attributes to let you control partial rerenders using XHR fetches.

How to display a “new version available” for a Progressive Web App

Dean Hume:

I’ve built a number of Progressive Web Apps that simply update the service worker silently for the user in the background, but I really like the approach where you get a popup notification that suggests that there is a new version of the site available like Google’s Inbox provides – especially for an offline first web app. […] It got me wondering how I could build something a little similar and it turns out that it is a little tricker than it seems – but it is not impossible!

How to display a “new version available” for a Progressive Web App →

Implementing Web Push Notifications

Alexander Zlatkov from SessionStack on how to implement Web Push Notifications:

There are three general steps to implementing a push:

  1. The UI — adding the necessary client-side logic to subscribe a user to a push. This is the JavaScript logic that your web app UI needs in order to enable the user to register himself to push messages.
  2. Sending the push message — implementing the API call on your server that triggers a push message to the user’s device.
  3. Receiving the push message — handling the push message once it arrives in the browser.

How JavaScript works: the mechanics of Web Push Notifications →

PWAs are coming to iOS 11.3: Cupertino, we have a problem

Recently it was announced that iOS 11.3 and macOS 10.13.4 will include Service Workers, opening up the way for PWAs on iOS (and potentially macOS too, following lead of Microsoft here).

☝️ If you don’t want to download the macOS 10.13.4 beta you can alternatively download Safari Technology Preview (> 48) for use with your current macOS install.

Great news indeed, yet as Maximiliano Firtman documented there are quite a few caveats right now. Some might get fixed, yet some others won’t. One of the bigger issues I find worrying is the fact that you can end up with different versions of one and the same PWA, depending on the context you are running it in:

On Safari, on each app’s Web view, and home screen web apps, the engine doesn’t share Service Workers nor the Cache Storage, which means the user might end with several copies of all the files for the PWA on the same device.

PWAs are coming to iOS 11.3: Cupertino, we have a problem →

On a sidenote: Twitter recently announced they’ll no longer be shipping the Twitter app to macOS …

Network based image loading using the Network Information API in Service Worker

Clever use of a service worker in combination with navigator.connection.effetiveType by Michael Scharnagl. The SW watches all requests for image resources, and rewrites them to high resolution versions in case navigator.connection.effetiveType is fast enough.

Network based image loading using the Network Information API in Service Worker →

Tame your Service Worker before your Progressive Web App go into the wild

Slidedeck by Maxim Salnikov:

With great power comes great responsibility – trivially, but true: I’ll show the examples of how easy the “Progressive” part of the PWA term could become “Regressive”, how to fix this, and how to test our Service Worker before deploying your app. First, we’ll go through the well-known PWA functionality (App Shell, Offline Cache, Push) with focusing on the pitfalls we could easily get into, and how to avoid these. Next, I’ll expand your horizons with the new PWA features, like Foreign fetch, Periodic sync, Navigation Preloads. And again – “handle with care”. I’ll share the points we have to pay attention to, as well as best practices.

sw-toolbox – A Collection of Tools for Service Workers

Service Worker Toolbox provides some simple helpers for use in creating your own service workers. Specifically, it provides common caching strategies for dynamic content, such as API calls, third-party resources, and large or infrequently used local resources that you don’t want precached.

The code itself is very readable I must say:

importScripts('sw-toolbox.js');

toolbox.precache([
  '/css/app.css',
  '/img/logo.png'
]);

toolbox.router.get('/css*', toolbox.cacheFirst);
toolbox.router.get('/img*', toolbox.cacheFirst);
toolbox.router.get('/(.*)', toolbox.networkFirst, { networkTimeoutSeconds: 5});

A bit more advanced is to precache a “you’re offline” page first, and return that in case a user is offline (src):

importScripts('sw-toolbox.js');

toolbox.precache([
  '/offline',
  '/img/offline.png',
   // …
]);

// …

toolbox.router.get('/(.*)', function(request, values, options) {
  // networkFirst will attempt to return a response from the network,
  // then attempt to return a response from the cache.
  return toolbox.networkFirst(request, values, options).catch(function(error) {
    // If both the network and the cache fail, then `.catch()` will be triggered,
    // and we get a chance to respond with our cached fallback page.
    if (request.method === 'GET' && request.headers.get('accept').includes('text/html')) {
      return toolbox.cacheOnly(new Request('/offline'), values, options);
    }
    throw error;
  });
});

sw-toolbox
sw-toolbox (GitHub) →

A beginner’s guide to making Progressive Web Apps forms a nice introduction to implementing Service Workers using sw-toolbox.

If you’re looking for a non-library version I’d recommend starting with Implementing “Save For Offline” with Service Workers.

Service Worker Gotchas

Osvaldas Valutis from kollegorna.se has written an in-depth article on Service Workers

We’ve learned there quite a few gotchas to grasp in order to get Service Worker working correctly…

Comes with some solid advice and lots of code snippets.

One thing mentioned, that hadn’t crossed my mind before at all, is that it’s perfectly possible to “just” use ES2015 in your service worker:

It is completely safe to use the renewed JavaScript syntax in your serviceworker.js file, because browsers which support ES6 syntax also support Service Workers. However, I wouldn’t recommend using the new syntax for code that registers Service Worker, unless you use a transpiler.

Service Worker gotchas →

Related: Una Kravets’ writeup Implementing “Save For Offline” with Service Workers also forms a good start when it comes to Service Workers.

Implementing “Save For Offline” with Service Workers

Una Kravets implemented “Save For Offline” on her blog, using Service Workers. She described the reasoning, process, and code to implement.

With “Save For Offline”, if someone is reading your blog on the subway, and loses their Internet connection, they won’t lose their place. If they accidentally click and the browser refreshes, they won’t lose their place. If they want to save your article to read on an airplane or at at a café in a foreign country where they have no internet plan, they can do that. And if they just want to save battery and be on airplane mode, that’s cool too.

✨Magic.✨

Jeremy also did something similar, to make his Resilient Web Design work offline.

Implementing “Save For Offline” with Service Workers →
Making Resilient Web Design work offline →