20

I added PWA and service worker to my existing web app based on Angular 5. Everything looks fine on first release. However, when I try to release updates, something strange is happening.

On PC using Chrome, I don't have problem. After each release, I got an alert asking me to update for a new version, which is great.

However, this alert is missing on iOS, which is probably okay because iOS doesn't support auto update yet as I understand it. If I use Chrome on iOS, I can get the new version after manually refresh it (sometimes it takes a few refreshes). However, Safari browser doesn't normally show the new version. If I keeps refreshing the page, the new version comes up eventually, but it falls back again after I close and reopen it. As I play around, the only way I get to the new version is to manually clear the Safari cache first. This is not acceptable to regular user.

I understand iOS has limited support of PWA, but is this the most what we can get on iOS? Without the auto update, how can an iOS user know of the new release and update it?

newman
  • 6,841
  • 21
  • 79
  • 126
  • 1
    I got around this issue by using new file names for each update and un- and re-registering the service worker as a new version is available. Calling the function manually after checking a value from the server. Haven't found another solution to this either, would be glad to have a cleaner solution for safari. – Agash Thamo. Jul 30 '18 at 14:57
  • @AgashThamo. I have this same problem, but am not sure what you mean about un and re-registering the service worker from within the app, would you mind sharing a code example? Thanks! – Stephen Aug 10 '18 at 05:09
  • @Stephen Sorry I don't have access to any concrete code showing this. But what I meant is, that your service worker can check if there is a new file in your ngsw.json and if there are new files but your file is still the old, you can destroy the SW https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/unregister basically deleting all the cache and stuff and register a new service worker. It worked for me, but I thought this problem was solved with the new angular version 6 anyways (with Chrome some had a similar problem). – Agash Thamo. Aug 10 '18 at 07:26
  • @Stephen I just noticed, the unregister() function is not supported in iOS on Safari yet. I'll have to look up how I did it, hope I can find an example in my repo. – Agash Thamo. Aug 10 '18 at 07:27
  • Did you use a `webp` image file? That stopped me from seeing new updates in iOS. – Hyfy May 02 '21 at 10:04

3 Answers3

25

I found and posted the answer here, but to re-iterate:

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.

  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.

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:

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

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

Stephen
  • 8,038
  • 6
  • 41
  • 59
  • Any information about what the actual problem is? Links to resources? – Meekohi Feb 19 '19 at 23:48
  • 4
    The actual problem is that Apple does not fully support the PWA spec. If it did, this would be unnecessary (as it is on Android) – Stephen Mar 20 '19 at 10:31
  • Great work around, and gives the user a heads up about the update. – Wasim Abuzaher Apr 17 '19 at 00:55
  • 1
    Thanks. It works. I provide an open source project using this method for reference: https://github.com/MrMYHuang/cbetar2 This PWA supports both auto update at app startup or manual update! They work on macOS 10.15 + Edge Chrome, Android 9 + Chrome, iOS 13.6 + Safari, Windows 10 + Edge Chrome. Please check the these files, src/serviceWorker.ts, src/Globals.tsx, src/index.tsx, src/App.tsx, and src/pages/SettingsPage.tsx. – Meng-Yuan Huang Aug 19 '20 at 04:56
6

after trying many solutions, calling registration.update didn't make it for me. Only this one is working for me :

const forceReload = () =>
  navigator.serviceWorker
    .getRegistrations()
    .then((registrations) =>
      Promise.all(registrations.map((r) => r.unregister())),
    )
    .then(() => window.location.reload())

The strategy :

  • write a version number in the new deployed app (ie: x.x.x)
  • listen to a version number served on a webSocket
  • call forceReload when they don't match

That way any user connected or not stays up to date with the latest version.

Hope it'll help someone.

bossno
  • 587
  • 6
  • 12
-1

I also had the same issue and I spent days looking at it and reading about how service worker works and registers new changes. You will have to debug your service worker using https://base_web_app_url/ngsw/state . If this returns the normal state, then your service worker is working properly and will load the new changes when they are available. Sometimes, we have to close the app and reopen it so that service worker could release the old page.

If the ngsw/state is not normal then it might complain about the hash mismatch which was the case in my app. There was a post build script that was generating the new hash for styles.css and that is why the hash mismatch error, leading to SW not loading new changes.