[Solved] PWA force refresh when new version released?



  • @applecider Yes these techniques really don’t have much to do with quasar itself (although there are a few quasar specific settings related to injecting options into workbox, you can just add those yourself).



  • Further update now that that iOS 13 has been released.

    I removed skipWaiting() and clientClaim() from our configuration settings. I can open our PWA and it will show the old version. After closing the app via the app switcher, when the app restarts, it has updated to the new version. iOS might close a suspended PWA after a certain amount of time has passed without any user action, although as with most things PWA related on iOS, information from Apple is scarce.



  • Sorry to resurrect this old thread but I am having the same problems as some users above. I have a PWA with a custom service worker. In App.vue in a preFetch method, I read a file called version.json and compare that with a localstorage “version” property. If the localstorage is lower, I then create a Quasar Dialog that there is a newer version available and then do window.location.reload(true) when the user clicks “ok”. That all works, however it doesn’t fetch the latest app data. Only when I either close the app and reopen it or in a browser I do a ctrl-f5 to refresh.

    When changing to generatesw instead of injectmanifest, thus eliminating my custom service worker (which I want because of various route caching strategies), quasar creates a red bar that a new version is available and when clicking that, it fetches the latest version of the app from the server.

    Does anyone know how generateSW is doing this and how I could duplicate that functionality in my custom service worker?





  • @PiotrG I am pretty sure you are running into the same problem I was running into originally, which is that a simple reload will not work because you can’t let go of the app itself while you are using it without invoking the update method of your registration in navigator.serviceWorker see above for that code, and also remember that you must have skipWaiting and clientsClaim present in your service worker (they can be loaded into the service worker when using GenerateSW if you add workboxOptions: { skipWaiting: true, clientsClaim: true } in your quasar.conf.js file btw)



  • @ssuess Which one are you using right now ? The last one posted here …
    https://forum.quasar-framework.org/topic/2560/solved-pwa-force-refresh-when-new-version-released/31?_=1605731186657

    forceSWupdate () {
      if ('serviceWorker' in navigator) {
        navigator.serviceWorker.getRegistrations().then(function (registrations) {
          for (let registration of registrations) {
            registration.update()
          }
        })
      }
    }
    


  • @turigeza I use the function above when I want to trigger an update from within my code, and within my service worker I have code that checks my local version number, clears localstorage, and then forces a reload. I also have to unfortunately check in SW for what browser we are using, because different browsers behave differently when reloaded. You can see that code and explanation in this thread: https://forum.quasar-framework.org/topic/6959/solved-chrome-but-not-ff-nor-safari-randomly-fails-pwa-update/41?_=1605780104096



  • @ssuess said in [[Solved] PWA force refresh when new version released?](/post

    @turigeza I use the function above when I want to trigger an update from within my code, and within my service worker I have code that checks my local version number, clears localstorage, and then forces a reload.

    Mind sharing the snippet



  • I linked to it above, but here it is again in more or less final form (with domains changed):

    import localforage from 'localforage'
    
    register(process.env.SERVICE_WORKER_FILE, {
      ready () {
        console.log('App is being served from cache by a service worker.')
      },
      registered (registration) { // registration -> a ServiceWorkerRegistration instance
        console.log('Service worker has been registered.')
        // console.log('scope: ' + registration.scope)
      },
      cached (registration) { // registration -> a ServiceWorkerRegistration instance
        console.log('Content has been cached for offline use.')
      },
      updatefound (registration) {
        console.log('New content is available; please refresh.')
        /* eslint-disable no-extra-boolean-cast */
        if (!!window.chrome) { // for chromium based browsers
          fetch('https://pwatest.mydomain.com/av.json?t=' + Date.now())
            .then(response => {
              response.json().then(function (data) {
                const r = confirm('There is a new version (' + data.version + ') available. Your version will be updated, alright? ' + data.message)
                if (r === true) {
                  if (data.clearStorage === true) {
                    localforage.clear().then(function () {
                      // Run this code once the database has been entirely deleted.
                      console.log('Database is now empty. so there now.')
                      location.reload(true)
                    }).catch(function (err) {
                      // This code runs if there were any errors
                      console.log(err)
                    })
                  }
                } else {
                  console.log('You pressed Cancel!')
                }
              })
              console.log('response:', response)
            })
            .catch(error => {
              console.error(error)
            })
          }
        },
        updated (registration) { // registration -> a ServiceWorkerRegistration instance
          console.log('New content is available; please refresh.')
          if (!window.chrome) { // for non chromium browsers
          fetch('https://pwatest.mydomain.com/av.json?t=' + Date.now())
            .then(response => {
              response.json().then(function (data) {
                const r = confirm('There is a new version (' + data.version + ') available. Your version will be updated, alright? ' + data.message)
                if (r === true) {
                  if (data.clearStorage === true) {
                    localforage.clear().then(function () {
                      // Run this code once the database has been entirely deleted.
                      console.log('Database is now empty. so there now.')
                      location.reload(true)
                    }).catch(function (err) {
                      // This code runs if there were any errors
                      console.log(err)
                    })
                  }
                } else {
                  console.log('You pressed Cancel!')
                }
              })
              console.log('response:', response)
            })
            .catch(error => {
              console.error(error)
            })
          }
        },
        offline () {
          console.log('No internet connection found. App is running in offline mode.')
        },
        error (err) {
          console.error('Error during service worker registration:', err)
        }
    })
    


  • @ssuess Thank you for sharing the code and explaining it as well.





  • @danbars Quasar supports loading clientsClaim and skipWaiting directives into the SW file, but for a number of reasons this is not enough. Also, while on supported platforms it will update your PWA, it will do so silently. I needed a way to do all of the following:

    • Control or initiate the update process
    • Set my own versioning
    • Notify users about updates
    • Perform actions related to the update
    • Work around platform and browser limitations and inconsistencies so that it works the same everywhere and without failure

Log in to reply