iOS 通知在被抑制时可能会失去推送权限。Discourse 的代码配置为可以选择性地抑制通知,当这种情况发生三次时,它将失去推送权限。
这是推送通知服务工作者的代码。
这段代码在第 178 行有一个严重错误。在那里,服务工作者会检查用户是否处于活动状态(即非空闲状态)。在这种情况下,推送事件会返回 false 而不显示通知。
(它还会检查 payload.hide_when_active,但事实证明 hide_when_active 是始终为 true,因此当用户处于活动状态时,此代码始终返回 false。)
Apple 禁止静默推送,在三次不显示通知的事件后撤销推送权限
这不符合 Apple 关于推送通知的规定。
https://webkit.org/blog/12945/meet-web-push/
功耗和隐私
WebKit 开源项目和 Apple 都将隐私视为一项基本人权。与 Web 平台的其他特权功能一样,请求推送订阅需要明确的用户手势。它还要求您将
userVisibleOnly标志设置为 true,并通过在响应推送消息时始终显示通知来兑现这一承诺。Web 推送 API 不是静默后台运行的邀请,因为这既会违背用户的信任,又会影响用户的电池寿命。
违反
userVisibleOnly承诺将导致推送订阅被撤销。
(我的重点)
这在 Apple 的 WWDC 关于 Web 推送的视频中得到了更详细的解释,在 9:57 处:
https://developer.apple.com/videos/play/wwdc2022/10098/?time=596
请注意,我们明确表示我们承诺始终让推送对用户可见。虽然 JavaScript 推送 API 的标准可以选择性地允许在响应推送时进行静默 JavaScript 后台运行,但大多数浏览器不支持这一点。Safari 不支持这一点。
……然后在 13:35:
https://developer.apple.com/videos/play/wwdc2022/10098/?time=814
正如我在向您展示如何请求推送订阅的代码时提到的,您必须承诺推送将对用户可见。处理推送事件并不是让您的 JavaScript 进行静默后台运行的邀请。这样做既会违背用户的信任,又会影响用户的电池寿命。在处理推送事件时,您实际上必须将通知发布到通知中心。其他浏览器都有针对违反推送对用户可见承诺的对策,Safari 也是如此。在 macOS Ventura 的测试版中,在三次未能及时发布通知的推送事件后,您网站的推送订阅将被撤销。您需要再次经历权限工作流。
(我的重点)
Apple 建议立即显示通知,而不是在关闭通知后显示
Apple 推荐的代码如下,在 11:39 处:
https://developer.apple.com/videos/play/wwdc2022/10098/?time=699
self.addEventListener('push', (event) => {
let pushMessageJSON = event.data.json();
// 我们的服务器将显示通知所需的一切都放在
// 我们的 JSON 数据中。
event.waitUntil(self.registration.showNotification(pushMessageJSON.title, {
body: pushMessageJSON.body,
tag: pushMessageJSON.tag,
actions: [{
action: pushMessageJSON.actionURL,
title: pushMessageJSON.actionTitle,
}]
}));
}
还记得我们订阅推送时,我们的 JavaScript 承诺它们将始终对用户可见吗?这意味着我们必须始终在响应每个推送时显示一个平台原生通知。最好在推送事件处理程序中尽早执行此操作。
Discourse 的代码没有遵循推荐的最佳实践。Discourse 的代码首先关闭所有通知,然后再显示通知。
Discourse 应该始终在响应推送事件时调用 showNotification,并且应该始终尽快执行此操作。