Introduction to Progressive Web Applications

Balaji Kamalesh
Mar 23 · 7 min read

An In-depth look at PWA, its principles and implementations.

A Progressive web application (PWA) is a web app that looks and feels like a native app on a wide range of devices (especially mobiles). It uses modern web capabilities to provide features like offline capability, background sync, push notifications and many more. In this post, we’ll understand PWA and how PWA principles can be easily integrated into your existing application. Let’s get started.

Why Native apps are popular?

The following is the percentage of time spent by an average user in Mobile web vs Native Apps:

Source: comScore Mobile Metrix, US Age 18+, 2015

Some of the reasons why users spend more time in native apps over the mobile web are:

  1. Push notifications bring users back to the application at any time.
  2. The home screen icon makes access easier.
  3. Access to native device features like camera etc keeps the user engaged.
  4. It will possibly work offline as well.

But do you think users equally prefer all the native apps in their mobile devices? Definitely not.

Source: comScore Mobile Metrix, US Age 18+, 2015

And I am pretty sure its Whatsapp, Facebook, and Youtube for most of the people.

Then why not just build a native app instead of a PWA?

Yes. That’s probably a good idea. BUT :

  1. You have to learn different programming languages.
  2. Even though the time spent by a user is high on native apps, more users use Mobile web compared to native app
figures are in millions, Source: comScore Mobile Metrix, US Age 18+, 2015

With the above numbers, it feels like building a PWA is not a bad idea after all. With PWA we can combine the best of both worlds.

Progressive web applications combine the best of both worlds

Core Principles of Progressive Web Application :

  1. Application manifest — Allows the addition of our application to the home screen of the device (like an icon).
  2. Service Worker — enables caching, offline support, background sync and mobile like push notifications.
  3. Responsive Design — the application should look and work well across devices.

Let’s look into these principles one by one in detail.

Application Manifest :

The Application manifest is a simple file that we can add to the application code, which enables us to add our application to our device’s home screen as an icon and access it from there. The application manifest provides information about the application including name, author, description, icon details, screen orientation, scope, etc. The manifest file needs to be added to the root directory of our application code (for ex: the directory which contains index.html file)

The reason why application manifest is useful is that the user need not remember our application’s URL for accessing it, which increases user interactivity. A typical application manifest file looks like this:

// manifest.json
{
"name": "Voice Memos",
"short_name": "VoiceMemos",
"icons": [
{
"src": "/src/images/icons/app-icon-48x48.png",
"type": "image/png",
"sizes": "48x48"
},
{
"src": "/src/images/icons/app-icon-96x96.png",
"type": "image/png",
"sizes": "96x96"
},
{
"src": "/src/images/icons/app-icon-144x144.png",
"type": "image/png",
"sizes": "144x144"
},
{
"src": "/src/images/icons/app-icon-192x192.png",
"type": "image/png",
"sizes": "192x192"
}
],
"start_url": "/index.html",
"scope": ".",
"display": "standalone",
"orientation": "portrait-primary",
"background_color": "#fff",
"theme_color": "#3f51b5",
"description": "sample application description",
"dir": "ltr",
"lang": "en-US"
}
Click on “Add to Home screen”

After adding the application manifest, browse for your application from your mobile web browser and click on “Add to Home screen”. This should add your application as an icon to your device’s home screen.

Service Workers :

Service workers are javascript codes that browsers run in the background, in a separate thread. Like manifest file, the service Worker file also needs to be added to the root directory of our application (ex: SW.js). Service workers are decoupled from the DOM and lives on even after the application tabs are closed.

Service Worker acts as a network proxy, allowing us to control how network requests from our application are handled. Also, it works only over HTTPS.

Service Worker acts as a network proxy

First, we have to register the service worker in our application. Add the following piece of code to your application’s main Javascript file.

// in app.js
if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register('/sw.js') //path of service worker file
.then(function () {
console.log('Service worker registered!');
})
.catch(function(err) {
console.log(err);
});
}

Check if ‘serviceWorker’ key exists in the navigator object, to identify unsupported browsers. To make sure if the service worker is registered successfully, check chrome developer console -> Application -> Service Worker. A Service worker has a different lifecycle event that it can listen to:

  • install
  • activate
  • fetch
  • sync
  • notificationclick
  • notificationclose
  • push

These events are attached to the browser window object through Event listeners.

self.addEventListener(<lifecycle event>, function (event) {
//custom code
});

Here, the ‘self’ refers to the window. The next step is to implement caching via the service worker. Some of the cachable items include the asset that doesn’t change that often (like toolbar, main styling, etc). By caching these assets, the application shell will always be visible even if the device loses connectivity.

// in SW.js
self.addEventListener('install', function (event) {
console.log('Installing Service Worker ...', event);
event.waitUntil(
caches.open(<Cache name>) //creates new cache with given name
.then(function (cache) {
console.log('Precaching App Shell');
cache.addAll(<list of file locations>);
})
)
});

The cache is created after the installation of the Service worker ( For cache API related methods refer to https://developer.mozilla.org/en-US/docs/Web/API/Cache )

The next step is to cache Dynamic data. This needs to be done during the ‘fetch’ event of the Service Worker. The cache stores values in ‘key: value’ format where the key is the resource URL and values is the returned response.

// in SW.js
self.addEventListener('fetch', function(event) {
event.respondWith(caches.match(event.request)
.then(function(response) {
if (response) { //if response is received
return response;
} else { // else fetch it from the cache
return fetch(event.request)
.then(function(res) {
return caches.open(<cache name>)
.then(function(cache) {
cache.put(event.request.url, res.clone());
return res;
})
})
}
})
);
});

It is possible that the device loses connectivity right at the time when the user is making a POST request. In this situation, we can temporarily store the POST payload data in IndexedDB and perform background sync.

// in SW.js
self.addEventListener('sync', function(event) {
//read data from IndexedDB
// make a POST request
})

‘Sync’ event is auto-triggered when the connectivity is re-established. For more https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API

‘Push Notification’ is yet another cool feature that we can implement using service workers.

A sample push notification

This feature is enabled by the ‘Notification’ key in windows object. Following is a code sample for requesting permission from the user and displaying the notification:

function askForNotificationPermission() {
Notification.requestPermission(function(result) {
console.log('User Choice', result);
if (result !== 'granted') {
console.log('No notification permission granted!');
} else {
displayConfirmNotification();
}
});
}
function displayConfirmNotification() {
var options = {
body: 'You successfully subscribed to our Notification service!'
};
new Notification('Successfully subscribed!', options);
}

For more on notifications, refer https://developer.mozilla.org/en-US/docs/Web/API/notification

We can also customize the way we can handle the user interaction on these notifications:

self.addEventListener('notificationclick', function(event) {
var notification = event.notification;
var action = event.action;

if (action === 'confirm') {
console.log('Confirm was chosen');
notification.close();
} else {
//custom code or re-routing to specific url
}
});
self.addEventListener('notificationclose', function(event) {
console.log('Notification was closed', event);
});

The web server can also send push messages to the front-end. When a message is received, it’ll result in a ‘push’ event being dispatched in your service worker.

//in a sample nodejs web server
var webpush = require('web-push');
webpush.sendNotification(<pushConfig>, JSON.stringify({
title: 'New Post',
content: 'New Post added!',
openUrl: '/help'
}))
//in front-end app
self.addEventListener('push', function(event) {
console.log('Push Notification received', event);
var data = {title: 'New!',
content: 'Something new happened!',
openUrl: '/'};

if (event.data) {
data = JSON.parse(event.data.text());
}

var options = {
body: data.content,
icon: '/src/images/icons/app-icon-96x96.png',
badge: '/src/images/icons/app-icon-96x96.png',
data: {
url: data.openUrl
}
};

event.waitUntil(
self.registration.showNotification(data.title, options)
);
});

Responsive Design :

A Responsive layout is an essential part of PWA. This is to ensure usability across a wide range of devices. A responsive layout can be achieved using the following ways:

  • Adding CSS Media queries to adjust layout and design. Media queries are used to change the styling based on screen resolution. This ensures proper scaling of DOM elements when viewed on different resolutions.
.sample-style {
margin: 10px auto;
width: 80%;
}
@media (min-width: 600px) { //this styling is applied when screen
.sample-style { //resolution is more than 600px
width: 60%;
}
}
@media (min-width: 1000px) { //this styling is applied when screen
.sample-style { //resolution is more than 1000px
width: 45%;
}
}
  • Making the image responsive. This can be done either using media queries or by having a list of images and picking one based on the screen resolution (using the HTML attribute “srcset”).
<img src="/src/images/main-image.jpg" 
srcset="/src/images/main-image-lg.jpg 1200w,
/src/images/main-image.jpg 900w,
/src/images/main-image-sm.jpg 480w" />
  • Adding CSS animation.

Conclusion :

Concluding, we find that Progressive web applications can revolutionize the way we develop cross-device applications. The above information might help developers make educated choices when it comes to determining whether a PWA is a right approach given their target users’ means of Web access.

Reference :

  1. What is in a Web View? An Analysis of Progressive Web App Features When the Means of Web Access is not a Web Browser — Thomas Steiner, Google Germany GmbH, 20354 Hamburg, Germany (https://storage.googleapis.com/pub-tools-public-publication-data/pdf/94181cd63583b60835d2db9869d9a194bab308ed.pdf)
  2. Shunhao Zhu and Michael Yeung. 2017. PWA and AMP ♥ China (GDD China ’17). https://www.youtube.com/watch?v=JCTjQx56-NY. (2017).

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade