如何使用pushToStartTokens处理同类型多Live Activity实例?
问题:多同类型Live Activity的推送启动区分处理
我想让用户关注多场足球比赛,需要订阅多个同类型(相同FootballActivityAttributes)的Live Activity,但在监听pushToStartTokenUpdates序列时,无论调用多少次相关函数,始终得到同一个token。如何实现启动多个待启动的Activity并分别用推送触发?本地存储matchId,仅在推送id匹配时处理更新是否是最优方案?还是Apple没考虑这种场景?
补充代码示例(运行后两次打印相同token):
import ActivityKit import SwiftUI struct FootballActivityAttributes: ActivityAttributes { var eventId: String var homeTeam: String var awayTeam: String public struct ContentState: Codable, Hashable { var homeScore: Int var awayScore: Int } } func getPushToStartToken(eventID: String) async throws -> String? { print("Running Live Activity: Fetching pushToStartToken for event \(eventID)...") for await data in Activity<FootballActivityAttributes>.pushToStartTokenUpdates { let token = data.map {String(format: "%02x", $0)}.joined() print("Live Activity: Saved pushToStartToken for event \(eventID): \(token)") return token } print("Live Activity: Found no token") return nil } for eventId in ["12345", "67890"] { Task { try await getPushToStartToken(eventID: eventId) } }
解决方案
核心结论
pushToStartToken是按Activity的Attributes类型生成的,同一类型的所有Live Activity共享同一个启动token,这是Apple的设计逻辑,并非代码问题。你需要通过推送payload携带唯一标识来区分不同的Activity实例。
标准实现方案
本地存储matchId与Activity实例ID的映射是官方预期的解决方案,只要做好映射的生命周期管理,就是高效合规的实现方式,具体步骤如下:
创建待启动的Activity并存储映射
为每场比赛创建待启动的Live Activity实例,将赛事eventId与Activity的id关联存储,方便后续推送时匹配:func createPendingFootballActivity(eventId: String, homeTeam: String, awayTeam: String) async throws -> Activity<FootballActivityAttributes>? { let attributes = FootballActivityAttributes(eventId: eventId, homeTeam: homeTeam, awayTeam: awayTeam) let initialContentState = FootballActivityAttributes.ContentState(homeScore: 0, awayScore: 0) // 请求推送启动授权 let authStatus = await Activity<FootballActivityAttributes>.requestPushToStartAuthorization() guard authStatus == .authorized else { print("推送启动授权失败") return nil } // 创建待启动的Activity实例 let activity = try Activity.request( attributes: attributes, contentState: initialContentState, pushType: .token ) // 存储eventId与Activity ID的映射(用UserDefaults示例,也可选用Keychain) var pendingMatches = UserDefaults.standard.dictionary(forKey: "PendingFootballMatches") as? [String: String] ?? [:] pendingMatches[eventId] = activity.id.uuidString UserDefaults.standard.set(pendingMatches, forKey: "PendingFootballMatches") return activity }处理推送并启动指定Activity
在推送回调中,从payload里取出eventId,匹配对应的Activity ID,再启动该实例:func handleLiveActivityPush(payload: [AnyHashable: Any]) { guard let aps = payload["aps"] as? [String: Any], let eventId = aps["event-id"] as? String, // 推送payload需携带该字段 let pendingMatches = UserDefaults.standard.dictionary(forKey: "PendingFootballMatches") as? [String: String], let activityIdStr = pendingMatches[eventId], let activityId = UUID(uuidString: activityIdStr) else { return } Task { do { // 启动指定的待启动Activity try await Activity<FootballActivityAttributes>.request(options: .pushToStart, id: activityId) // 启动成功后移除映射,避免冗余数据 var updatedMatches = pendingMatches updatedMatches.removeValue(forKey: eventId) UserDefaults.standard.set(updatedMatches, forKey: "PendingFootballMatches") } catch { print("启动Live Activity失败: \(error.localizedDescription)") } } }
额外注意事项
- 推送payload必须包含
event-id(或自定义的唯一标识),且需符合Apple的Live Activity推送格式要求。 - 定期清理过期的映射关系(比如赛事结束后),避免存储冗余数据。
- 若用户删除App或重置存储,需重新创建待启动的Activity并生成映射。
内容的提问来源于stack exchange,提问作者MatiasVian
相关产品推荐
相关产品推荐

