Web Programming

Progressive Web Applications (PWAs)

A Critical Introduction

Gerald Senarclens de Grancy

What is a PWA? 🤔

A modern website that mimics the user experience of a native application

Core Technologies

HTML5, CSS, ECMAScript
HTTPS
Web Manifest
installation and metadata
Service Workers
caching and network control

Benefits

  • Little overhead over a conventional responsive website
  • No additional programming language, packaging, or proprietary SDK required
  • Can be installed (almost like a native application)
  • Installed does not require app stores (no approval and fees)
  • Works offline
  • (Limited) access to hardware (geolocation, some sensors etc.)

Limitations

  • Performance
  • Browser web API support is still limited
  • No access to sensitive native APIs (e.g. contacts or proprietary sensors)
  • Browser storage limits can be restrictive for data-heavy applications
  • PWAs are not prominently featured in the main iOS or Android stores
  • Service workers can be less power-efficient than optimized native code

Creating a PWA

Websites can be progressively enhanced into PWAs

Website or web application are made installable by

  • serving the website over TLS
  • adding a manifest

Web applications can work offline by adding service workers

PWA Manifest

A JSON file that contains metadata for your app

The manifest must contain the apps name (or short_name), icons and presentational directives

It can be stored in manifest.json or manifest.webmanifest

The manifest must be linked from your website's head, eg.:

<link rel="manifest" href="/data/manifest.json">

Example: manifest.json

{
  "name": "My First PWA",
  "start_url": "/",
  "icons": [
    { "src": "../images/logo_512.png", "sizes": "512x512" },
    { "src": "../images/logo_192.png",
      "sizes": "192x192",
      "purpose": "maskable"
    }
  ],
  "display": "standalone",
  "background_color": "rgba(49, 54, 57, 1)",
  "lang": "en"
}
start_url
The start page when a user launches the PWA
display
the app's display mode (including fullscreen or standalone as well as minimal-ui
orientation
portrait or landscape; can be omitted if the app works well in both

Web app manifest members reference

Debugging the Manifest

Use your browser's developer tools: Application -> Manifest

Note that PWAs can only be installed using a manifest file via HTTPS

Alternatively, sites can be installed using HTTP from 127.0.0.1 and localhost

Powerful Web APIs are available in these secure contexts

PWA Service Workers

Service workers progessively enhance the PWA to work offline

This is done by caching selected resources using the service worker

To do this, the service worker has to

  • Administer the version of the cache
  • Maintain a list of resources to be cached
  • Handle three events (install, activate and fetch)

Cache Versioning

To recognize new or update resources, the PWA has to know about the changes

const VERSION = '0.6.0-1';
const CACHE_NAME = `study.find-santa.eu-${VERSION}`;

The VERSION constant must be updated when a resource was changed

Cached Resources

The service worker has to maintain a list of cached (static) resources

const APP_STATIC_RESOURCES = [
  '/',
  '/index.html',
  '/images/coat_of_arms_512.png',
  '/images/favicon.png',
  '/pos/',
  '/pos/index.html',
  '/pos/1/',
  '/pos/1/index.html',
  // ...
  '/wmc/1/index.html',
];

install Event Handler

self.addEventListener('install', (event) => {
  event.waitUntil(
    (async () => {
      const cache = await caches.open(CACHE_NAME);
      try {
        cache.addAll(APP_STATIC_RESOURCES);  // atomic operation
      } catch (error) {  // falling back to individual caching
        const promises = APP_STATIC_RESOURCES.map(async (resource) => {
          try {
            await cache.add(resource);
          } catch (err) {
            console.warn(`Failed to cache: ${resource}`, err.message);
          }
        });
        await Promise.all(promises);
      }
    })(),
  );
});

activate Event Handler

self.addEventListener('activate', (event) => {
  event.waitUntil(
    (async () => {
      const names = await caches.keys();
      await Promise.all(
        names.map((name) => {
          if (name !== CACHE_NAME) {
            return caches.delete(name);
          }
          return undefined;
        }),
      );
      await clients.claim();
    })(),
  );
});

fetch Event Handler

Think of the service worker acting as a proxy server

self.addEventListener('fetch', (event) => {
  event.respondWith(
    (async () => {
      const cache = await caches.open(CACHE_NAME);
      const cachedResponse = await cache.match(event.request);
      if (cachedResponse) {
        return cachedResponse;
      }
      // try to load missing resource and deal with errors if that fails
    })(),
  );
});

Progressively Enhance PWA With Service Worker

Place the following code in a script tag of your entry page

if ("serviceWorker" in navigator) {
  navigator.serviceWorker.register("/scripts/sw.js").then(
    (registration) => {
      console.log("Service worker registration successful:", registration);
    },
    (error) => {
      console.error(`Service worker registration failed: ${error}`);
    },
  );
} else {
  console.error("Service workers are not supported.");
}

Debugging Serive Workers

Use your browser's developer tools: Application -> Service Workers

Remote debug your PWA

  • Enable developer settings in Android (click 7x on build number)
  • Enable USB debugging and connect your phone to your dev machine
  • Navigate to chrome://inspect/#devices

When the service worker fails, first "unregister" it

While on the "Network" tab, do a hard reload (usually Ctrl + Shift + R)

Complete Example

study.find-santa.eu was turned into a PWA

It only serves the purpose of illustrating PWAs with 11ty

For this POC no plugin automating any of the steps is used

All relevant resources are available for download

Conclusion

PWAs are a powerful but not a universal solution

PWAs excel
Content-focused apps and small utilities
Anything without deep system integration
Native apps excel (for now)
High-performance gaming and complex image/video processing
Applications that require access to native SDKs (e.g. banking features)

Outlook

PWAs are getting increasingly powerful over time

  • They get more and more APIs
  • Desktop support is already usable in production (eg on KDE Plasma)
  • Browser and platform support is also improving as we speak

Questions
and feedback...

Further Reading

MDN Community Progressive web apps https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps
MDN Community CycleTracker PWA tutorial https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Tutorials/CycleTracker
chromeOS List your Progressive Web App in Google Play https://chromeos.dev/en/publish/pwa-in-play