研发协作
用 ActivityKit 实现 iOS…
在 iOS 应用中构建、审查或改进锁屏与灵动岛的实时更新 Live Activity(外卖配送、比分、打车、计时器等时效性信息)时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
用 ActivityKit 实现 iOS通用
解决思路
- 确认宿主 App 已开启 Live Activity 能力,并在宿主 App(不是扩展)的 Info.plist 中设置 NSSupportsLiveActivities = YES。
- 定义 ActivityAttributes:静态属性(活动生命周期内不变)与动态 ContentState(每次更新变化);ContentState 要尽量小,因为每次更新和推送都会序列化整个结构体。
- 为 ContentState 编写编解码测试夹具,确保与服务端 payload 契约一致,包括自定义的日期/区间编码。
- 创建 ActivityConfiguration,并预览锁屏与灵动岛各状态,包括 stale 和终止状态。
- 在启动前检查 ActivityAuthorizationInfo.areActivitiesEnabled,再请求并观察活动生命周期。
- 用 Activity.request(attributes:content:pushType:) 启动活动;pushType 传 .token 以启用 APNs 远程更新;ActivityContent 形式需要 iOS 16.2+。
- 在 ActivityContent 上设置 staleDate 告知系统内容何时过期;系统会在过期后把 context.isStale 置为 true,视图中要显示回退 UI(如 “Updating...”)。
- 用 activity.update(content) 做静默更新,或用 activity.update(content, alertConfiguration:) 配合 AlertConfiguration 触发横幅与声音。
- 用 activity.end(finalContent, dismissalPolicy:) 结束活动;dismissalPolicy 可选 .default(系统决定,最多 4 小时)、.immediate(立即移除)、.after(Date)(指定时间后移除,最多 4 小时)。
- 在所有终止路径上都要结束活动:成功、用户/App 取消、登出/会话停止、不可恢复的 App 错误、服务端终止性失败;若服务端表示事件无法继续或无法准确表示,应发送最终终止状态并结束活动,而不是留下过期的进度。
- 锁屏是主要展示面(iOS 16.1+ 所有设备),先设计锁屏布局,再适配灵动岛;锁屏布局垂直空间有限,避免超过约 160 点高度。
- 在 iOS 18+ 上,若提供超出默认的自适应布局,使用 supplementalActivityFamilies:.medium 用于 iOS/macOS Live Activity 尺寸,.small 用于 watchOS Live Activity 尺寸。
- 设计灵动岛三种模式:compact(compactLeading 放图标或小标签,compactTrailing 放一个关键值如计时器/比分/状态)、minimal(多个活动竞争空间时只显示单个图标或字形)、expanded(长按后显示,区域有 .leading、.trailing、.center、.bottom)。
- 用 .keylineTint(.blue) 给灵动岛边框加轻微色调。
- 推送更新:启动时传 .token,然后观察 activity.pushTokenUpdates 异步序列,把每个发出的 token 转发到服务端重新注册(token 会轮换)。
- APNs 推送更新使用 HTTP/2 POST,必需请求头:apns-push-type: liveactivity、apns-topic: <bundle-id>.push-type.liveactivity、apns-priority: 5(低优先级)或 10(立即,消耗预算);aps.alert 控制可见提醒/横幅/声音,优先级本身不会产生提醒。
- 把 timestamp、event 和完整的 content-state 放进 aps 内;对照参考文档中的完整示例校验 update、end 和 push-to-start 请求体,包括精确的 Codable 日期/区间表示。
- Push-to-Start(iOS 17.2+):通过 Activity<Attributes>.pushToStartTokenUpdates 收集 ActivityKit 专用的 push-to-start token(区别于普通 App/设备 APNs token 和每活动更新 token),并注册到服务端;push-to-start payload 必须包含 alert。
- 需要更高推送频率时,在 Info.plist 添加 NSSupportsLiveActivitiesFrequentUpdates = YES;检查 ActivityAuthorizationInfo.frequentPushesEnabled 并观察 frequentPushEnablementUpdates;Apple 不保证固定更新速率。
- iOS 26+ 定时 Live Activity:用 Activity.request(..., start: scheduledDate) 让系统在指定时间自动启动活动(适用于已知开始时间的赛事、航班、定时配送)。
- iOS 18+ 用 style: 参数选择持久性:.standard 用于持久 Live Activity(配送、打车、比分、计时器、航班状态板);.transient 仅用于短暂的展开灵动岛展示(用户锁屏、收起/缩小展开视图、离开 App 或做其他操作时可能自动结束)。
- iOS 18+ 频道推送:在 Xcode 外启用广播能力,在服务端创建 channel,然后用 .channel(channelID) 订阅;频道推送只能更新或结束 Live Activity,不能启动;使用 apns-channel-id 和过期时间,而不是设备 token 的 apns-topic。
- iOS 26+ 配对 Mac 与 CarPlay:Live Activity 可显示在配对 Mac 和 CarPlay 主屏幕上,无需额外 ActivityKit API,但要验证紧凑布局;CarPlay 中 Live Activity 的按钮和开关不会执行操作。
- 避免在 App 侧过于频繁地更新(耗电);服务端驱动的更新用 push-to-update,App 侧更新仅限用户操作。
- 不要在 ActivityAttributes 中存放敏感信息(锁屏可见);敏感数据留在 App 内,只展示可安全显示的摘要。
- 不要在 Live Activity 视图中直接做网络请求或定位;在 App 或服务端预先计算好展示值,再通过 ActivityKit 更新或推送传入。
- 使用 ActivityContent 进行所有生命周期调用,不要用已废弃的基于 contentState 的 API。
- 审查时区分三种时长:活动活跃生命周期(最多 8 小时,除非 App 或用户提前结束)、系统结束后的锁屏存在时间(最多额外 4 小时,总计 12 小时)、App 主动结束用 .default 的残留时间(最多 4 小时)。
- 在真机上测试推送投递和灵动岛行为。
适用边界 · 注意事项
- 不使用 ActivityKit 构建普通主屏幕/时间线小组件(应使用 widgetkit),也不处理通用 APNs 配置(应使用 push-notifications)。
- 没有 Dynamic Island 的设备上不要假设存在灵动岛;灵动岛只是补充展示面,锁屏才是主要展示面。
- 不要用 .transient 样式承载需要持久展示的 Live Activity(如配送、比分、计时器)。