Web Push Notifications – The Beginning

Push Notifications

Web Push Notifications – From Hackathon to Live

Every year Moonpig hosts a hackathon for everyone within the company. You have 24 hours to develop whatever you want, and the best hacks receives a price. This year, a few of us decided to try and add Web Push Notifications to the website. The idea attracted a lot of attention and we decided that we should try it on the live website.

As we have no idea if this is something customers will find useful we decided to start by implementing the sign-up process. If enough people sign up, we will go ahead and implement the sending of the notifications as well.

In this article, I will focus on how we implemented the sign-up process, and especially the problems we ran into when bringing the code up to live standard. Before we begin I will give a brief overview of what web push notifications are, and how they work.

What are web push notifications?

Web push notifications are a way of communicating with a user even though they are no longer browsing your site. You can push a message which will be displayed as a notification in the user’s browser. You may have noticed these kinds of notifications appearing from a few of the big sites, such as Facebook and YouTube.

Web Push notifications are usually mentioned together with Progressive Web Apps a term coined by Google in 2015. This is a software development methodology focused around creating web apps that acts like a hybrid of traditional websites and mobile apps. These web apps need to be reliable, fast and engaging to keep the user wanting to come back. Web push notifications are a key concept for this to be possible.

How does it work?

In short push notifications work by using the Push and Notifications APIs. These are exposed via a new a technology called “Service Worker”. A service worker is a piece of JavaScript that can run in the browser but outside of your website.

When you want to subscribe a user to push notifications you register a service worker which will give you access to the Push and Notification APIs. The service worker registers itself with the browser messaging server and provides you with the subscription information. You need to store this information on your server and use it to push new notifications.

When you want to send a push notification you communicate with the messaging server and the messaging server forwards the notification to the browser. For a more detailed explanation please see Googles tutorial.

Registering the service worker

The first thing you need to do is register the service worker. You can see an example below.

<script type="text/javascript">
    if ('serviceWorker' in navigator) {
        window.addEventListener('load', function () {
            navigator.serviceWorker.register('<path to service worker javascript>');
        });
    }
</script>

Once the service worker is registered you can access it using the function below.

navigator.serviceWorker.ready

The function returns a promise and when that promise is resolved it passes the service worker reference as a parameter. During the hackathon, we placed the service worker script in the root of the website. Now we moved the script into a separate project that contains all of our front end resources. After the move the promise stopped resolving. This is when we learned about service worker scopes.

Service Worker Scopes

A service worker scope is by default set to the folder of the service worker script. Using the below registration the scope would be “/cdn/folder1/folder2/”.

navigator.serviceWorker.register('https://www.moonpig.com/cdn/folder1/folder2/service-worker.js');

When we tried to use the service worker from a different path on the website (for example the root) the ready function was never fired (as that path was not within the service workers’ scope).

To solve this, the registration of the service worker can be changed to include a scope parameter. This allows us to set the scope to something other than the current directory.

<script type="text/javascript">
    if ('serviceWorker' in navigator) {
        window.addEventListener('load', function () {
            navigator.serviceWorker.register('<path to service worker javascript>', { scope: '/' });
        });
    }
</script>

This on its own is not enough. If you run this, you will notice that you get an error in the console.

Failed to register a ServiceWorker: The path of the provided scope ('/') is not under the max scope allowed

For security reasons, the server has to approve that the service worker changes the scope to a path which sits above the service worker itself. To do this the request to retrieve the service worker Javascript need to return a header with the name “Service-Worker-Allowed”, and the value of the allowed scope (in our case “/”). As we are running an ASP.NET website we choose to include a web.config in the folder of the service worker script to ensure this header is set.

<configuration>
  <system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="Service-Worker-Allowed" value="/" />
      </customHeaders>
    </httpProtocol>
  </system.webServer>
</configuration>

Once we added the header the service workers’ ready function was called as expected.

Subscribing a user

I’m not going to go into too much detail on the code we use to subscribe the users as it is pretty much exactly as described in the examples from Google. The main difference is that we put most of the code behind an abstraction layer that allowed us to easily communicate with the service worker from our Angular front end. Let’s have a look at some code.

if ('PushManager' in $window && 'Notification' in $window) {
	if (notificationsSubscriptionService.notificationPermission() !== "denied") {
		notificationsSubscriptionService.isSubscribed().then(function (isSubscribed) {
			if (!isSubscribed) {
				if (notificationsSubscriptionService.notificationPermission() === "granted") {
					autoSubscribe();
				} else {
					promptForSubscription();
				}
			}
		});
	}
}

The purpose of the code is to subscribe the user to our notifications. When they have already allowed notifications we can do this automatically. The push API will detect that the user has already allowed notifications and will not prompt for permissions again. This allows us to sign up users very easily.

On the other end of the spectrum, when a user has already blocked permissions the browser will not allow us to prompt them again. Instead you will have to display a message explaining how to unblock the notifications. As this is currently not a very user friendly process we have opted to not display anything in this case. As we have never offered notifications before there should not be many people who have already blocked us. This is something we may reevaluate later.

The most common use case will be users who still have their permissions set to the default setting. These users will see a window explaining what the notifications are. If they click subscribe we will subscribe them via the push API and the browser will prompt them to allow notifications. It will be very interesting to see how many persons choose to subscribe put decide to opt out due to the browser permissions.

Loading the service worker script

At this point we thought we had everything ready to go. However, we ran into one last problem when we deployed this to our staging environment.

Staging is the first environment where we load our resources using our CDN. When we loaded the site the service worker was unable to register. This is when we learned that for a service worker to be loaded the script has to be downloaded from the same domain as the website you request it from. A good explanation on why this is required can be found in the following Github issue.

In our case, we choose to download the script from the server instead of the CDN as it seemed like the easiest option at the time. We have since learned about importScripts which is mentioned in the above Github issue. It seems like we should be able to import a script from CDN that way, however it is not something that we have tried.

Final thoughts

Web push notifications seems like an exciting new technology and from a technical point of view it was not too difficult to implement. The question is, will the users like it?

We are currently running a test on production that allows users to sign up to web push notifications and will be using the data from the test to decide what the next steps are. Hopefully the interest will be great enough for us to implement sending the notifications. If that is the case, expect a follow up post.

Regardless of if web push notifications turn out to be a success, service workers as a technology offers other exciting possibilities such as offline access. This is something else we will be looking at in the coming weeks.

Make sure to check back in for updates.

Leave a Reply

Your email address will not be published. Required fields are marked *