close-box Documentation

pwaUpdate(): Application update

One of the advantages of PWAs over native applications is undoubtedly the updating of the application.

On native applications, on Android for example, you have to go to Google Play Store, go to the profile icon, go to "Manage device applications" then finally press "Update".

This process is not ideal in terms of user experience and this action is often delayed.

With PWAs, even if it's not necessarily a good idea, you can update the application transparently and asynchronously.

The problem with PWA updates

The serviceworker is a javascript file that will serve as a single proxy for all instances of the application. If a tab remains open, the update will not occur, even if the browser has detected it.

In addition, refreshing the application tab will not be enough to trigger the update. For some time, the new instance of the application exists simultaneously in memory alongside the previous one.

You can force the update on the serviceworker side, but this can generate user experience issues, as in the case where the update would be carried out while the user enters his credit card in the conversion tunnel of the application.

Solution proposed by PWA Bunga!

In order to overcome these problems, the pwaUpdate function will allow:

Demo

HTML

In order for the function to be functional, these elements must be added to the DOM:

Inset and update button


<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

We hide the elements useful to the function


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

If the element has class is-visible, we display it


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

The function


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

Selection of DOM elements

We set the DOM elements to be manipulated as variables


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')

Service worker

We set the instance of the serviceworker of the application as a variable, which will be used to detect an update and a change of status


const registration = await navigator.serviceWorker.getRegistration()

Detection of an update

We detect if a new serviceworker is available


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

We detect if he is ready to take over


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

If a serviceworker already exists (otherwise it is the first installation of the application), the update box is displayed


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

Update button

On click on the update button


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

We hide the update insert


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

We display the loader which will make it possible to bridge the time between the moment when we send the serviceworker the indication to update and the moment when it is updated.


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

We indicate to the serviceworker that the user has requested the update by sending a message with the API postMessage


registration.waiting.postMessage('SKIP_WAITING')

Running the update

When the serviceworker has received the above message, it updates itself (see the PWA Bunga! PWA service worker part for more details on this treatment). We intercept this moment with the controllerchange event and we refresh all open instances of the application


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

Special case

In the event that for some reason, the user does not update, the update does not take place because other instances of the application are open, the update insert is displayed.


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

Update inset close button

We hide the update insert when the pwaUpdateClose button is clicked


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

Function call

Here is the function call used on pwabunga.js


pwaUpdate()

Complete code of the pwaUpdate function


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')
    })

}

The functions of PWA Bunga!

Service worker registration

Improved user experience

Permissions