You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS 16.4.3 Safari中Push API无法正常工作的问题求助

问题:iOS 16.4.3 Safari PWA中推送通知无法正常工作

我实现了Push和Notification API,在Mac OS的Chrome、Safari,Windows的Chrome等浏览器中均可正常运行,但在iOS 16.4.3的Safari(已添加至主屏作为PWA)中存在问题:点击按钮#enable-notifications后,通知授权弹窗正常弹出,但授权后无任何反应,后端也未收到请求。经测试,navigator.serviceWorker.controller返回null。后续修改JS代码后,出现通知授权弹窗两次,且其他浏览器中服务端也无法收到请求的新问题。

原始应用JS代码

document.getElementById("enable-notifications").addEventListener("click", () => {
    main();
});

const check = () => {
  if (!('serviceWorker' in navigator)) {
    throw new Error('No Service Worker support!')
  }
  if (!('PushManager' in window)) {
    throw new Error('No Push API Support!')
  }
}

const registerServiceWorker = async () => {
    const swRegistration = await navigator.serviceWorker.register('/assets/js/order-dashboard/serviceworker.js');
    return swRegistration;
}

const requestNotificationPermission = async () => {
    Promise.resolve(Notification.requestPermission()).then(function(permission) {
        if (permission !== 'granted') {
          throw new Error('Permission not granted for Notification')
      }
  });
}

const main = async () => {
    check();
    const swRegistration = await registerServiceWorker();
    const permission = await requestNotificationPermission();
}

const showLocalNotification = (title, body, swRegistration) => {
    const options = {
        body,
    };
    swRegistration.showNotification(title, options);
}

serviceworker.js代码

// urlB64ToUint8Array 用于将base64公钥编码为订阅选项所需的ArrayBuffer
const urlB64ToUint8Array = base64String => {
  const padding = '='.repeat((4 - (base64String.length % 4)) % 4)
  const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/')
  const rawData = atob(base64)
  const outputArray = new Uint8Array(rawData.length)
  for (let i = 0; i < rawData.length; ++i) {
    outputArray[i] = rawData.charCodeAt(i)
  }
  return outputArray
}
// saveSubscription 将订阅信息保存到后端
const saveSubscription = async subscription => {

  const SERVER_URL = 'https://good-months-invite-109-132-150-239.loca.lt/save-subscription'
  const response = await fetch(SERVER_URL, {
    method: 'post',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(subscription),
  })

  console.log(response);

  return response.json()
}
self.addEventListener('activate', async () => {
  // 仅在Service Worker激活时调用一次
  try {
    const applicationServerKey = urlB64ToUint8Array(
      'BDLVKNq32B-Dr3HRd4wQ2oNZL9mw5JAGhB1XGCdKlDE9_KDEw7uTOLuPKH-374RRolaa0rr7UyfrJd7tvRvp304'
    )
    const options = { applicationServerKey, userVisibleOnly: true }
    const subscription = await self.registration.pushManager.subscribe(options)
    const response = await saveSubscription(subscription)
    console.log(response)
  } catch (err) {
    console.log('Error', err)
  }
})

self.addEventListener("push", function(event) {
  if (event.data) {
    console.log("Push event!!! ", event.data.text());
    showLocalNotification("Yolo", event.data.text(),  self.registration);
  } else {
    console.log("Push event but no data");
  }
});
const showLocalNotification = (title, body, swRegistration) => {
  const options = {
    body
    // 可在此添加更多属性,如icon、image、vibrate等
  };
  swRegistration.showNotification(title, options);
};

Node.js后端代码

const express = require('express')
const cors = require('cors')
const bodyParser = require('body-parser')
const webpush = require('web-push')
const app = express()
app.use(cors())
app.use(bodyParser.json())
const port = 4000
app.get('/', (req, res) => res.send('Hello World!'))
const dummyDb = { subscription: null } // 内存中的模拟存储,请勿在生产环境使用
const saveToDatabase = async subscription => {
  // 此处应替换为实际的数据库存储逻辑
  dummyDb.subscription = subscription
}
// 保存订阅信息的接口
app.post('/save-subscription', async (req, res) => {
  const subscription = req.body
  await saveToDatabase(subscription)

  console.log("saved");

  res.json({ message: 'success' })
})
const vapidKeys = {
  publicKey:
    'BDLVKNq32B-Dr3HRd4wQ2oNZL9mw5JAGhB1XGCdKlDE9_KDEw7uTOLuPKH-374RRolaa0rr7UyfrJd7tvRvp304',
  privateKey: 'BGbNIt2twl1XsbDHPNe_w6FrKsWcZrys6anByEKyCGo',
}
// 配置VAPID密钥
webpush.setVapidDetails(
  'mailto:myuserid@email.com',
  vapidKeys.publicKey,
  vapidKeys.privateKey
)
// 向订阅设备发送通知的函数
const sendNotification = (subscription, dataToSend) => {
  webpush.sendNotification(subscription, dataToSend)
}
// 测试发送通知的接口
app.get('/send-notification', (req, res) => {
  const subscription = dummyDb.subscription // 从数据库获取订阅信息
  const message = 'Hello World'
  sendNotification(subscription, message)
  res.json({ message: 'message sent' })
})
app.listen(port, () => console.log(`Example app listening on port ${port}!`))

修改后的应用JS代码

document.getElementById("enable-notifications").addEventListener("click", (event) => {
  event.preventDefault()
  askNotificationPermission().then(alert).then(registerServiceWorker);
})


function askNotificationPermission() {
    return new Promise((resolve, reject) => {
      if (checkNotificationPromise()) {
        Notification.requestPermission().then(resolve);

      } else {
        Notification.requestPermission(resolve)
      }
    })
  }


function checkNotificationPromise() {
    try {
      Notification.requestPermission().then();
    } catch(e) {
      return false;
    }

    return true;
  }

const registerServiceWorker = async () => {
//    alert("registering service worker");
    const swRegistration = await navigator.serviceWorker.register('/assets/js/order-dashboard/serviceworker.js');
//    alert(swRegistration.active);
    console.log(swRegistration);
    return swRegistration;
}

内容的提问来源于stack exchange,提问作者O'Niel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 20:39:57