Konubinix' opinionated web of thoughts

Progressive Web App

Fleeting

progressive web app

PWABuilder

PWABuilder Suite Documentation

If you skip this step, your app will crash or a browser address bar will appear inside your app

https://docs.pwabuilder.com/

Once your updated assetlinks.json file is deployed to your server, the address bar will disappear from your app

https://docs.pwabuilder.com/

making PWAs work offline with Service workers

Making PWAs work offline with Service workers

Service Workers are a virtual proxy between the browser and the network

https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Offline_Service_workers

don’t have any access to the DOM structure

https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Offline_Service_workers

The “offline first” — or “cache first” — pattern is the most popular strategy for serving content to the user.

https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Offline_Service_workers

offline mode

example of offline service worker

  • External reference: https://googlechrome.github.io/samples/service-worker/basic/

    In the code somewhere

    if ('serviceWorker' in navigator) {
        navigator.serviceWorker
            .register('./sw.js')
            .then(function() {
                console.log('Service Worker Registered');
            });
    }
    

    In sw.js

    // taken from https://googlechrome.github.io/samples/service-worker/basic/
    
    const PRECACHE = 'precache-v1';
    const RUNTIME = 'runtime';
    
    // A list of local resources we always want to be cached.
    const PRECACHE_URLS = [
        'index.html',
        'js/main.js',
        "img/icon.png",
        "img/icon-48x48.png",
        "img/icon-96x96.png",
        "img/icon-144x144.png",
        "img/icon-192x192.png",
        "img/icon-512x512.png",
        "manifest.json",
        "css/framework.min.css",
        "sound/alert.ogg",
        "sound/end.ogg",
        "sound/gong.ogg",
        "sound/gong2.ogg",
    ];
    
    
    self.addEventListener('install', event => {
        event.waitUntil(
            caches.open(PRECACHE)
                .then(cache => cache.addAll(PRECACHE_URLS))
                .then(self.skipWaiting())
        );
    });
    
    // clean up old caches
    self.addEventListener('activate', event => {
        const currentCaches = [PRECACHE, RUNTIME];
        event.waitUntil(
            caches.keys().then(cacheNames => {
                return cacheNames.filter(cacheName => !currentCaches.includes(cacheName));
            }).then(cachesToDelete => {
                return Promise.all(cachesToDelete.map(cacheToDelete => {
                    return caches.delete(cacheToDelete);
                }));
            }).then(() => self.clients.claim())
        );
    });
    
    self.addEventListener('fetch', event => {
        // Skip cross-origin requests, like those for Google Analytics.
        if (event.request.url.startsWith(self.location.origin)) {
            event.respondWith(
                caches.match(event.request).then(cachedResponse => {
                    if (cachedResponse) {
                        return cachedResponse;
                    }
    
                    return caches.open(RUNTIME).then(cache => {
                        return fetch(event.request).then(response => {
                            // Put a copy of the response in the runtime cache.
                            return cache.put(event.request, response.clone()).then(() => {
                                return response;
                            });
                        });
                    });
                })
            );
        }
    });
    

network first

self.addEventListener('fetch', event => {
    if (event.request.url.startsWith(self.location.origin)) {
        event.respondWith(
            fetch(event.request).then(response => {
                // Put a copy of the response in the runtime cache.
                return caches.open(RUNTIME).then(cache => {
                    return cache.put(event.request, response.clone()).then(() => {
                        return response;
                    });
                });
            }).catch(function() {
                return caches.match(event.request)
            })
        );
    }
});

making pwa installable

Making PWAs installable - Progressive web apps | MDN

For a web app to be promoted for installation by a supporting browser, it needs to meet some technical requirements. We can consider these the minimum requirements for a web app to be a PWA

https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Making_PWAs_installable

require that the manifest includes the following members:

name icons start_url display and/or display_override

https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Making_PWAs_installable

must be served in a secure context. This usually means that it must be served over HTTPS. Local resources, such as localhost, 127.0.0.1 and <//> are also considered secure

https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Making_PWAs_installable

must include a service worker with a fetch event handler that provides a basic offline experience.

https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Making_PWAs_installable

When a supporting browser determines that a web app meets the installability criteria described earlier, it will promote the app to the user for installation

https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Making_PWAs_installable

A PWA can provide its own in-page UI for the user to open the install prompt, instead of relying on the UI provided by the browser by default

https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Making_PWAs_installable

technique relies on the beforeinstallprompt event, which is fired on the global Window object as soon as the browser has determined that the PWA is installable

https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Making_PWAs_installable

example of manifest

{
    "name": "my app",
    "short_name": "my app",
    "description": "my app",
    "start_url": "./index.html",
    "display": "fullscreen",
    "background_color": "#ffffff",
    "theme_color": "#4285f4",
    "icons": [
        {
            "src": "./icons/icon-192x192.png",
            "type": "image/png",
            "sizes": "192x192"
        },
        {
            "src": "./icons/icon-512x512.png",
            "type": "image/png",
            "sizes": "512x512"
        }
    ],
    "orientation": "any",
    "prefer_related_applications": false,
    "scope": "./",
    "permissions": [],
    "splash_pages": null,
    "categories": []
}

To add in the head section of the html document

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

Notes linking here