Last October I visited the TechDays conference in Amsterdam. One of the sessions I attended was Jeff Burtoft’s on Progressive Web Apps (PWA). He did a talk on the same subject last year and I remember finding it interesting back then but the tech was fairly new and browser support wasn’t great so I didn’t follow up on it. This year was different though, all major browsers are working on implementing the API’s required to run a PWA, it seems the time was right to start exploring Progressive Web Apps. I left Jeff’s session inspired and I was eager to get started with Progressive Web Apps myself. I was already working on a personal project to get (more) familiar with technologies like ASP.NET Core 2.0 (with JavaScript services), webpack, CosmosDB, Auth0 and Aurelia so I decided to transform this Single Page Application (SPA) into a Progressive Web App (PWA).
PWA - a very short intro
It’s not the intention of this blog post to give you a full intro on PWAs, there are plenty of great resources out there doing that already.
In short a PWA works on any device and enhances functionality progressively based on the capabilities of the browser and device. PWA’s promise is that it combines the strengths of both web apps and mobile apps. With capabilities and features like
- Using the application shell model to make it look and feel like a native app
- Installable to home screen
- Support for offline or low connectivity scenario’s
- Sending push notifications
it provides the user with an app-like user experience. But because it is still a web app there is no app store involved which means you don’t have to stand out between all other competitors and users don’t need to download, install and update an app and can preserve storage on their devices. And as a bonus we developers don’t need to make the extra effort to get the app in the major app stores. We just publish and update our web app to always deliver the latest and greatest to our users.
Of course there are also some requirements:
- The website needs to be served from a secure origin, so use HTTPS or HTTP2
- A service worker must be installed and running
- A manifest.json must be present
- An iconset must be available
Service Workers are scripts written in JavaScript that run in the background. A service worker acts as a proxy between the web app and the server. It enables us developers to intercept network requests, handle push messages (even when the app is closed) and perform other tasks like caching.
The manifest provides information about the PWA (f.e. name, description, icons, theme colors) and is used by browsers (or other user agents) to know about its requirements and give access to needed features.
Meeting the requirements
I started this project as an ASP.NET Core 2.0 project based on the SPA template with Aurelia as the JavaScript framework of choice.
Unfortunately Aurelia is not visible as a default choice when starting a new ASP.NET Core Web project from Visual Studio 2017 (only Angular and React are). If you want to create a new ASP.NET Core 2.0 project with Aurelia you can type dotnet new aurelia
at the command line. You might need to install the SPA templates first dotnet new --install "Microsoft.AspNetCore.SpaTemplates::*"
After generating this project only some small efforts are required to turn this SPA into a PWA.
Provide a secure origin
I host my web app on Azure with the default domain azurewebsites.net. This domain already comes with an SSL certificate so it can be loaded over HTTPS. This means I just need to make sure that all incoming traffic will go over HTTPS. We can easily manage this in the configuration of ASP.NET Core. Add the following to the ConfigureServices
method in Startup.cs
services.Configure(options =>
{
options.Filters.Add(new RequireHttpsAttribute());
});
This will make sure all requests require HTTPS and all requests over HTTP are ignored. Of course we do not want to lose all HTTP traffic so the next step is to redirect all requests over HTTP to HTTPS. Add the following lines to the Configure
method in Startup.cs
var options = new RewriteOptions()
.AddRedirectToHttps();
app.UseRewriter(options);
Now all traffic to the web app will run over HTTPS.
PWA Builder
For meeting the other requirements I used the website PWA builder. This generator will create the following:
- Manifest
- Iconset
- Service worker
- A script to register the service worker
The first step in the wizard will create the manifest. Just enter the URL of your web app and hit’Get Started'. The wizard will prompt you for some information about your web app. The following fields are required if you want browsers to show your web app visitors the ‘Web App Install Banner’ (this will add your web app to the home screen):
- Name
- Short name
- The image to create icons from
- Start URL
- Choose ‘standalone’ for display
It is also preferred to pick a theme color (do not use the default value ‘transparant’).
The second step will create the service worker script and the script to register it in the web app. You will be presented with some options for the functionality of the service worker. I went for the ‘Offline copy of pages’ option.
In the final step you can download a zip file with all the created files.
Turn your SPA into a PWA
Extract the downloaded zip file and move the manifest (manifest.json), service worker (pwa-builder-sw.js) and the images folder to the wwwroot folder of the project.
Now that all files are in place we need to use them in the existing web app. Open the _Layout.cshtml
file and add a link tag pointing to the manifest file <link rel="manifest" href="../manifest.json" />
. The manifest is now wired to our web app and this also makes our PWA discoverable to search engines and app stores. Based on the information in the manifest they can now index and ingest our PWA.
Keep the layout file open and insert the script to register the service worker
<script>
//This is the "Offline copy of pages" service worker
console.log(location.pathname);
//Add this below content to your HTML page, or add the js file to your page at the very top to register service worker
if (navigator.serviceWorker.controller) {
console.log('[PWA Builder] active service worker found, no need to register')
} else {
//Register the ServiceWorker
navigator.serviceWorker.register('../pwabuilder-sw.js', {
scope: './'
}).then(function (reg) {
console.log('Service worker has been registered for scope:' + reg.scope);
})
}
</script>
Now we should be all set to run our PWA for the first time. Build or publish and browse to the web app. Open the DevTools (I use Google Chrome) and go the application tab. Verify that the manifest is found and the service worker is installed.
That’s it, we’ve build our first PWA!
Analyse and test the PWA
To see how well our SPA meets the standards of a Progressive Web App we can use a tool by Google called Lighthouse. Lighthouse has audits for performance, accessibility, progressive web apps, and more. It can be installed as an extension to the Chrome DevTools, as a Chrome extension or run from the node command line. I installed it into the Chrome DevTools.
Once installed go to the ‘audits’ tab in Chrome DevTools and select ‘Progressive Web Apps’ as the audit to perform.
Click ‘Run audit’ and wait for Lighthouse to complete the tests.
Lighthouse will return an overall score and an overview of passed and failed audits. Most of the time failed audits can easily be fixed with the description provided by Lighthouse. In one of the first audits I ran, Lighthouse suggested to add a meta tag with the theme color to the page. Adding <meta name="theme-color" content="#ffffff" />
to the _Layout.cshtml
file made the audit pass.
Once we have a score close to 100% we can start testing on a mobile device that supports Service Workers. An Android device running an up-to-date version of Chrome is a good choice. Browser support for Service Workers can be checked on caniuse. When using the web app the browser will (eventually) show a banner asking us to add the web app to the home screen.
This could happen to first time we browse to the web app or it can take some more visits. Chrome uses a set of criteria and visit-frequency heuristics to determine when to show the banner. Once the web app is pinned to the home screen it will act like any other app that is installed on the device. It will start up with a splash screen (formatting is based on values in the manifest).
It can also be selected and closed like any other app.
Conclusion
In this blog post we turned a Single Page Application into a Progressive Web App with just some minor adjustments and additions to the original web app. Of course this is just a basic implementation for a PWA. The PWA can become even more progressive by adding features like push notifications and media capture, and adjusting to offline scenario’s with background syncing and caching. I might use these as a topic for future posts.
Update January 2018: For those interested in adding push notifications to a PWA, I covered the subject of push notifications in this blog post.