by Adrien Zaganelli
In today’s world, connectivity has made us more mobile than ever which (paradoxically) cause us to be offline sometimes: when we’re in airplane mode, have a bad connection, have no more data, are on the subway…and so on.
A second effect of this mobility is the slow loading of heavy websites: Amazon found that just 100 milliseconds of extra load time cost them 1% of sales.
We can easily see there is a demand for this feature and how it can benefit your business.
It is time for the web to go offline.
What is a service Worker (SW)?
The agency BETC made a demo website called whentheinternetisdown.com for the french telecom company Bouygues. It only works offline and feels kind of magical. Go try it out :)
It’s the caching that makes the magic of the site: you can come back in 3 weeks, 1 month, 1 year, still without a connection, and access all the content. — Maxime Huygue, Head of BETC Digital Studio
Ok this is cool, tell me how to do it then.
Ok, let’s begin with some prerequisites:
- In order to use SWs, you must enable https on your website.
- SWs works in all modern browsers except our friend IE.
- Understand how databases operate.
Service workers lifecycle
To be able to work, SWs must be registered within your application, then installed. You should check if SWs are compatible with your client before doing so.
If available, register your SW file. It will return a promise, so you can handle errors. You can also specify a scope of urls in the registration options.
Then open the method or create a cache linked to a desired name. The returned promise needs to be wrapped into event.waitUntil(), which will delay the installation of the service worker until the promise is resolved. Otherwise, the install event fails and the service worker will be discarded.
Please be cautious with caching: your user’s storage is precious, so don’t abuse it. Also, be careful: the install event can only be called once, and you’ll need to update your SW to modify it.
This one is a bit subtle.
Once the installation has completed, the service worker isn’t active yet: we are in the installed state.
In this state, it waits to take control of the page. It then moves on to the next phase in the lifecycle, which is the activation phase.
The activation phase is handy when you update a SW. The most common case is to clear the cache of the previous SW installed.
Please note that, once successfully installed, the updated worker will wait until the existing worker is controlling zero clients (clients overlap during a refresh).
self.skipWaiting() prevents the waiting, meaning the service worker activates as soon as it’s finished installing. The advantage of this method is that you can receive fetch events faster.
It doesn’t really matter when you call skipWaiting(), as long as it’s during or before waiting. It’s pretty common to call it in the install event.
Phew! Let’s take a break and sum up what we’ve seen:
- We explored the SW Lifecycle: registration, installation, activation
- We learned how to implement common uses cases such as: caching resources and clearing caches with the Cache API.
- We saw that self.skipWaiting and self.clients.claim allow us to activate SWs faster in order to catch events faster.
Ok moving right along…
The fetch event allows us to intercept network requests and store responses or customize them.
The main advantage of this hook is to return the cached resources instead of making a request call. You should take a look at the Fetch API for handling your request calls.
We can’t cover all the possibilities offered by service workers in one article. Still, I encourage you to look into what’s possible: Custom 404, Background Sync API for offline analytics, ServiceWorker-side templating…. the future looks exciting!
So far we’ve seen what a service worker is, how it works through its lifecycle, and the most common use cases by playing with Cache and Fetch API. Those two APIs give us a whole new way of managing URL addressable resources in the browser. To complete this guide, let see how we can store other types of data, for example a user’s JSON from your database.
Store custom data with IndexedDB
A general guideline for data storage is that URL addressable resources should be stored with the Cache interface, and other data should be stored with IndexedDB. For example HTML, CSS, and JS files should be stored in the cache, while JSON data should be stored in IndexedDB. Note that this is only a guideline, not a firm rule. (source)
In short, we will see when you shouldn’t use Cache API but IndexedDB instead. Both are asynchronous and accessible in service workers, web workers, and the window interface . The good news is that it is well supported, even in recent versions of IE.
IndexedDB is a NoSQL database. IndexedDB data are stored as key-value pairs in object stores rather than tables. A single database can contain any number of object stores. Whenever a value is stored in an object store, it is associated with a key. It looks like this:
Pretty classic, right? The main thing to understand is the concept of key path. It tells the browser which key to use to extract data from in the object store or index.
In this example, we can see that our key path is the property id, and it is defined in line 10. Then we return all the items from the database. This is a very basic use case, so if you want to learn more about how IndexedDB works, I advise you to read this excellent article.
We’ve seen how to use them trough the service worker lifecycle and what you can do by using the Cache and Fetch API. The possibilities are almost limitless. so be creative and not too greedy on the device storage.
You can even use databases offline: that what’s IndexedDB is made for. These offline capabilities certainly are part of the future of the web, so it plays well with the new type of websites that Google are creating: Progressive Web Apps.
- The Offline Cookbook: https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/
- PWA and offline: https://developers.google.com/web/ilt/pwa/lab-offline-quickstart
- Lab: Caching Files with Service Worker: https://developers.google.com/web/ilt/pwa/lab-caching-files-with-service-worker
- The Service Worker Lifecycle: https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle
- Demystifying The Service Worker Lifecycle: https://scotch.io/tutorials/demystifying-the-service-worker-lifecycle
- Activate Service Workers Faster: https://davidwalsh.name/service-worker-claim
- Live Data in the Service Worker: https://developers.google.com/web/ilt/pwa/live-data-in-the-service-worker
- IndexedDB Basic concepts: https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Basic_Concepts_Behind_IndexedDB
- Getting Started with Persistent Offline Storage with IndexedDB: https://itnext.io/getting-started-with-persistent-offline-storage-with-indexeddb-1af66727246c