The Progressive Web App Philosophy
Adapted from the presentation “The Progressive Web App Philosophy”, delivered to the Fredericksburg Developer group on February 12, 2019
A Progressive Web App (PWA) is a normal website that mimics the responsiveness and functionality that you expect from an installed app. It can work offline, has access to your camera, GPS and other hardware, accepts push notifications when it’s off, and can even be “installed” with its own icon and window. So naturally, PWAs are often thought of as a replacement for a native app. But PWAs are much more than that.
Behind every Progressive Web App is a philosophy in how to approach its architecture in. This philosophy is composed on three tenets:
- Accessibility
- Performance
- Native features
The Philosophy at its core is that the app will work effectively in any browser, for any user, with whatever features or resources however minimal. But the app will also progressively enhance its functionality to take maximum advantage of the operating environment. For example, if your app works with photos, you might start with an image upload form. Then, if the app is running on a device with a camera, enhance that experience with the ability to take a picture in real-time.
Accessibility is the most important part of the PWA Philosophy. It’s not just about accommodating disabilities. People consume information in different ways. By providing information with a variety of media, you open up the possibilities for a larger audience. A user doesn’t have to be blind to benefit from auditory feedback; nor deaf to benefit from textual descriptions. Not everyone has the patience to sit through a video tutorial, yet others would avoid reading all together. Providing visual and auditory feedback is important because it allows users to intuit the apps response to their action. In short, accessibility is not an add on. Embracing multiple perspectives can only increase the usefulness of your app.
Performance is the most straight-forward aspect of the PWA Philosophy. It means that every aspect of your app should respond to the user as fast a possible. When someone enters your website address, typically you have about 4 seconds to grab their attention before they give up and move on. An app that has to download a javascript library to display, may take too long, or not display at all. It’s important to prioritize your features. Get the most basic functionality up and running right away. Even if it means static html and css. Then lazy load in features as needed. Modern browsers offer the ability to customize the cache and pull new content in the background. And always optimize your graphics, use SVG when possible, and limit use of web fonts to get that page on the screen as fast as possible.
Native features is the aspect of the PWA Philosophy that tends to get the most attention. Modern browsers offer a wealth of access to device hardware and software. The Web Manifest gives a website the ability to be “installed” to the desktop/home screen. Service workers run in the background, even when the browser isn’t active. The Fetch, Cache and Sync APIs work together to provide offline access to your app. LocalStorage and IndexedDB store your app data locally. And things like the Location API, UserMedia API and more give access to device hardware.
With the PWA Philosophy in mind, the requirements for qualifying as an “installable ” PWA to the browser are actually quite simple. There are a lot of services and frameworks that offer easy methods of creating installable PWAs. In practice, it’s equally easy to do yourself. The advantage of learning the workings of the install process mean you can take full control of your PWA experience.
There are 3 minimum requirements to qualify as an installable PWA:
- SSL
- A Web Manifest
- A Service Worker
The first requirement, SSL, is likely already set up by your hosting company. If not, doing so is usually a simple process best done by your hosting company or sysadmin.
Now let’s take a dive into the other two requirements with a little project I call…
The 10 Minute PWA!
All of the code for the 10 Minute PWA is available on GitHub at https://github.com/mwilber/gz-10-minute-pwa Check it out and follow along as we look at the important parts.
Add a Web Manifest
A web manifest is just a JSON file containing some metadata to describe your app. Simply name it “manifest.json” and place in your website root directory. Then link to the manifest with a meta tag in your app’s index page:
<link rel=”manifest” href=”manifest.json”>
The web manifest is very straightforward and there are only a handful of values that are required.
{
"name": "GreenZeta Progressive Web App Demo",
"short_name": "GZ PWA",
"theme_color": "#7bb951",
"background_color": "#111313",
"display": "fullscreen",
"Scope": "/",
"start_url": "/",
"icons": [
{
"src": "/assets/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
],
"splash_pages": null
}
You can read up on each of the Web Manifest properties on MDN [https://developer.mozilla.org/en-US/docs/Web/Manifest]. The format is supported to varying degrees in each browser so expect results to vary for each user.
As a side note, the original app environment for the iPhone was essentially a progressive web app. As such, iOS has an alternative standard of meta tags which work much in the same way as the web manifest. While iOS Safari is gaining support for web manifest, it’s worth the effort to supply the native meta tags as well. Here is an example of the above web manifest implemented in iOS meta tags, placed in the index page header:
<meta name="viewport" content="width=device-width">
<meta name="apple-mobile-web-app-capable" content="yes" /><meta name="theme-color" content="#7bb951">
<meta name="mobile-web-app-capable" content="yes">
<link rel="apple-touch-icon" href="assets/icons/icon-512x512.png">
<link rel="apple-touch-icon" sizes="72x72" href="assets/icons/icon-72x72.png">
<link rel="apple-touch-icon" sizes="144x144" href="assets/icons/icon-144x144.png">
<link rel="apple-touch-icon" sizes="512x512" href="assets/icons/icon-512x512.png">
With the web manifest and meta tags in place, you’re already 50% of the way to meeting the installable requirement. All that remains is a service worker.
Register a Service Worker
A service worker is simply some javascript code that listens for events. These evens can come from your app front end, or the operating system itself. This is what allows a PWA to run in the background, even when the browser is not active. Service workers are sandboxed to the domain in which they are registered so they cannot interfere with other PWAs.
Before you can use a service worker, it must be registered by your app. This is done with a few lines of javascript. The following checks for serviceWorker support in the browser and then calls the serviceWorker.register() method, passing it the path to your service worker javascript file:
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('service-worker.js')
.then(registration => {
console.log('SW registered: ', registration);
}).catch(registrationError => {
console.log('SW registration failed: ', registrationError);
});
});
}
Add a Caching Strategy
Implementing a caching strategy, for offline use, is the most common function of service workers. How you do this is up to the requirements of your app, but you are required to cache at least the default page to meet the installation requirement. For this, you can implement a simple static cache. Service workers also have the ability to intercept all network requests from their registered app. It allows them to operate as a proxy and override network requests. By responding with cached items, a service worker can continue to serve data to your app even while disconnected from the internet. The following code is placed in the service-worker.js file, referenced in the code above. It contains two examples of caching strategies: “Static”, which runs on install and immediately caches hard coded requests. And “Cache First”, which returns a network fetch request from the cache if available and, if not, forwards the request to the network and caches the response.
self.addEventListener('install', function(event){
console.log('[SW] installing...');
event.waitUntil(caches.open('static')
.then(function(cache){
console.log('[SW] precaching');
cache.addAll([
'/',
'/index.html'
]);
}));
});self.addEventListener('fetch', function(event){
event.respondWith(
caches.match(event.request)
.then(function(response){
if(response){
return response;
}else{
return fetch(event.request);
}
})
);
});
Add an Install Prompt
This step is optional depending on your target browser. Some browsers will automatically prompt the user to install a PWA while others require a manual triggering. The following example runs in your app and listens for a beforeinstallprompt event. The event fires in the browser when a PWA qualifies as installable. In some browsers, the install prompt must be triggered as the result of user interaction. Therefore the event is held in a variable where its prompt() method can be called as the result of a button click event.
let deferredPrompt;window.addEventListener('beforeinstallprompt', (e) => {
// Store the install prompt event
deferredPrompt = e;
console.log('install prompt ready');
});document.getElementById('install').addEventListener('click', (e)=>{
// Show the prompt
deferredPrompt.prompt();
// Wait for the user to respond to the prompt
deferredPrompt.userChoice
.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
console.log('User accepted the prompt');
} else {
console.log('User dismissed the prompt');
}
deferredPrompt = null;
});
});
Why create a PWA?
In the context of a native app alternative, there are many advantages to a web based approach. It gives you the ability to bypass the app stores which are crowded and difficult to stand out. it also avoids the dreaded app store review process. PWAs lower the barrier to entry for users because you don’t have to redirect to an app store, they can jump right into your app from your website. And when built into an existing website, they can leverage that site’s search ranking. Finally, PWAs have a smaller footprint on the device since most of the app overhead is taken from the browser, which is installed anyway.
The PWA Philosophy improves any existing web experience, even when not focused on creating a native app. The core principles of the Philosophy: Accessibility, Performance and Native Features should be applied to all web sites. Doing so only creates the best experience reachable to the widest audience possible.