DEV Community

Cover image for #04 - Make Progressive Web Apps Reliable
Nitya Narasimhan, Ph.D for Microsoft Azure

Posted on • Originally published at microsoft.github.io

#04 - Make Progressive Web Apps Reliable

Welcome to Day 4 of #30DaysOfPWA! New to the series? Three things you can do to catch up:

This is a shortened version of this canonical post for the #30DaysOfPWA.


What We'll Cover Today

  • What is a Service Worker?
  • Why is HTTPS essential for PWA?
  • Understand registration and lifecycle events?
  • How are service workers used in PWA?
  • Exercise: Inspect the service worker in a real app!

In our last post we covered Web App Manifests that help make PWAs installable. Today we'll explore the remaining building blocks (HTTPS, Service Workers) with specific focus on using Service Workers.


PWAs are like startups!

To set the stage, let's use another analogy. We previously talked about how Web App Manifests are like app resumes. Now think about the PWA like a startup - with each technology being a founding team member with a specialized purpose that helps deliver a progressively-enhanced experience.

  • The app page is the CEO - it drives the core experience and remains responsive to user needs and interactions.
  • The Web App Manifest is the resume - it describes app identity, brand and capabilities to devices and app stores for installability.
  • HTTPS is the Chief Security Officer (CSO) - it encrypts end-to-end communications between app and server endpoints for safety.
  • The Service Worker is the Chief Operations Officer (COO) - it unblocks the CEO from performing time-consuming or synchronous tasks, and takes proactive actions to ensure reliable operation even when offline.

Image describes relationship of PWA startup.

Let's see how this translates to PWA implementations and making characteristics like network-independence, safe operation and re-engageable user experiences, possible.


Make PWAs Safe

HTTPS enforces end-to-end encryption of all client-server communications, providing privacy safeguards for information exchanged over the network. Visualize this as a Chief Security Officer locking down all entry and access paths into your startup and protecting information from malicious third-party access or tampering.

HTTPS support is mandatory for using Service Workers. Thankfully, as we covered in our earlier post, it is easy to implement HTTPS support. Use modern cloud hosting providers (who enable it by default) or take advantage of free certificate options (e.g., Let's Encrypt) to secure your own servers.

Make PWAs Reliable & Re-Engageable

Service Workers are a special type of Web Worker. Web Workers operate in a separate thread, allowing them to execute long-running or asynchronous tasks in the background, minimizing the impact on page performance ("unblocking" the CEO).

Service Workers make PWA operation reliable by helping deliver usable experiences even under flaky or offline network conditions. They do this by intercepting network fetch requests from the page (CEO) and strategically handling them using cached responses (if offline), or network-fetched resources (if real-time), or some combination of both based on predefined caching strategies for resource types.

Service Workers make PWAs re-engageable by having the ability to alert users to app changes or context, even if the page itself is inactive. They do this by listening for asynchronous push notifications (from a server) and working with platform capabilities to deliver alerts in a device-familiar way. When users engage with the alert, they are drawn back into the app seamlessly - just like with other native app experiences.


How do Service Workers work?

From a development perspective, we need to know two concepts:

  • Service Worker Registration - where CEO "hires" the COO.
  • Service Worker Lifecycle - where COO "handles" operational events.

Let's look at registration first. Like all Web Workers, the Service Worker must be authored in its own file. The location of that file (relative to the root of the app) defines the scope of its authority. Service Workers can only intercept or manage requests to pages within their scope. Placing the file at the root of your app ensures your service worker will manage all pages within it.

Let's inspect the DevTools Tips PWA in the browser again. Look at Service Workers under the Application tab. We can see that the service worker is implemented in the "sw.js" file in the root directory - implicitly setting its scope to the whole app.

Inspect PWA in DevTools

If you inspect the application source (in Elements tab) you will find this snippet of code for service worker registration:

if('serviceWorker' in navigator) {
    // Register the service worker
    navigator.serviceWorker.register('/sw.js', {
        scope: '/'
    });
}
Enter fullscreen mode Exit fullscreen mode

Because Service Worker is a more recent technology that may not be supported on all browsers, we test for its existence before registering it. The scope is set implicitly by the file location - the code shows how you can explicitly set this if needed.


Service Worker: Lifecycle Events

Service worker registration is like onboarding the COO. Once that is complete, the service worker is ready to listen for lifecycle events (install, activate) to set itself up for success. Think of this as three phases:

  1. Registration: The browser registers the service worker, kicking off the Service Worker lifecycle.

  2. Installation: The browser triggers install as the first event to the Service Worker. It can use this for pre-caching resources (e.g., populate cache with long-lived resources like logos or offline pages).

self.addEventListener( "install", function( event ){
    console.log( "WORKER: install event in progress." );
});
Enter fullscreen mode Exit fullscreen mode
  1. Activation: The browser sends the activate event to indicate that the service worker has been installed. This service worker can now do clean up actions (e.g., remove old caches from prior version) and ready itself to handle functional events. If there is an old service worker in play, you can use clients.claim() to immediately replace the old service worker with your new one.
self.addEventListener( "activate", function( event ){
    console.log( "WORKER: activation event in progress." );
    clients.claim();
    console.log( "WORKER: all clients are now controlled by me! Mwahahaha!" );
});
Enter fullscreen mode Exit fullscreen mode

Service Worker: Functional Events

Functional events are those that require the asynchronous or background processing abilities of service workers to support reliable and re-enageable behaviors. For now, think about just two: "fetch" and "push".

  1. The fetch event is triggered when the browser tries to access a page that lies within the scope of the service worker. The service worker acts as an interceptor - returning a response either from the cache or from the network (or some combination of both) based on predefined strategies. We'll cover more on this in the next post.
self.addEventListener( "fetch", function( event ){
    console.log( "WORKER: Fetching", event.request );
});
Enter fullscreen mode Exit fullscreen mode
  1. The push event is triggered when the browser receives a push message from a server to display as a toast notification to users. This occurs only if the PWA had previously subscribed for server notifications and user has granted the PWA permission to receive them. Push events are critical to re-engaging users when the app is not otherwise active.
self.addEventListener( "push", function( event ){
    console.log( "WORKER: Received notification", event.data );
});
Enter fullscreen mode Exit fullscreen mode

In our next post, we'll dive into details of service worker support for offline operation - looking at how service workers engage with the Fetch and Cache Storage APIs to provide continuity of experience in a network-independent manner. For now, it's time for a hands-on exercise!


Exercise: Explore Service Workers

Use DevTools to inspect a different sample PWA and see if you can identify and understand the service worker hooks and implementation:

  • Go to Elements tab and explore the application source
    • Where is the service worker registered?
    • What is the scope for registration?
  • Go to Applications tab and explore the service worker file
    • What events is it handling?
    • Can you understand its caching strategy (for fetch)?
    • Is it re-engaging the user (with push)?
  • Go to the Cache Storage section
    • What files or assets do you see stored there?
    • How do these correlate to actions taken for install event?
  • Go to the Service Workers section - click "Offline"
    • What happens when you reload the page?
    • What happens when you go to a different site (offline)?

Want to read more content from Microsoft technologists? Don't forget to follow Azure right here on dev.to:

Oldest comments (2)

Collapse
 
jeffposnick profile image
Jeff Posnick • Edited

This is great, Nitya! One correction to add:

If there is an old service worker in play, you can use clients.claim() to immediately replace the old service worker with your new one.

clients.claim() does not affect whether an old service worker is immediately replaced by a new one. (If there is both an old and a newly updated service worker with the same scope, the new one will remain in a "waiting" state indefinitely until either a) all the clients of the old service worker have closed or b) the new service worker calls self.skipWaiting().)

clients.claim() is useful during the very first time a service worker is registered for a given scope, if you want it to take control of pages that are already open after it finishes installation. If you don't call it, the newly registered service worker won't be in control of anything already open, and it will only start controlling pages after the next navigation to an in-scope URL.

There's more info in Jake's fantastic "The Service Worker Lifecycle".

Collapse
 
nitya profile image
Nitya Narasimhan, Ph.D

Thanks Jeff!! Will take this change back to the main docs and update along with other feedback, to provide clarifications.