Meet ActivityKit
System & Services 进阶 20m

认识 ActivityKit

Meet ActivityKit

2023年6月5日

在 Apple 官方观看视频

一句话判断

ActivityKit 是 Live Activities 的核心框架——这场 Session 把 Live Activities 从创建到更新的完整生命周期讲得清清楚楚,包括 iOS 17 新增的 iPad 支持和交互能力。

这场 Session 讲了什么

Session 全面介绍了 ActivityKit 框架和 Live Activities 的完整生命周期。

Live Activities 概览

  • 锁屏上的实时信息卡片,有明确的开始和结束。
  • iPhone 14 Pro 上通过灵动岛展示——compact(单活动)、minimal(双活动)、expanded(长按展开)四种展示形态。
  • iOS 17 新增:StandBy 模式展示、iPad 支持、交互能力(Button 和 Toggle)。

生命周期四步骤

  1. 请求(Request)

    • 只能在前台请求,且需要在用户明确操作后触发(比如”关注比赛”)。
    • 定义 ActivityAttributes 描述静态数据和动态数据(ContentState)。
    • 支持 staleDate(内容过期时间)和 relevanceScore(多活动排序)。
  2. 更新(Update)

    • 通过 Activity.update() 更新动态内容。
    • 可附带 AlertConfiguration——在 iPhone/iPad 上展示更新 UI 和提示音,在 Apple Watch 上展示通知。
    • 支持通过推送通知远程更新(Session 10185 详述)。
  3. 观察(Observe)

    • 通过 activityStateUpdates 异步监听状态变化。
    • 四种状态:started、finished、dismissed、stale。
    • 也可通过 activityState 同步查询。
  4. 结束(End)

    • 创建最终内容状态,设置 dismissalPolicy(afterDate / atEnd / default)。
    • 结束后 Live Activity 在锁屏上保留一段时间后自动消失。

UI 构建

  • 使用 SwiftUI + WidgetKit 声明式布局。
  • 必须支持锁屏展示和灵动岛的所有展示形态。
  • StandBy 模式自动缩放锁屏展示。

值得深挖的点

Live Activities 的”用户调节”机制类似通知——用户可以单独关闭某个 App 的 Live Activities。API 设计上强制要求在用户明确操作后才能请求,不是你想弹就能弹。这是 Apple 防止滥用的设计约束。

AlertConfiguration 的跨设备行为差异是个容易忽视的细节。在 iPhone/iPad 上,alert 表现为 UI 更新 + 提示音(不展示通知横幅)。在配对的 Apple Watch 上,alert 的 title 和 body 作为推送通知展示。这意味着你的 alert 文案要同时适配两种展示场景。

relevanceScore 在多 Live Activities 并存时的作用。比如用户同时在关注两场比赛,relevanceScore 高的那场在灵动岛的展示位置更优。如果你的 App 可能同时有多个 Live Activities,合理设置 relevanceScore 很重要。

代码片段

定义 Activity Attributes 和请求 Live Activity:

import ActivityKit

// 定义静态和动态数据
struct AdventureAttributes: ActivityAttributes {
    let hero: Hero  // 静态数据:冒险的角色

    // 动态数据:随冒险进展变化
    struct ContentState: Codable, Hashable {
        var healthLevel: Double
        var eventDescription: String
    }
}

// 请求 Live Activity(必须在前台)
let attributes = AdventureAttributes(hero: selectedHero)
let initialState = AdventureAttributes.ContentState(
    healthLevel: 1.0,
    eventDescription: "冒险开始"
)

let activity = try Activity.request(
    attributes: attributes,
    content: .init(state: initialState, staleDate: nil),
    pushType: nil  // nil 表示只支持本地更新
)

更新和结束 Live Activity:

// 更新:英雄受到攻击
let updatedState = AdventureAttributes.ContentState(
    healthLevel: 0.3,
    eventDescription: "英雄受到重创,需要药水!"
)

// 创建警报配置
let alert = AlertConfiguration(
    title: "英雄受伤",   // Apple Watch 上作为通知标题
    body: "需要药水恢复",  // Apple Watch 上作为通知正文
    sound: .default
)

// 发送更新
await activity.update(
    .init(state: updatedState, staleDate: nil),
    alertConfiguration: alert  // iPhone 上播放提示音并更新 UI
)

// 结束 Live Activity
let finalState = AdventureAttributes.ContentState(
    healthLevel: 1.0,
    eventDescription: "英雄击败了Boss!"
)
await activity.end(
    .init(state: finalState, staleDate: nil),
    dismissalPolicy: .default  // 在锁屏上短暂保留后消失
)

观察状态变化:

// 异步监听 Live Activity 状态变化
for await state in activity.activityStateUpdates {
    switch state {
    case .started:
        // 活动进行中
        break
    case .dismissed:
        // 用户手动关闭了 Live Activity
        // 清理相关数据
        break
    case .finished:
        // 活动已结束
        break
    case .stale:
        // 内容已过时
        break
    @unknown default:
        break
    }
}

最佳实践

  • 只在用户明确操作后才请求 Live Activity,不要自动弹出。
  • 必须实现所有展示形态(锁屏 + 灵动岛 compact/minimal/expanded),缺少任何一个会导致审核问题。
  • 重要状态变化时附带 AlertConfiguration,确保用户注意到关键更新。
  • 设置合理的 staleDate,让系统知道内容何时过期。过期的 Live Activity 会显示为”已过时”状态。
  • 多个 Live Activities 同时存在时,用 relevanceScore 控制优先级。
  • 结束 Live Activity 时提供有意义的最终状态,让用户看到”完成”而不是”消失”。

还有什么值得关注

  • iOS 17 的交互式 Live Activities 通过 WidgetKit 和 SwiftUI 的 Button/Toggle 实现,详见 Session 10028。
  • 推送通知更新 Live Activities 的方式在 Session 10185 中有完整介绍。
  • iPad 上的 Live Activities 不支持灵动岛,只有锁屏(和 StandBy)展示。
  • StandBy 模式下系统自动缩放锁屏布局填充屏幕,不需要额外适配。
  • Live Activities 最长持续 12 小时,超时后系统自动结束。
WWDC 2023