[Solved] PWA force refresh when new version released?



  • As mentioned in other posts, I’ve solved this adding the next code to the register-server-worker.js:

    updated (registration) { // registration -> a ServiceWorkerRegistration instance
      console.log('New content is available; please refresh.')
      Notify.create({
        message: i18n.t('messages.update_available'),
        icon: 'cloud_download',
        closeBtn: i18n.t('labels.update'),
        timeout: 10000,
        onDismiss () {
          location.reload(true)
        }
      })
    }
    

    But as you want to update immediately, you must take control of the service worker lifecycle using skipWaiting().
    Add the next code to your custom-service-worker.js if you are using one like me:

    workbox.core.skipWaiting();
    

    But, if you are using the default configuration, add the next to the quasar.conf.js file that will add for you:

    pwa: {
      workboxOptions: { skipWaiting: true }, // only for NON InjectManifest
    }
    

    If you don’t add this skip, your app will enter in a endless loop asking for update if you try to reload.



  • @Carlitos Wow! Great! Thank you very much! I tried the same in the past and I thought it wasn’t the right way to do so because of the endless loop. I didn’t heard about this skipWaiting property. Now it’s working flawlessly.

    Just one thing, how did you manage to use translations inside register-server-worker.js file? I couldn’t figure out how to import Quasar translations.



  • @kidox You only must export the instance from your i18n boot file and import where you need. This article will help you.



  • @Carlitos solved. Thank you!



  • @Carlitos when I add workboxOptions: { skipWaiting: true } chrome throws an error:

    Uncaught TypeError: workbox.core.skipWaiting is not a function

    Any ideas?



  • Actually I see the problem above, and have fixed it with a custom service worker file: workboxOptions: { skipWaiting: true } unfortunately results in a workbox.core.skipWaiting() being placed in the service worker file. This is incorrect. The correct call should be workbox.skipWaiting(). When I put that in a custom service worker file, it works. I will report this as a bug in Github, but FYI for everyone here.



  • OK, the problem was actually that despite updating quasar to all the latest and greatest with each new update, something was corrupt because it was still embedding the 3.6.3 version of workbox scripts from google instead of the current 4.3.1. Completely creating a new quasar project folder and moving all my files into it seems to have fixed it. Now workboxOptions: { skipWaiting: true } works without an error.



  • I have been playing around with the service worker logic for days now, and I can’t find a way to actually trigger the service worker to check for an update other than attaching a window.location.reload(true) to a button that the user presses. Does anyone have a way other than this to get the service worker to check for changes? On iOS, it seems to do it at quite random times, so I might have an new version on the server but the service worker won’t know or check for many hours. Once it finally does, I can use the update mechanism above, but I would like to be able to force it to check without reloading the window (which when there is an update causes a double reload in my app).





  • Thanks, but the second link you sent is actually something I wrote the answer to myself, and it doesn’t address the service worker problem, it is a bit of a hacky workaround, and the most important problem with it is that it does not always reload all of the contents (because the service worker is holding on to the current context). That is why I am looking for a way to actually trigger the service worker itself to check. I am not sure if the bug is in the register-service-worker npm that quasar uses for this purpose, in quasar code itself, or something in my implementation. This is a punishingly frustrating thing to try to troubleshoot. There should be a way to force the service worker to check for an update, and then perform whatever actions are specified in the register-service-worker.jsfile. Currently the only way I can find to force that is with a window.location.reload(true) as mentioned above, but it suffers from the need to have the user trigger it and the dual reload problem if there is in fact an update.



  • I have a solution finally!

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

    This solves both of my problems above, and I can put it into a method that is either user triggered or programatically. Hope this helps someone else.



  • I’ll add my observations, because I think this will be a recurring issue for lots of people.

    From what I’ve seen, Quasar will automatically check for the existence of a new service worker when it loads. However by default, a PWA won’t start using the new service worker until it is completely shut down and restarted. Simply reloading, or even hard-refreshing the page won’t work (and PWAs in standalone mode don’t always have a refresh button available). This is particularly problematic on iOS at the moment - when you close a PWA, iOS “freezes” the state of the app rather than shutting it down. And there’s no way to easily shut down the app. I’ve found going into the settings for Safari and doing a full cache clear will let a new service worker take over the PWA, and the updated version will appear.

    Enabling the skipWaiting() setting (which can be done in the quasar.conf.js file) will the PWA to start using the new service worker as soon as possible. The observed behaviour flow: app loads up, PWA checks for new service worker, identifies a new one is available and installs it. If the page is reloaded or revisited, the new service worker will take over and the updated version will display. The updated version isn’t shown absolutely immediately, but it is shown pretty fast.

    One potential issue I’ve seen (not 100% confirmed). Using skipWaiting() in conjunction with lazy loading is a known potential risk: a new service worker might try to load content that is in control of the older service worker. We’ve seen new content load up, navigate to a different page, navigate back and the old version is displaying.

    Edit: iOS 13 (coming out in a couple of weeks) brings back the ability to force quit a PWA. That might also influence the decision of whether to use skipWaiting().



  • @ssuess Hello, will this work on normal vue.js app? without using quasar? Thank you in advance. I’m also dealing on how to refresh the page for cache refreshing whenever it detects new changes.



  • @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.


Log in to reply