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

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")
}

关键注意事项

  1. 开启后台推送权限:在Info.plist中添加UIBackgroundModes,包含remote-notification项。
  2. 精准匹配Activity:通过activity.id匹配目标Live Activity,避免误更新其他Activity。
  3. 后台任务超时处理:Notification Service Extension有时间限制,需确保更新逻辑高效执行。

内容的提问来源于stack exchange,提问作者Ian

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 20:24:59