iOS 上的 PWA 是否有自动更新(或只是清除缓存)的方法?
Any way yet to auto-update (or just clear the cache on) a PWA on iOS?
我一直在 iOS 上苦苦挣扎,但在 Android 上可以轻松工作:让我的 PWA 在有新版本时自动更新。我完全不确定这在 iOS 上是否可行。我已经使用 vue.js 和 Quasar 构建了我的应用程序,并且在 Android 上一切都开箱即用。这是 iOS 版本目前的(丑陋、可怕的)方式:
- 我可以检查我自己的服务器的版本并将其与存储在我的应用程序(在 indexedDB 中)中的当前版本进行比较,并发出有新版本的通知。到目前为止一切顺利。
- 除了让用户手动清除 SAFARI 缓存 (!!) 之外,我无法弄清楚如何以编程方式从应用程序中清除 PWA 缓存或以其他方式强制上传。
所以此时我想我的问题是:
- 有没有人能够在 iOS(11.3 或更高版本)上获得 PWA 以在新版本可用时自动更新?
- 有没有办法从我的 PWA 中清除 (safari) 应用缓存?
显然,通知用户为了更新他们必须在应用程序之外执行几个步骤才能刷新应用程序,这是一种非常糟糕的用户体验,但似乎这就是 iOS 的立场目前,除非我遗漏了什么。有人做过这个吗?
经过数周又数周的搜索,我终于找到了解决方案:
我在服务器上添加了对版本字符串的检查,然后 return 如上所述将其添加到应用程序。
我在localtstorage(IndexedDB)里找,没找到就加。如果找到它,我会比较版本,如果服务器上有更新的版本,我会弹出一个对话框。
关闭此对话框(我的按钮标记为“更新”)运行 window.location.reload(true)
,然后将新版本字符串存储在本地存储
瞧!我的应用程序已更新!我简直不敢相信它归结为如此简单的事情,我无法在任何地方找到解决方案。希望这对其他人有帮助!
2019 年 9 月更新:
上述技术存在一些问题,特别是它绕过了 PWA service worker 机制,并且有时重新加载不会立即加载新内容(因为当前的 SW 不会释放页面)。我现在有了一个似乎适用于所有平台的新解决方案:
forceSWupdate () {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(function (registrations) {
for (let registration of registrations) {
registration.update()
}
})
}
}
在我的 serviceworker 中,如果有更新,我现在会抛出对话框,然后从那里执行我的 location.reload(true)
。这总是会导致我的应用程序立即刷新(重要的警告是我在注册中添加了 skipWaiting
和 clientsClaim
指令)。
这在每个平台上都是一样的,我可以通过编程检查更新或等待 service worker 自己完成(尽管它检查的时间因平台、设备和其他不可知因素而有很大差异。不过通常不会超过 24 小时。)
如果有人对此仍有疑问,registration.update() 对我不起作用。我使用了确切的解决方案,但是当我的服务器的版本与我本地存储的版本不匹配时,我不得不注销服务工作者才能工作。
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)
}
然后,当应用程序重新加载时,服务工作者会重新注册,应用程序的当前版本在 iOS Safari 浏览器和 'bookmarked' PWA 上可见。
除了提示用户有新版本可用之外,您还可以扩展 'activate' 事件监听器,以便在您发布新的 serviceworker 版本时删除旧缓存。
- 添加版本和名称变量
var version = "v3" // increase for new version
var staticCacheName = version + "_pwa-static";
var dynamicCacheName = version + "_pwa-dynamic";
- 删除缓存,当它们的名称不适合当前版本时:
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);
})
);
})
);
});
(学分:)
为了使 iOS safari 浏览器和 'bookmarked' PWA 也能正常工作,我刚刚添加了 @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);
})
);
})
);
});
这样你只需要增加版本号,更新由 serviceworker 自动完成。
我一直在 iOS 上苦苦挣扎,但在 Android 上可以轻松工作:让我的 PWA 在有新版本时自动更新。我完全不确定这在 iOS 上是否可行。我已经使用 vue.js 和 Quasar 构建了我的应用程序,并且在 Android 上一切都开箱即用。这是 iOS 版本目前的(丑陋、可怕的)方式:
- 我可以检查我自己的服务器的版本并将其与存储在我的应用程序(在 indexedDB 中)中的当前版本进行比较,并发出有新版本的通知。到目前为止一切顺利。
- 除了让用户手动清除 SAFARI 缓存 (!!) 之外,我无法弄清楚如何以编程方式从应用程序中清除 PWA 缓存或以其他方式强制上传。
所以此时我想我的问题是:
- 有没有人能够在 iOS(11.3 或更高版本)上获得 PWA 以在新版本可用时自动更新?
- 有没有办法从我的 PWA 中清除 (safari) 应用缓存?
显然,通知用户为了更新他们必须在应用程序之外执行几个步骤才能刷新应用程序,这是一种非常糟糕的用户体验,但似乎这就是 iOS 的立场目前,除非我遗漏了什么。有人做过这个吗?
经过数周又数周的搜索,我终于找到了解决方案:
我在服务器上添加了对版本字符串的检查,然后 return 如上所述将其添加到应用程序。
我在localtstorage(IndexedDB)里找,没找到就加。如果找到它,我会比较版本,如果服务器上有更新的版本,我会弹出一个对话框。
关闭此对话框(我的按钮标记为“更新”)运行
window.location.reload(true)
,然后将新版本字符串存储在本地存储
瞧!我的应用程序已更新!我简直不敢相信它归结为如此简单的事情,我无法在任何地方找到解决方案。希望这对其他人有帮助!
2019 年 9 月更新:
上述技术存在一些问题,特别是它绕过了 PWA service worker 机制,并且有时重新加载不会立即加载新内容(因为当前的 SW 不会释放页面)。我现在有了一个似乎适用于所有平台的新解决方案:
forceSWupdate () {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(function (registrations) {
for (let registration of registrations) {
registration.update()
}
})
}
}
在我的 serviceworker 中,如果有更新,我现在会抛出对话框,然后从那里执行我的 location.reload(true)
。这总是会导致我的应用程序立即刷新(重要的警告是我在注册中添加了 skipWaiting
和 clientsClaim
指令)。
这在每个平台上都是一样的,我可以通过编程检查更新或等待 service worker 自己完成(尽管它检查的时间因平台、设备和其他不可知因素而有很大差异。不过通常不会超过 24 小时。)
如果有人对此仍有疑问,registration.update() 对我不起作用。我使用了确切的解决方案,但是当我的服务器的版本与我本地存储的版本不匹配时,我不得不注销服务工作者才能工作。
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)
}
然后,当应用程序重新加载时,服务工作者会重新注册,应用程序的当前版本在 iOS Safari 浏览器和 'bookmarked' PWA 上可见。
除了提示用户有新版本可用之外,您还可以扩展 'activate' 事件监听器,以便在您发布新的 serviceworker 版本时删除旧缓存。
- 添加版本和名称变量
var version = "v3" // increase for new version
var staticCacheName = version + "_pwa-static";
var dynamicCacheName = version + "_pwa-dynamic";
- 删除缓存,当它们的名称不适合当前版本时:
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);
})
);
})
);
});
(学分:
为了使 iOS safari 浏览器和 'bookmarked' PWA 也能正常工作,我刚刚添加了 @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);
})
);
})
);
});
这样你只需要增加版本号,更新由 serviceworker 自动完成。