PWA推送通知在iOS上为啥不生效?

❤素香 阅读 13

我用Push API做了个PWA,安卓上能正常收推送,但iOS Safari完全没反应,是不是iOS根本不支持?

我已经注册了service worker,也调用了Notification.requestPermission(),安卓端一切正常。但在iPhone上,连权限弹窗都不出现,控制台也没报错。

查了下资料说iOS 16.4开始支持Web Push,但我测试的设备是iOS 17,还是不行。是不是还需要额外配置什么?比如apple-app-site-association或者APNs?

现在我的manifest.json里有"display": "standalone",sw.js也注册成功了,但navigator.serviceWorker.ready.then(...)之后调用pushManager.subscribe()直接没动静。

我来解答 赞 3 收藏
二维码
手机扫码查看
1 条解答
W″佳丽
这题我熟,去年在这个坑里躺了三天,血泪教训分享给你。

iOS支持Web Push,但它的限制比安卓严得多,有几个必须满足的条件,少一个都不行。

第一,也是最关键的,iOS必须在"添加到主屏幕"后才能使用推送。直接在Safari浏览器里访问是拿不到推送权限的,Push API在Safari浏览器模式下根本就是残废状态。你必须把PWA添加到主屏幕,从主屏幕图标启动才行。

第二,订阅操作必须由用户手势触发。不能在页面加载时自动调用,必须放在按钮点击事件里。你提到的pushManager.subscribe()没动静,大概率就是这个问题。代码要这样写:

// 必须在用户交互事件中调用
document.getElementById('subscribeBtn').addEventListener('click', async () => {
const permission = await Notification.requestPermission();
if (permission !== 'granted') {
console.log('用户拒绝了权限');
return;
}

const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array('你的VAPID公钥')
});

// 把subscription发给后端保存
});

function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}


第三,manifest.json里display设成standalone是对的,但还需要确保有nameshort_name字段,以及正确的start_url。另外建议加上"prefer_related_applications": false,防止iOS误判。

第四,不需要apple-app-site-association,那是Universal Links的配置,跟Web Push没关系。也不需要单独配置APNs,Web Push走的是苹果的Web Push服务,后端用标准的Web Push协议(VAPID)发推送就行,苹果会自动转成APNs推送到设备。

排查步骤给你列一下:

先确认是从主屏幕图标启动的PWA,不是Safari。然后在控制台输入navigator.serviceWorker.ready.then(r => console.log(r.pushManager)),看看pushManager是否存在。如果存在,把订阅逻辑绑到按钮上,点击后再试。

还有个坑,iOS的权限弹窗只会弹一次,如果用户点了拒绝或者关掉了,以后再调用requestPermission()会直接返回denied,不会弹窗。这种情况得去系统设置里手动改权限,或者删了主屏幕图标重新添加。

最后提醒一句,iOS模拟器不支持Web Push测试,必须用真机。我当初在模拟器上折腾了一整天才发现这个问题。
点赞 1
2026-03-01 02:04