close-box Documentation

pwaUpdate() : Mise à jour de l'application

Un des avantages des PWA sur les applications natives est sans aucun doute la mise à jour de l'application.

Sur les applications natives, sur Android par exemple, il faut aller dans Google Play Store, aller sur l'icône du profil, aller sur "Gérer les applications de l'appareil" puis enfin appuyer sur "Mettre à jour".

Ce processus n'est pas idéal en terme d'expérience utilisateur et on retarde souvent cette action.

Avec les PWA, même si ça n'est pas forcément une bonne idée, on peut mettre à jour l'application de manière transparente et asynchrone.

Le problème des mises à jour des PWA

Le serviceworker est un fichier javascript qui va servir de proxy unique pour toutes les instances de l'application. Si un onglet reste ouvert, la mise à jour ne se fera pas, même si le navigateur a détecté l'a détecté.

De plus, rafraichir l'onglet de l'application ne suffira pas à enclencher la mise à jour. Pendant un certain temps, la nouvelle instance de l'application existe simultanément dans la mémoire à coté du précédent.

On peut forcer la mise à jour coté serviceworker, mais cela peut générer des soucis d'expérience utilisateur, comme dans le cas où la mise à jour s'effectuerait pendant que l'utilisateur saisi sa carte bancaire dans le tunnel de conversion de l'application.

Solution proposée par PWA Bunga!

Afin de pallier ces problèmes, la fonction pwaUpdate va permettre :

Démo

HTML

Afin que la fonction soit fonctionnelle, il faut ajouter ces éléments au DOM :

Encart et boutton de mise à jour


<div class="pwa-update">
    <button class="pwa-update-close">Close</button>
    {{ content }}
    <button class="pwa-update-btn">Install</button>
</div>

Loader


<div class="pwa-loader">
    {{ loader }}
</div>

CSS

On cache les éléments utiles à la fonction


.pwa-update,
.pwa-loader {
    display: none;
}

Si l'élément a la classe is-visible, on l'affiche


.pwa-update.is-visible,
.pwa-loader.is-visible {
    display: block;
}

La fonction


const pwaUpdate = async () => {
    ...
}

Sélection des éléments du DOM

On met en variable les éléments du DOM à manipuler


const pwaUpdateBar    = document.querySelector('.pwa-update')
const pwaUpdateBtn    = document.querySelector('.pwa-update-btn')
const pwaUpdateClose  = document.querySelector('.pwa-update-close')
const pwaUpdateLoader = document.querySelector('.pwa-loader')

Serviceworker

On met en variable l'instance du serviceworker de l'application, qui va nous servir à détecter une mise à jour et un changement de statut


const registration = await navigator.serviceWorker.getRegistration()

Détection d'une mise à jour

On détecte si un nouveau serviceworker est disponible


registration.addEventListener("updatefound", async () => {
    if (registration.installing) {

On détecte s'il est prêt à prendre le relais


registration.installing.addEventListener('statechange', async () => {
    if (registration.waiting) {

S'il existe déjà un serviceworker (sinon il s'agit de la première installation de l'application), on affiche l'encart de mise à jour


if (navigator.serviceWorker.controller) {
    pwaUpdateBar.classList.add('is-visible')
}

Bouton de mise à jour

Au clic sur le bouton de mise à jour


pwaUpdateBtn.addEventListener('click', async () => {

On cache l'encart de mise à jour


pwaUpdateBar.classList.remove('is-visible')

On affiche le loader qui va permettre de combler le laps de temps entre le moment où on envoi au serviceworker l'indication de se mettre à jour et le moment où il est mis à jour.


pwaUpdateLoader.classList.add('is-visible')

On indique au serviceworker que l'utilisateur a demandé la mise à jour en envoyant un message avec l'API postMessage


registration.waiting.postMessage('SKIP_WAITING')

Exécution de la mise à jour

Quand le serviceworker à reçu le message précèdent, il se met à jour (voir la partie PWA Bunga! PWA service worker pour plus de détails sur ce traitement). On intercepte ce moment avec l'événement controllerchange et nous rafraichissons toutes les instances de l'application ouvertes


navigator.serviceWorker.addEventListener('controllerchange', () => {
    window.location.reload()
})

Cas particulier

Dans le cas où pour une raison diverse, l'utilisateur ne fait pas la mise à jour, que la mise à jour ne se fait pas car d'autres instances de l'application sont ouvertes, on affiche l'encart de mise à jour.


if (registration.waiting) {
    pwaUpdateBar.classList.add('is-visible')
}

Bouton de fermeture de l'encart de mise à jour

On cache l'encart de mise à jour au click du bouton pwaUpdateClose


pwaUpdateClose.addEventListener('click', () => {
    pwaUpdateBar.classList.remove('is-visible')
})

Appel de la fonction

Voici l'appel de la fonction utilisé sur pwabunga.js


pwaUpdate()

Code complet de la fonction pwaUpdate


const pwaUpdate = async () => {

    const pwaUpdateBar    = document.querySelector('.pwa-update')
    const pwaUpdateBtn    = document.querySelector('.pwa-update-btn')
    const pwaUpdateClose  = document.querySelector('.pwa-update-close')
    const pwaUpdateLoader = document.querySelector('.pwa-loader')

    const registration = await navigator.serviceWorker.getRegistration()

    registration.addEventListener("updatefound", async () => {
        if (registration.installing) {
            registration.installing.addEventListener('statechange', async () => {
                if (registration.waiting) {
                    if (navigator.serviceWorker.controller) {
                        pwaUpdateBar.classList.add('is-visible')
                    }
                }
            })
        }
    })

    pwaUpdateBtn.addEventListener('click', async () => {
        pwaUpdateBar.classList.remove('is-visible')
        pwaUpdateLoader.classList.add('is-visible')t
        registration.waiting.postMessage('SKIP_WAITING')
    })

    navigator.serviceWorker.addEventListener('controllerchange', () => {
        window.location.reload()
    })

    if (registration.waiting) {
        pwaUpdateBar.classList.add('is-visible')
    }

    pwaUpdateClose.addEventListener('click', () => {
        pwaUpdateBar.classList.remove('is-visible')
    })

}

Les fonctions de PWA Bunga!

Enregistrement du Service worker

Amélioration de l'expérience utilisateur

Autorisations