简体   繁体   中英

PWA keeps showing the old version after a new release on Safari

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

After each release, I got an alert asking me to update to a new version.

However when a new version comes up eventually my app is updated, but it falls back again to the old version after I close and reopens it.

It should be noted that this happens only on some phones.

Please help me to solve it.

My SW:

navigator.serviceWorker
        .getRegistrations()
        .then(function (registrations) {
                return Promise.all(registrations.map(function(r) {return r.unregister()}));
            })
        .then(function() {
                return navigator.serviceWorker.register(serviceFilePath, {
                            scope: _this.options.scope
                }).then(function (registration) {
                    console.info("Service worker has been registered for scope: " + registration.scope);
                    if (needsUpdate) {
                            return registration.update().then(function () {
                            _this.reload();
                            window.parent.postMessage("new-version", "*");
                            console.info("Service worker has been updated.");
                            window.location.reload();
                            return true;
                });
            }
            return true;
        });
});


ServiceWorker.prototype.reload = function () {
            var _this = this;
            return this.getOptions(true).then(function (options) {
                return Promise.all([
                    _this.preload(),
                    // _this.checkPersistence(),
                    _this.checkPushServiceStatus(false)
                ]).then(function () {
                    _this.debug("Clear old caches... (< v" + options.version + ")");
                    var promises = [];
                    for (var i = 0; i < options.version; i++) {
                        promises.push(new AdvancedServiceWorker.BrowserCache(options.storageName, i).remove());
                    }
                    return Promise.all(promises).then(function () {
                        return true;
                    });
                });
            }).catch(function () {
                return false;
            });
        };
        
        
        
BrowserCache.prototype.remove = function (condition) {
            if (!condition) {
                return caches.delete(this.storageName);
            } else if (typeof condition === "string" ||
                condition instanceof String ||
                condition instanceof RegExp) {
                if (!(condition instanceof RegExp)) {
                    condition = AdvancedServiceWorker.Condition.wildCardToRegEx((condition));
                }
                return this.open().then(function (cache) {
                    return cache.keys().then(function (keys) {
                        var promises = [];
                        keys.forEach(function (request) {
                            if (request.url && condition.test(request.url)) {
                                promises.push(cache.delete(request));
                            }
                        });
                        if (!promises.length) {
                            return Promise.resolve(false);
                        }
                        return Promise.all(promises).then(function (results) {
                            for (var i = 0; i < results.length; i++) {
                                if (results[i]) {
                                    return true;
                                }
                            }
                            return false;
                        }, function () {
                            return false;
                        });
                    });
                });
            } else if (condition instanceof Array && condition.length) {
                return this.open().then(function (cache) {
                    var promises = [];
                    for (var i = 0; i < condition.length; i++) {
                        promises.push(cache.delete((condition[i])));
                    }
                    return Promise.all(promises).then(function (results) {
                        for (var j = 0; j < results.length; j++) {
                            if (results[j]) {
                                return true;
                            }
                        }
                        return false;
                    }, function () {
                        return false;
                    });
                });
            } else {
                return Promise.resolve(false);
            }
        };

Ok, my service worker below.

Some things to consider, as this is a SW that I've made for my own needs, as such you might need to tweak for yours.

It's designed to work for a simple SPA, that loads all it's resource's on page load. Generally speaking this is how most SPA's will work. But if you have resources that you say load dynamically via fetch etc, you will want to add those files manually using cache.addAll , some JS frameworks do load routes dynamically, so just be aware.

Because I'm loading the resources automatically and not using addAll in theory you could have an offLine app that's not got all it's resources fully loaded, eg. if you just happen to loose inte.net during update. In practice this is not likely, and if happens a simple refresh would fix, but if you do want to make sure everything is loaded you will have to manually add them using cache.addAll , see comments in file for were to do that.

Enough explaining, here is the code. -> ps, it's also using Typescript here, but you can remove that easily by deleting the declare on the first line. (I just like my types).

declare var self: ServiceWorkerGlobalScope;

//remember to update version number if updating.
const cacheName = "my-spa-v1.0.0";

self.addEventListener("install", (e) => {
  e.waitUntil(
    (async () => {
      const cache = await caches.open(cacheName);
      //if you want to manually add resources
      //await cache.addAll([contentToCache,...]);
      return self.skipWaiting();
    })()
  );
});

//lets keep our cache's clean
self.addEventListener("activate", (e) => {
  e.waitUntil(
    caches.keys().then((keyList) => {
      return Promise.all(
        keyList.map((key) => {
          if (key === cacheName) {
            return;
          }
          return caches.delete(key);
        })
      );
    })
  );
});

//lets auto cache GET requests
self.addEventListener("fetch", (e) => {
  e.respondWith(
    (async () => {
      const r = await caches.match(e.request);
      if (r) {
        return r;
      }
      const response = await fetch(e.request);
      const cache = await caches.open(cacheName);
      if (e.request.method === 'GET')
        cache.put(e.request, response.clone());
      return response;
    })()
  );
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM