I’ve been playing around in web development for years, in fact, the first web “application” I wrote was in 1994, the year the world wide web was available to the general public in New Zealand.
The last couple of years I’ve been focused on Progressive Web Apps (PWA’s), and this includes using service workers, which are notoriously bad at breaking the refresh button in browsers.
Maybe another title for this post might be: How to Fix the Refresh Button When Using a Service Worker 🔄
The Backstory
So, let’s start with: I use Google Workbox.
They even have a Github Repository.
Also, I am a New Zealander (a kiwi 🥝), and I host all my websites and apps in New Zealand. In fact, I run my own hosting and internet company called Net Enterprises Ltd.
Another (I think it might be little known) fact, is New Zealand isn’t connected very well with the rest of the world in terms of fibre connections. So, something in the UK that might be simple, like loading the workbox library from the CDN, turns out to be a slow experience here in New Zealand.
The punchline? I host a local copy of all the libraries I use: jQuery, Boostrap, Popper.js, Workbox, DataTables, etc …
I wanted an easy method to update my workbox library locally.
Fortunately, Google provide a “workbox-cli” to do exactly this.
You need Node installed, and npm-check-updates for my little script to work:
Running this script will create a local copy of the workbox library in a folder called “local_workbox”.
The Service Worker
So, a key concept for a service worker is that, to load a service worker you need to run javascript in your page, then you have separate javascript running as the service worker.
So, let’s get to it.
The Service Worker Lifecycle
Google produces some great documentation entitled The Service Workers Lifecycle, but if you’re starting out, this doc can be overwhelming.
So I’m going to try and break this down as simply as I can (this is how I understood service workers).
- On the first visit to a PWA a service worker is installed, and some cache maybe?
- The second visit to PWA the content is probably loaded from the cache.
- The PWA has some new content.
- On the third visit to the PWA (after the new content has been added), the content is loaded from the cache!!
- Wait?!?!? What?!?!? Where’s the new content?????
- During the third visit a new version of the service worker is installed and sits in a state called “waiting”.
- With some magically voodoo magic the new service worker is installed, replacing the first service worker.
- On the fourth visit to the PWA the new content appears.
So, there was definitely some weird thing I was missing about caching and loading new service workers.
This week I figured it out.
I need check to see if there is a service worker in “waiting” and prompt the user. (I actually saw this in real life on Tinder, last year sometime 😂).
So back to the code:
Code in your page
Let’s work through this script, top to bottom to see what it does.
First off, there’s a little function that creates a JS prompt, and on clicking “ok” will return onAccept.
Then we head to the meat of the script!
We import a couple of libraries from the workbox local copy: Workbox, and messageSW (Later we’re going to send a message to the service worker!).
Let’s first check if the browser supports service workers (if ‘serviceWorker’ in navigator).
The we’ll fire up a new Workbox object.
Here’s the tricky bit .. SkipWaiting …
This had me stumped for a while, mainly because I didn’t really understand how service workers worked!
But reading through the lifecycle, kind of got me to a point where I understood just enough.
We create a function showSkipWaitingPrompt that does 2 things:
- Send a message to the service worker that is “waiting” to skipwaiting, and just install itself.
- Reload the page.
Now we add an event listener that “listens” for the “waiting” message in the browser, and when it hears it, it runs the showSkipWaitingPrompt function.
Next, we register the service worker.
Plus, I chuck in a couple of console.log’s for good measure (so I can keep an eye of what’s going on).
that’s it .. that is our javascript module!!!
Note: it’s a module!!!
Which brings me to a key point!!
When loading this javascript you must load it as a module in your browser:
Notice that script tag that has type=”module”?
The Service Worker code
Now, my understanding of service workers is probably just enough to get me in trouble, so I’d love to hear people’s feedback about this.
So, here goes …
This was mostly lifted straight from Google’s Advanced Recipes.
The bit that I changed was how to handle sending the SkipWaiting message.
For some reason I couldn’t get messageSkipWaiting() loaded. I think they have removed it, but haven’t updated the documentation?
Anyway, I didn’t feel like picky to bits Google’s code, so I changed it to messageSW().
Which required me to add a small listener to the service worker:
Pretty simple: listen for a message of type ‘SKIP_WAITING’, with the content ‘SKIP_WAITING’, then execute skipWaiting(), i.e. install the new service worker.
You can also see how I configure routes to cache, and how I add the files into the cache.
Conclusion
Firstly, you can see how I use this exact principle with this web site, and how I integrate it with Jekyll.
Take a look at the TerminalAddict.com Github repository.
Now when new data is found on this website, a prompt is offered to the user, instead of hoping the user might come back later to see the new content.
Give me some feedback
As I mentioned, I’d love to hear people’s thoughts.
Service workers are very polarising in the web community, some hate them, some love them.
And everyone that uses them, seem to use them differently.
So drop me a message if you’ve got something to share.
2 Comments