用广播推送通知大规模更新 Live Activity
Broadcast updates to your Live Activities
2024年6月10日
一句话判断
如果你的 App 有大量用户同时关注同一个 Live Activity(比如体育赛事、航班信息),iOS 18 的 Broadcast Push Notifications 能用一条推送同时更新所有设备,再也不用逐个管理 push token 了。
这场 Session 讲了什么
Live Activity 是 iOS 上展示实时信息的利器,外卖配送、比赛比分、航班状态都在用。之前的推送模型是每个用户的每个 Live Activity 都有独立的 push token,服务端要逐一发送推送。当场景变成”一场比赛有上万人同时关注”时,这套机制就力不从心了——你需要存储和管理海量 token,每次更新都重复发送相同的 payload。
iOS 18 引入了 Broadcast Push Notifications。核心概念是 Channel(频道),类似电视广播:一个频道对应一个事件,所有订阅了这个频道的设备会同时收到同一条推送。服务端只需要发一次请求,APNs 负责分发给所有订阅者。
整个流程分三步:创建频道并获取 channel ID;App 端在启动 Live Activity 时订阅该频道;服务端向频道发送广播推送。频道可以在 Push Notifications Console 中手动创建用于测试,生产环境则由服务端直接调用 APNs 的频道管理 API。
值得深挖的点
频道的消息存储策略选择
创建频道时需要选择消息存储策略:No Storage 和 Most Recent Message。前者只在设备在线时投递,离线设备会错过这条推送,但发布预算更高;后者会为每个设备存储最近一条延迟消息,设备上线后能收到最新的那条。
选择的关键在于你的更新频率和场景。如果更新非常频繁(比如体育赛事每分钟都有新动态),选 No Storage 更合理——用户打开 App 时自然会看到最新状态,错过一两条中间更新影响不大。如果是航班延误这种低频但每条都很关键的场景,Most Recent Message 能保证用户不会漏掉最新通知。错误的存储策略选择要么浪费预算,要么导致用户体验断层。
代码片段
通过频道订阅启动 Live Activity
// 从服务端获取当前事件的频道 ID
let channelID = try await fetchChannelID(for: gameID)
// 创建 Activity 的属性和初始值
let attributes = SoccerGameAttributes(gameID: gameID)
let initialState = SoccerGameAttributes.ContentState(
homeScore: 0,
awayScore: 0,
updateText: "比赛即将开始"
)
// 关键:指定 push 类型为 channel,传入频道 ID
let activity = try Activity.request(
attributes: attributes,
content: .init(state: initialState, staleDate: nil),
pushType: .channel(channelID) // 订阅频道广播
)
场景:体育赛事 App,用户点击”关注比赛”后启动 Live Activity。注意 pushType: .channel() 是 iOS 18 新增的参数,替代了之前需要获取每个用户独立 push token 的方式。
坑点:channel ID 是 base64 编码的随机字符串,从服务端获取而不是本地生成。如果频道未创建就传了无效 ID,Activity 仍然会启动但无法接收推送更新。
通过频道发送广播推送
# 使用 APNs HTTP/2 请求发送广播推送
curl -v \
-H "authorization: bearer $JWT_TOKEN" \
-H "apns-push-type: liveactivity" \
-H "apns-priority: 10" \
-H "apns-channel-id: YOUR_CHANNEL_ID" \
-d '{"aps": {"content-state": {"homeScore": 2, "awayScore": 1, "updateText": "进球!"}, "event": "update"}, "timestamp": 1718000000}' \
https://api.push.apple.com:443/3/broadcast/push.apple.com
场景:服务端在比赛有进球时发送一条广播推送,所有订阅了该频道的设备同时更新 Live Activity。
坑点:广播推送的端点与普通推送不同,使用 /3/broadcast/ 路径。timestamp 必须使用 Unix 时间戳,且不能是未来时间。
最佳实践
- 在 Developer Portal 中为 App 开启 Broadcast Capability 后才能使用频道功能
- 开发阶段用 Push Notifications Console 创建频道和发送测试推送,部署后切换到服务端 API
- 频道 ID 应该与业务事件一一绑定(一场比赛一个频道,一个航班一个频道),不要复用频道
- 消息存储策略根据更新频率做选择:高频用 No Storage,低频且关键用 Most Recent Message
还有什么值得关注
- APNs 的频道管理 API 文档详细说明了如何在生产环境创建和删除频道
- Broadcast Push Notifications 的发布有速率限制(budget),No Storage 模式有更高的预算
- 这套机制目前仅支持 Live Activity,不适用于普通远程通知