By Dean Hume

Save 37% off Progressive Web Apps with code fcchume.

 

Understanding Progressive Web Apps

Imagine you had the ability to build a website that worked completely offline, offered your users near instant load times, and yet was secure and resilient to unreliable networks at the same time. Sounds both impossible and amazing! Believe it or not, most modern browsers already come with these features built in, they just need to be unlocked. When you build a website that takes advantage of these powerful features, you have what is known as a Progressive Web App (PWA for short).

In this article, we are going to learn about what makes a web app “progressive” and how you can unlock the powerful functionality that already lies within your browser. We will also delve into “Service Workers” – the new feature that makes PWAs possible in the first place.


What is a PWA?

So what exactly makes up a PWA? At their simplest, PWAs are just normal websites. They are created using the technologies we know and love as web developers – HTML, CSS & JavaScript. However, they go a few steps further and offer users an enhanced experience. I really like the way that Alex Russell, a Developer on the Google Chrome team, describes them:

“These apps aren’t packaged and deployed through stores, they’re just websites that took all the right vitamins.”

PWAs point to a file known as a manifest file, which contains information about the website – including its icons, background screen, colours and default orientation.

PWAs use an important new feature known as Service Workers to let you tap into network requests and build better web experiences – we will talk about them more in a bit. Additionally, you can “save” a PWA to the home screen of your device. It will appear exactly as a native app would, giving you easy access to a web app at the touch of a button.

A PWA should also be able to work offline. Using Service Workers, you can selectively cache parts of your site to provide an offline experience. If you’ve browsed most websites today without an internet connection, you’ve probably seen a screen that looks similar to figure 1 below.


Figure 1 As a user, the offline screen can be quite frustrating, especially if you need to access information in a hurry!


With Service Workers, our users no longer need to face the dreaded “No internet connection” screen anymore. Using Service Workers, you can intercept and cache any network requests to and from your site. Whether you are building websites for mobile, desktop or tablet devices, you have control over how you want to respond to requests – with or without a network connection.

Simply put, PWAs are more than just a set of great new features, they are actually a way for us to build better websites. PWAs are quickly becoming a set of best practices. The steps you take to build a PWA will benefit anyone who visits your website, regardless of what device they choose to use.


Service Workers: the key to Progressive Web Apps

As I mentioned earlier, the key to unlocking the power of PWAs lies in Service Workers. At their core, Service Workers are simply worker scripts that run in the background. They are written in JavaScript, and with just a few lines of code, they enable a developer to intercept network requests, handle push messages and perform many other tasks.

Best of all, if a user’s browser doesn’t support Service Workers, they will simply fall back and your website will function as a normal website. They’ve been described as the “perfect progressive enhancement” because of this. The phrase “progressive enhancement” refers to the idea that you can build an experience that works anywhere and then enhance the experience for devices that support more advanced features.


Understanding Service Workers

So how does a Service Worker….work? Well, in order to make it as simple to understand as possible, I really like to explain them how Jeff Posnick of Google describes them:

“Think of your web requests as planes taking off. A Service Worker is the air traffic controller that routes the requests. It can load from the network or even off the cache.”

As “air traffic controllers”, Service Workers give you total control of each and every web request made from your site, which opens up the possibility for many different use cases. In the same way that an air traffic controller might redirect a plane to another airport, or even delay a landing, a Service Worker enables you to redirect your requests or even stop them completely.

While Service Workers are written in JavaScript, it’s important to understand that they are slightly different from your standard JavaScript file. The Service Worker:

  • runs in its own global script context;
  • isn’t tied to a particular web page;
  • isn’t able to modify elements in the web page – it has no DOM access; and
  • is HTTPS only.

You don’t need to be a JavaScript master to begin experimenting with Service Workers. They are event-driven and you can simply pick and choose the events that you want to tap into. Once you have a basic understanding of the different events, getting started with Service Workers is easier than you think!

Let’s have a look at figure 2 below, in order to better explain Service Workers.


Figure 2 Service Workers are able to intercept incoming and outgoing HTTP requests, giving you total control of your website.


In the diagram above, you can see that the Service Worker sits on a different thread and is able intercept network requests. Remember that Service Workers are like “air traffic controllers” that give you total control of network requests coming and going from your website. This ability makes them extremely powerful and allows you to decide how to respond.


The Service Worker Lifecycle

Before we dive into a coding example, it’s important to understand the different stages a Service Worker goes through in its lifecycle. In order to explain this better, let’s imagine a basic website that uses a Service Worker under the hood. The website is a popular blogging platform that millions of writers use every day to share their content.

In its simplest form, the website constantly receives requests for content, including images and even videos. In order to understand how the Service Worker lifecycle might fit into this, let’s pick one of these millions of interactions that go to and from the website every single day.

Figure 3 shows the Service Worker lifecycle that will take place when a user visits a blogging page on this website.


Figure 3 The Service Worker life cycle.


Let’s walk through figure 3 in step-by-step, in super slow motion, so we can understand how the Service Worker lifecycle fits into this.

When a user navigates to a URL for the first time, the server will return a response for the web page – you can see that in step 1 of figure 3, where the Service Worker beings downloading when you call the register() function.  During the registration process, the browser will download, parse and execute the Service Worker (step 2). If, at any point during this step, there is an error, the register() promise will reject and the Service Worker will be discarded.

As soon as the Service Worker successfully executes, the install event is then activated (step 3). One of the great things about Service Workers is that they are event-based, which means that you can tap into any one of these events.

Once the install step has completed, the Service Worker is then activated (step 4) and in control of things within its own scope. If all of these events in the lifecycle were successful, your Service Worker is in place and being used!

It’s important to note that when you load a page for the first time (without an active Service Worker), any requests coming or going won’t be handled by the Service Worker. Only once it has been installed and activated, is it in control of its own scope. This means that the logic inside the Service Worker will only kick in if you refresh the page or navigate to another page.

A basic Service Worker example

I’m sure by now you are itching to see what this might look like in code form, so let’s get started.

Because a Service Worker is simply a JavaScript file that runs in a background thread, you reference it in an HTML web page the same way you would any JavaScript file. Let’s imagine we’ve created a Service Worker file and named it sw.js. In order to register it, use the following code in your HTML web page (see listing 1).


Listing 1 An example Service Worker file

 
 
<html> 
<head>The best web page ever</head> 
  <body> 
    <script> 
    // Register the service worker 
   if ('serviceWorker' in navigator) {                                       ❶     
  
   navigator.serviceWorker.register('/sw.js').then(function(registration) {  ❷     
    // Registration was successful 
   console.log('ServiceWorker registration successful with scope: ', registration.scope);   
    }).catch(function(err) {                                                                
    // registration failed :( 
    console.log('ServiceWorker registration failed: ', err); 
    }); 
   } 
  </script> 
  </body> 
</html>
 
 

❶    Check to see if the current browser supports Service Workers

❷    If it does, register a Service Worker file called ‘sw.js’

❸   Log to the console if successful

❹   If something goes wrong, catch the error and log to the console


Inside the script tag, we are first checking to see if Service Workers are actually supported in the browser. If they are, we register it using the navigator.serviceWorker.register(‘/sw.js’) function which in turn notifies the browser that it needs to download the Service Worker file. If the registration is successful, it will then begin the rest of the stages of the Service Worker lifecycle.

In the code example above, you may notice that the JavaScript code isn’t using callbacks. That’s because Service Workers use JavaScript Promises which are a really clean, readable way to deal with callbacks. A Promise represents an operation that hasn’t completed yet, but is expected in the future. This lets asynchronous methods return values like synchronous methods and makes writing JavaScript cleaner and also a lot easier to read. Promises can do a great many things, but for now, all you need to know is that if something returns a promise, you can attach .then() to the end and include callbacks inside it for success, failure, etc.

The navigator.serviceWorker.register() function returns a promise, and if the registration is successful we can decide how we want to proceed.

Earlier I mentioned that Service Workers are event-driven, and one of the most powerful features of Service Workers is that they allow you to listen out for any network requests by tapping into the fetch event. When a fetch event occurs for a resource, you can decide how you want to proceed. You could alter anything on the outgoing HTTP request or the incoming HTTP response. It’s rather simple, but extremely powerful at the same time!

Let’s imagine the following snippet of code inside your Service Worker file (see listing 2).


Listing 2 An event listener

 
 
self.addEventListener('fetch', function(event) {     ❶     
  if (/\.jpg$/.test(event.request.url)) {            ❷     
    event.respondWith( 
      fetch('/images/unicorn.jpg, {                    
        mode: 'no-cors' 
      }) 
    ); 
  } 
});
 
 

❶   Add an event listener to the fetch event

❷   Check to see if the incoming HTTP request if for an image of type JPEG

❸   Try and fetch an image of a unicorn and then respond with it instead

 


In the code above, we are listening out for the fetch event and if the HTTP request is for a JPEG file, we are intercepting it and forcing it to return a picture of a unicorn, instead of its original intended URL. The code above will do this for each and every HTTP request made for a JPEG file from the website. While pictures of unicorns are awesome, you probably wouldn’t want to do this for a real world website (everyone knows that unicorns are indeed awesome, but your users might eventually get tired of getting pictures of them instead of what they are searching for). The coding example above gives you an idea of what Service Workers are capable of. With just a few lines of code, we’ve created a powerful proxy within the browser!

Security Considerations

In order for a Service Worker to run on a website, it needs to be served over HTTPS. While this does make it a little tougher to get started using them, there is an important reason for this. Remember the analogy of a Service Worker being like an air traffic controller? With great power comes great responsibility, and in the case of Service Workers, they could actually be used for malicious purposes too. If someone was able to register a dodgy Service Worker on your web page, they would be able to hijack connections and redirect them to a malicious endpoint. In fact, the bad guys might be able do whatever they wanted with your HTTP requests! To avoid this, Service Workers have been built so that you can only register them on web pages that are served over HTTPS. This ensures that the web page hasn’t been tampered with during its journey through the network.

If you are a web developer that wants to get into building PWAs on the side, you might be a little disheartened at this point – don’t be! Getting SSL certificates for your website might have traditionally cost you quite a bit of money, but believe it or not, there are actually many free solutions available to you as a web developer today.

First, if you would like to test Service Workers on your own computer, you can do so by serving pages from your localhost. They’ve been built with this feature in mind and it makes it easier for developers to develop locally before deploying their applications live.

If you are ready to release your PWA to the world there are a few free services that you can use. Let’s Encrypt (https://letsencrypt.org/) is a new Certificate Authority that is free, automated, and open. You can quickly get started serving your site over HTTPS using Let’s Encrypt. If you’d like to learn more about Let’s Encrypt, head over to their “getting started” page (https://letsencrypt.org/getting-started/).

If, like me, you use GitHub for source control, then you might have come across relevant GitHub pages (see figure 4). You can host directly from your GitHub repository for basic websites without a back end.


Figure 4 GitHub Pages allow you to host a website over SSL directly from a GitHub repository.


The advantage of using GitHub pages is that, by default, your web pages are served over HTTPS. When I first started experimenting with Service Workers, GitHub pages allowed me to quickly spin up a website and test out an idea in no time.

Hopefully this article has made you want to learn more about PWAs! For more information, download the free first chapter of Progressive Web Apps and see this Slideshare presentation. Don’t forget to save 37% with code fcchume.