Oktober 17, 2019

Und zack...PWA

Da ist es passiert. Mein Phoenix - die Buchungsverwaltung für Kunden von Phoenix Reisen - ist seit heute eine PWA ("Progressive Webapp"). Zum vorzeigen gibt's allerdings nix, da sich alles after login abspielt. ¯\_(ツ)_/¯

Deshalb nachfolgend nur ein abstraktes "was bisher geschah"...

Zunächst hat die App einen Service Worker bekommen. Um mir einiges an pain in the ass zu sparen, basiert dieser direkt auf Workbox. Nicht zuletzt, da Workbox während eines Aufrufs viele nützliche Infos auf der Konsole ausgibt. Die Strategie ist eine Mischung aus allem.

  • Images werden cache first ausgehändigt
  • Code gibt's via stale while revalidate
  • alles andere wird network first aufgelöst

Selbstredend gibt's auch ne valide manifest.json dazu, damit das Ganze standalone laufen kann.

Dann fehlt auch eigentlich schon nicht mehr viel, außer ihn in jedem (autarken) Frontend zu initialisieren. Gar nicht so schwer. Einfach im bootstrap-Teil folgende Funktion aufrufen & fertig ist die Laube.

const initServiceWorker = () => {
    if('serviceWorker' in navigator) {
        window.addEventListener('load', () => {
            try {
                navigator.serviceWorker.register('./service-worker.js');
            } catch(error) {
                Sentry.captureException(error);
            }
        }, false);
        window.addEventListener('online', () => {
            User.online = navigator.onLine;
            m.redraw();
        }, false);
        window.addEventListener('offline', () => {
            User.online = navigator.onLine;
            m.redraw();
        }, false);
        window.addEventListener('beforeinstallprompt', e => {
            Config.appInstallPrompt = e;
            m.redraw();
        }, false);
        window.addEventListener('appinstalled', () => {
            Config.appAlreadyInstalled = true;
            m.redraw();
        }, false);
    }
}

Wie man in der manifest.json sieht, ist die start_url nicht auf das Login-Frontend, sondern auf das Frontend der Übersicht dahinter festgelegt. Deshalb wird man bei einem Besuch der Loginseite nicht gefragt, ob man Mein Phoenix installieren möchte.

Hinter dem Login allerdings, auf der Übersichtsseite, kommt sie dann schließlich aber, die Frage aller Fragen. Einerseits (zum Beispiel) von Android Chrome selbst, andererseits aber auch durch eine selbstgebastelte Installbox, in der ich mir den beforeinstallpromp Event zunutze mache. Der lässt sich nämlich, wie im obigen Code-Beispiel zu sehen, zwischenspeichern und anschließend bei Bedarf - sagen wir bei einem Button Click - wieder aufrufen. Das sieht auf dem Desktop dann ziemlich ungefähr so aus:

Funktioniert auf meinem Pixel 3 recht super, genauso auf meinem Mac. Da die App komplett #JAMstacked ist und die getätigten REST API Calls ebenfalls alle gecacht werden, läuft die App schneller als die Konkurrenz erlaubt.

Natürlich ist das erst der Anfang.

Hiermit sind die Weichen für push notifications und Offline-Fähigkeit gestellt.

Ein bisschen üfflein ist auch schon drinnen: verliert der Gast seine Internetverbindung, erscheint ein dramatisch rotes Hinweis-Overlay, das ihm diesen Umstand mitteilt & die App nur noch eingeschränkt funktioniere - zumindest aber sieht er weiterhin die App und nicht die kalte, unpersönliche Offline-Seite des Browsers. Grundsätzlich kann er sich sogar weiterhin auf allen bisher aufgerufenen (und damit gecachten) Seiten bewegen, solange er nur readonly unterwegs ist. Sonst gibt's bisher nen Fehler. Bisher.