Any way yet to auto-update (or just clear the cache on) a PWA on iOS?

Instead of prompting the user, that a new version is available, you can also extend the 'activate' Eventlistener to delete your old cache whenever you publish a new serviceworker version.

  1. Add version and name variables
var version = "v3" // increase for new version
var staticCacheName = version + "_pwa-static";
var dynamicCacheName = version + "_pwa-dynamic";
  1. Delete caches, when their names do not fit the current version:
self.addEventListener('activate', function(event) {
    event.waitUntil(
        caches.keys().then(function(cacheNames) {
            return Promise.all(
                cacheNames.filter(function(cacheName) {
                    if (!cacheName.startsWith(staticCacheName) &&
                        !cacheName.startsWith(dynamicCacheName)) {
                        return true;
                    }
                }).map(function(cacheName) {
                    console.log('Removing old cache.', cacheName);
                    return caches.delete(cacheName);
                })
            );
        })
    );
});

(credits: https://stackoverflow.com/a/45468998/14678591)

In order to make this work for iOS safari browsers and 'bookmarked' PWAs too, I just added the sligthly reduced function by @jbone107:

self.addEventListener('activate', function(event) {
    event.waitUntil(
        caches.keys().then(function(cacheNames) {
            return Promise.all(
                cacheNames.filter(function(cacheName) {
                    if (!cacheName.startsWith(staticCacheName) &&
                        !cacheName.startsWith(dynamicCacheName)) {
                        return true;
                    }
                }).map(function(cacheName) {
                    // completely deregister for ios to get changes too
                    console.log('deregistering Serviceworker')
                    if ('serviceWorker' in navigator) {
                        navigator.serviceWorker.getRegistrations().then(function(registrations) {
                            registrations.map(r => {
                                r.unregister()
                            })
                        })
                        window.location.reload(true)
                    }

                    console.log('Removing old cache.', cacheName);
                    return caches.delete(cacheName);
                })
            );
        })
    );
});

This way you just have to increase the version number and updating is done by the serviceworker automatically.


After weeks and weeks of searching, I finally found a solution:

  1. I add a check for versionstring on the server, and return it to the app as mentioned above.

  2. I look for it in localtstorage (IndexedDB) and if I don’t find it, I add it. If I do find it, I compare versions and if there is a newer one on the server, I throw up a dialog.

  3. Dismissing this dialog (my button is labeled “update”) runs window.location.reload(true) and then stores the new versionstring in localstorage

Voila! My app is updated! I can't believe it came down to something this simple, I could not find a solution anywhere. Hope this helps someone else!

UPDATE SEPT 2019:

There were a few problems with the technique above, notably that it was bypassing the PWA service worker mechanisms and that sometimes reload would not immediately load new content (because the current SW would not release the page). I have now a new solution to this that seems to work on all platforms:

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

And inside my serviceworker I now throw up the dialog if there is an update, and do my location.reload(true) from there. This always results in my app being refreshed immediately (with the important caveat that I have added skipWaiting and clientsClaim directives to my registration).

This works on every platform the same, and I can programatically check for the update or wait for the service worker to do it by itself (although the times it checks vary greatly by platform, device, and other unknowable factors. Usually not more than 24 hours though.)


If anyone is still having issues with this, registration.update() did not work for me. I used the exact solution but when the version from my server did not match my local stored version, I had to unregister the service workers for it to work.

  if ('serviceWorker' in navigator) {
   await this.setState({ loadingMessage: 'Updating Your Experience' })
   navigator.serviceWorker.getRegistrations().then(function(registrations) {
    registrations.map(r => {
      r.unregister()
    })
   })
   await AsyncStorage.setItem('appVersion', this.state.serverAppVersion)
   window.location.reload(true)
  } 

Then when the app reloads, the service worker is reregistered and the current version of the app is visible on iOS safari browsers and 'bookmarked' PWAs.