iOS 17中如何通过推送通知仅更新Live Activity的event字段?
解决Live Activity推送仅更新单个字段的问题
问题背景
在iOS 17中使用ActivityKit实现Live Activity时,通过按钮更新可以轻松合并原有状态(比如仅修改counter保留event),但推送通知更新时,系统要求content-state必须包含所有字段,否则解码失败。想要实现推送仅更新event而保留原counter值,常规方法无法直接实现。
可行解决方案:拦截推送手动合并状态
核心思路是绕过系统自动用推送content-state更新Live Activity的逻辑,改为自行拦截推送、合并原有状态后手动调用ActivityKit的更新接口。
步骤1:修改推送Payload结构
不再使用系统默认的content-state字段,而是自定义字段传递需要更新的内容,同时携带目标Live Activity的ID(创建Activity时记录的ID):
{ "aps": { "content-available": 1, "sound": "" }, "activity_id": "目标Activity的ID", "update_event": "新的事件名称" }
步骤2:拦截推送并处理更新
使用Notification Service Extension(推荐,后台处理更稳定)或App后台回调来处理推送:
示例代码(Notification Service Extension)
import UserNotifications import ActivityKit class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent guard let content = bestAttemptContent, let activityID = content.userInfo["activity_id"] as? String, let newEvent = content.userInfo["update_event"] as? String else { contentHandler(content) return } // 后台异步获取目标Activity并更新 Task { guard let targetActivity = await Activity<NotificationLiveActivityAttributes>.activities.first(where: { $0.id == activityID }) else { contentHandler(content) return } // 合并原有状态与新字段 let currentState = targetActivity.content.state let newState = NotificationLiveActivityAttributes.ContentState( event: newEvent, counter: currentState.counter ) // 执行更新 await targetActivity.update(.init(state: newState, staleDate: nil)) } contentHandler(content) } override func serviceExtensionTimeWillExpire() { if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { contentHandler(bestAttemptContent) } } }
步骤3:创建Activity时记录ID
创建Live Activity时,保存其ID用于后续推送匹配:
let attributes = NotificationLiveActivityAttributes(title: "示例标题") let initialState = NotificationLiveActivityAttributes.ContentState(event: "初始事件", counter: 0) if let activity = try? Activity<NotificationLiveActivityAttributes>.request(attributes: attributes, contentState: initialState) { // 将activity.id保存到本地或上传至服务器,推送时携带该ID UserDefaults.standard.set(activity.id, forKey: "CurrentLiveActivityID") }
关键注意事项
- 开启后台推送权限:在Info.plist中添加
UIBackgroundModes,包含remote-notification项。 - 精准匹配Activity:通过
activity.id匹配目标Live Activity,避免误更新其他Activity。 - 后台任务超时处理:Notification Service Extension有时间限制,需确保更新逻辑高效执行。
内容的提问来源于stack exchange,提问作者Ian
相关产品推荐
相关产品推荐

