live-activities by dpearson2699/swift-ios-skills
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill live-activities使用 ActivityKit 在锁定屏幕、灵动岛、待机模式、CarPlay 和 Mac 菜单栏上构建实时、一目了然的体验。模式针对 iOS 26+ 和 Swift 6.2,除非特别说明,否则向后兼容至 iOS 16.1。
有关完整代码模式,包括推送负载格式、并发活动、状态观察和测试,请参阅 references/live-activity-patterns.md。
NSSupportsLiveActivities = YES。ContentState 的 ActivityAttributes 结构体。ActivityConfiguration。Activity.request(attributes:content:pushType:) 启动活动。广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
activity.update(_:) 进行更新,使用 activity.end(_:dismissalPolicy:) 结束活动。运行本文档末尾的审查清单。
定义静态数据(在活动生命周期内不可变)和动态的 ContentState(随每次更新而改变)。保持 ContentState 小巧,因为整个结构体在每次更新和推送负载时都会被序列化。
import ActivityKit
struct DeliveryAttributes: ActivityAttributes {
// 静态 -- 在活动创建时设置一次,永不更改
var orderNumber: Int
var restaurantName: String
// 动态 -- 在活动生命周期内更新
struct ContentState: Codable, Hashable {
var driverName: String
var estimatedDeliveryTime: ClosedRange<Date>
var currentStep: DeliveryStep
}
}
enum DeliveryStep: String, Codable, Hashable, CaseIterable {
case confirmed, preparing, pickedUp, delivering, delivered
var icon: String {
switch self {
case .confirmed: "checkmark.circle"
case .preparing: "frying.pan"
case .pickedUp: "bag.fill"
case .delivering: "box.truck.fill"
case .delivered: "house.fill"
}
}
}
在 ActivityContent 上设置 staleDate,以告知系统内容何时过时。系统在此日期之后会将 context.isStale 设置为 true;在您的视图中显示备用 UI(例如,“正在更新...”)。
let content = ActivityContent(
state: state,
staleDate: Date().addingTimeInterval(300), // 5 分钟后过期
relevanceScore: 75
)
使用 Activity.request 创建并显示实时活动。将 .token 作为 pushType 传递,以启用通过 APNs 的远程更新。
let attributes = DeliveryAttributes(orderNumber: 42, restaurantName: "Pizza Place")
let state = DeliveryAttributes.ContentState(
driverName: "Alex",
estimatedDeliveryTime: Date()...Date().addingTimeInterval(1800),
currentStep: .preparing
)
let content = ActivityContent(state: state, staleDate: nil, relevanceScore: 75)
do {
let activity = try Activity.request(
attributes: attributes,
content: content,
pushType: .token
)
print("Started activity: \(activity.id)")
} catch {
print("Failed to start activity: \(error)")
}
从应用更新动态内容状态。使用 AlertConfiguration 在更新时触发可见的横幅和声音。
let updatedState = DeliveryAttributes.ContentState(
driverName: "Alex",
estimatedDeliveryTime: Date()...Date().addingTimeInterval(600),
currentStep: .delivering
)
let updatedContent = ActivityContent(
state: updatedState,
staleDate: Date().addingTimeInterval(300),
relevanceScore: 90
)
// 静默更新
await activity.update(updatedContent)
// 附带提醒的更新
await activity.update(updatedContent, alertConfiguration: AlertConfiguration(
title: "订单更新",
body: "您的配送员就在附近!",
sound: .default
))
当跟踪的事件完成时结束活动。选择一个解除策略来控制已结束的活动在锁定屏幕上停留多长时间。
let finalState = DeliveryAttributes.ContentState(
driverName: "Alex",
estimatedDeliveryTime: Date()...Date(),
currentStep: .delivered
)
let finalContent = ActivityContent(state: finalState, staleDate: nil, relevanceScore: 0)
// 系统决定何时移除(最长 4 小时)
await activity.end(finalContent, dismissalPolicy: .default)
// 立即移除
await activity.end(finalContent, dismissalPolicy: .immediate)
// 在特定时间后移除(从现在起最长 4 小时)
await activity.end(finalContent, dismissalPolicy: .after(Date().addingTimeInterval(3600)))
始终在所有代码路径(成功、错误和取消)上结束活动。泄漏的活动会一直停留在锁定屏幕上,直到系统将其终止(最长 8 小时),这会使用户感到沮丧。
锁定屏幕是实时活动的主要展示界面。所有运行 iOS 16.1+ 的设备都会在此处显示实时活动。首先设计此布局。
struct DeliveryActivityWidget: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: DeliveryAttributes.self) { context in
// 锁定屏幕 / 待机模式 / CarPlay / Mac 菜单栏内容
VStack(alignment: .leading, spacing: 8) {
HStack {
Text(context.attributes.restaurantName)
.font(.headline)
Spacer()
Text("订单 #\(context.attributes.orderNumber)")
.font(.caption)
.foregroundStyle(.secondary)
}
if context.isStale {
Label("正在更新...", systemImage: "arrow.trianglehead.2.clockwise")
.font(.subheadline)
.foregroundStyle(.secondary)
} else {
HStack {
Label(context.state.driverName, systemImage: "person.fill")
Spacer()
Text(timerInterval: context.state.estimatedDeliveryTime,
countsDown: true)
.monospacedDigit()
}
.font(.subheadline)
// 进度步骤
HStack(spacing: 12) {
ForEach(DeliveryStep.allCases, id: \.self) { step in
Image(systemName: step.icon)
.foregroundStyle(
step <= context.state.currentStep ? .primary : .tertiary
)
}
}
}
}
.padding()
} dynamicIsland: { context in
// 灵动岛闭包(见下一节)
DynamicIsland {
// 展开区域...
DynamicIslandExpandedRegion(.leading) {
Image(systemName: "box.truck.fill").font(.title2)
}
DynamicIslandExpandedRegion(.trailing) {
Text(timerInterval: context.state.estimatedDeliveryTime,
countsDown: true)
.font(.caption).monospacedDigit()
}
DynamicIslandExpandedRegion(.center) {
Text(context.attributes.restaurantName).font(.headline)
}
DynamicIslandExpandedRegion(.bottom) {
HStack(spacing: 12) {
ForEach(DeliveryStep.allCases, id: \.self) { step in
Image(systemName: step.icon)
.foregroundStyle(
step <= context.state.currentStep ? .primary : .tertiary
)
}
}
}
} compactLeading: {
Image(systemName: "box.truck.fill")
} compactTrailing: {
Text(timerInterval: context.state.estimatedDeliveryTime,
countsDown: true)
.frame(width: 40).monospacedDigit()
} minimal: {
Image(systemName: "box.truck.fill")
}
}
}
}
锁定屏幕展示的垂直空间有限。避免布局高度超过大约 160 点。使用 supplementalActivityFamilies 来选择 .small(紧凑型)或 .medium(标准型)尺寸:
ActivityConfiguration(for: DeliveryAttributes.self) { context in
// 锁定屏幕内容
} dynamicIsland: { context in
// 灵动岛
}
.supplementalActivityFamilies([.small, .medium])
灵动岛在 iPhone 14 Pro 及更高版本上可用。它有三种展示模式。设计所有三种模式,但将锁定屏幕视为主要展示界面,因为并非所有设备都有灵动岛。
当单个实时活动处于活动状态时始终可见。空间极其有限——仅显示最关键的信息。
| 区域 | 用途 |
|---|---|
compactLeading | 标识活动的图标或小标签 |
compactTrailing | 一个关键值(计时器、分数、状态) |
当多个实时活动竞争空间时显示。只有一个活动能获得最小型位置。显示单个图标或字形。
当用户长按灵动岛时显示。
| 区域 | 位置 |
|---|---|
.leading | TrueDepth 摄像头左侧;可换行到下方 |
.trailing | TrueDepth 摄像头右侧;可换行到下方 |
.center | 摄像头正下方 |
.bottom | 所有其他区域下方 |
对灵动岛边框应用微妙的色调:
DynamicIsland { /* expanded */ }
compactLeading: { /* ... */ }
compactTrailing: { /* ... */ }
minimal: { /* ... */ }
.keylineTint(.blue)
推送更新通过 APNs 发送实时活动更新,这比从应用轮询更高效,并且在应用挂起时也能工作。
启动活动时将 .token 作为 pushType 传递,然后将推送令牌转发到您的服务器:
let activity = try Activity.request(
attributes: attributes,
content: content,
pushType: .token
)
// 观察令牌变化 -- 令牌可能会轮换
Task {
for await token in activity.pushTokenUpdates {
let tokenString = token.map { String(format: "%02x", $0) }.joined()
try await ServerAPI.shared.registerActivityToken(
tokenString, activityID: activity.id
)
}
}
使用以下标头和 JSON 正文向 APNs 发送 HTTP/2 POST 请求:
必需的 HTTP 标头:
apns-push-type: liveactivityapns-topic: <bundle-id>.push-type.liveactivityapns-priority: 5(低)或 10(高,触发提醒)更新负载:
{
"aps": {
"timestamp": 1700000000,
"event": "update",
"content-state": {
"driverName": "Alex",
"estimatedDeliveryTime": {
"lowerBound": 1700000000,
"upperBound": 1700001800
},
"currentStep": "delivering"
},
"stale-date": 1700000300,
"alert": {
"title": "配送更新",
"body": "您的配送员就在附近!"
}
}
}
结束负载: 结构相同,使用 "event": "end" 和可选的 "dismissal-date"。
content-state JSON 必须与 ContentState Codable 结构体完全匹配。不匹配的键或类型会导致静默失败。
无需应用运行即可远程启动实时活动(iOS 17.2+):
Task {
for await token in Activity<DeliveryAttributes>.pushToStartTokenUpdates {
let tokenString = token.map { String(format: "%02x", $0) }.joined()
try await ServerAPI.shared.registerPushToStartToken(tokenString)
}
}
在 Info.plist 中添加 NSSupportsLiveActivitiesFrequentUpdates = YES 以增加推送更新预算。用于每分钟更新超过一次的活动(体育比分、乘车跟踪)。
计划在未来某个时间启动实时活动。系统会自动启动活动,无需应用在前台运行。用于已知开始时间的事件(体育比赛、航班、计划配送)。
let scheduledDate = Calendar.current.date(
from: DateComponents(year: 2026, month: 3, day: 15, hour: 19, minute: 0)
)!
let activity = try Activity.request(
attributes: attributes,
content: content,
pushType: .token,
start: scheduledDate
)
style: 参数 iOS 26+)控制持久性:.standard(持续存在直到结束,默认值)或 .transient(系统可能自动解除)。使用 .transient 用于短暂更新,如交通到达时间。Activity.request 上的 style: 参数需要 iOS 26+。
let activity = try Activity.request(
attributes: attributes, content: content,
pushType: .token, style: .transient
)
实时活动会自动出现在 macOS Tahoe 菜单栏(通过 iPhone 镜像)和 CarPlay 主屏幕上。无需额外代码——确保锁定屏幕布局在较小尺寸下清晰可读。
使用 .channel 向多个实时活动广播更新:
let activity = try Activity.request(
attributes: attributes, content: content,
pushType: .channel("delivery-updates")
)
不要: 在紧凑型展示中放入过多内容——空间非常小。要: 在紧凑型左侧/右侧仅显示最关键的信息(图标 + 一个值)。
不要: 从应用过于频繁地更新实时活动(耗电)。要: 使用推送更新进行服务器驱动的更新。将应用端更新限制在用户操作上。
不要: 忘记在事件完成时结束活动。要: 始终在成功、错误和取消路径上结束活动。泄漏的活动会让用户感到沮丧。
不要: 假设灵动岛可用(仅限 iPhone 14 Pro+)。要: 将锁定屏幕作为主要展示界面进行设计;灵动岛是补充性的。
不要: 在 ActivityAttributes 中存储敏感信息(在锁定屏幕上可见)。要: 将敏感数据保留在应用中,仅显示可安全显示的摘要。
不要: 忘记处理过期日期。要: 在视图中检查 context.isStale 并显示备用 UI(“正在更新...”或类似内容)。
不要: 忽略推送令牌轮换。令牌可能随时更改。要: 使用 activity.pushTokenUpdates 异步序列,并在每次发出时重新注册。
不要: 忘记 NSSupportsLiveActivities Info.plist 键。要: 在宿主应用的 Info.plist 中添加 NSSupportsLiveActivities = YES(不是扩展)。
不要: 使用已弃用的基于 contentState 的 API 进行请求/更新/结束。要: 对所有生命周期调用使用 ActivityContent。
不要: 在实时活动视图中放置繁重的逻辑。它们在一个尺寸受限的小部件进程中渲染。要: 预先计算显示值并通过 ContentState 传递它们。
ActivityAttributes 定义了静态属性和 ContentStateNSSupportsLiveActivities = YESActivityContent(而非已弃用的 contentState API)context.isStaleactivity.pushTokenUpdates 将推送令牌转发到服务器AlertConfigurationActivityAuthorizationInforeferences/live-activity-patterns.md每周安装数
386
仓库
GitHub 星标数
269
首次出现
2026年3月3日
安全审计
安装于
codex383
gemini-cli380
amp380
cline380
github-copilot380
kimi-cli380
Build real-time, glanceable experiences on the Lock Screen, Dynamic Island, StandBy, CarPlay, and Mac menu bar using ActivityKit. Patterns target iOS 26+ with Swift 6.2, backward-compatible to iOS 16.1 unless noted.
See references/live-activity-patterns.md for complete code patterns including push payload formats, concurrent activities, state observation, and testing.
NSSupportsLiveActivities = YES to the host app's Info.plist.ActivityAttributes struct with a nested ContentState.ActivityConfiguration in the widget bundle with Lock Screen content and Dynamic Island closures.Activity.request(attributes:content:pushType:).activity.update(_:) and end with activity.end(_:dismissalPolicy:).Run through the Review Checklist at the end of this document.
Define both static data (immutable for the activity lifetime) and dynamic ContentState (changes with each update). Keep ContentState small because the entire struct is serialized on every update and push payload.
import ActivityKit
struct DeliveryAttributes: ActivityAttributes {
// Static -- set once at activity creation, never changes
var orderNumber: Int
var restaurantName: String
// Dynamic -- updated throughout the activity lifetime
struct ContentState: Codable, Hashable {
var driverName: String
var estimatedDeliveryTime: ClosedRange<Date>
var currentStep: DeliveryStep
}
}
enum DeliveryStep: String, Codable, Hashable, CaseIterable {
case confirmed, preparing, pickedUp, delivering, delivered
var icon: String {
switch self {
case .confirmed: "checkmark.circle"
case .preparing: "frying.pan"
case .pickedUp: "bag.fill"
case .delivering: "box.truck.fill"
case .delivered: "house.fill"
}
}
}
Set staleDate on ActivityContent to tell the system when content becomes outdated. The system sets context.isStale to true after this date; show fallback UI (e.g., "Updating...") in your views.
let content = ActivityContent(
state: state,
staleDate: Date().addingTimeInterval(300), // stale after 5 minutes
relevanceScore: 75
)
Use Activity.request to create and display a Live Activity. Pass .token as the pushType to enable remote updates via APNs.
let attributes = DeliveryAttributes(orderNumber: 42, restaurantName: "Pizza Place")
let state = DeliveryAttributes.ContentState(
driverName: "Alex",
estimatedDeliveryTime: Date()...Date().addingTimeInterval(1800),
currentStep: .preparing
)
let content = ActivityContent(state: state, staleDate: nil, relevanceScore: 75)
do {
let activity = try Activity.request(
attributes: attributes,
content: content,
pushType: .token
)
print("Started activity: \(activity.id)")
} catch {
print("Failed to start activity: \(error)")
}
Update the dynamic content state from the app. Use AlertConfiguration to trigger a visible banner and sound alongside the update.
let updatedState = DeliveryAttributes.ContentState(
driverName: "Alex",
estimatedDeliveryTime: Date()...Date().addingTimeInterval(600),
currentStep: .delivering
)
let updatedContent = ActivityContent(
state: updatedState,
staleDate: Date().addingTimeInterval(300),
relevanceScore: 90
)
// Silent update
await activity.update(updatedContent)
// Update with an alert
await activity.update(updatedContent, alertConfiguration: AlertConfiguration(
title: "Order Update",
body: "Your driver is nearby!",
sound: .default
))
End the activity when the tracked event completes. Choose a dismissal policy to control how long the ended activity lingers on the Lock Screen.
let finalState = DeliveryAttributes.ContentState(
driverName: "Alex",
estimatedDeliveryTime: Date()...Date(),
currentStep: .delivered
)
let finalContent = ActivityContent(state: finalState, staleDate: nil, relevanceScore: 0)
// System decides when to remove (up to 4 hours)
await activity.end(finalContent, dismissalPolicy: .default)
// Remove immediately
await activity.end(finalContent, dismissalPolicy: .immediate)
// Remove after a specific time (max 4 hours from now)
await activity.end(finalContent, dismissalPolicy: .after(Date().addingTimeInterval(3600)))
Always end activities on all code paths -- success, error, and cancellation. A leaked activity stays on the Lock Screen until the system kills it (up to 8 hours), which frustrates users.
The Lock Screen is the primary surface for Live Activities. Every device with iOS 16.1+ displays Live Activities here. Design this layout first.
struct DeliveryActivityWidget: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: DeliveryAttributes.self) { context in
// Lock Screen / StandBy / CarPlay / Mac menu bar content
VStack(alignment: .leading, spacing: 8) {
HStack {
Text(context.attributes.restaurantName)
.font(.headline)
Spacer()
Text("Order #\(context.attributes.orderNumber)")
.font(.caption)
.foregroundStyle(.secondary)
}
if context.isStale {
Label("Updating...", systemImage: "arrow.trianglehead.2.clockwise")
.font(.subheadline)
.foregroundStyle(.secondary)
} else {
HStack {
Label(context.state.driverName, systemImage: "person.fill")
Spacer()
Text(timerInterval: context.state.estimatedDeliveryTime,
countsDown: true)
.monospacedDigit()
}
.font(.subheadline)
// Progress steps
HStack(spacing: 12) {
ForEach(DeliveryStep.allCases, id: \.self) { step in
Image(systemName: step.icon)
.foregroundStyle(
step <= context.state.currentStep ? .primary : .tertiary
)
}
}
}
}
.padding()
} dynamicIsland: { context in
// Dynamic Island closures (see next section)
DynamicIsland {
// Expanded regions...
DynamicIslandExpandedRegion(.leading) {
Image(systemName: "box.truck.fill").font(.title2)
}
DynamicIslandExpandedRegion(.trailing) {
Text(timerInterval: context.state.estimatedDeliveryTime,
countsDown: true)
.font(.caption).monospacedDigit()
}
DynamicIslandExpandedRegion(.center) {
Text(context.attributes.restaurantName).font(.headline)
}
DynamicIslandExpandedRegion(.bottom) {
HStack(spacing: 12) {
ForEach(DeliveryStep.allCases, id: \.self) { step in
Image(systemName: step.icon)
.foregroundStyle(
step <= context.state.currentStep ? .primary : .tertiary
)
}
}
}
} compactLeading: {
Image(systemName: "box.truck.fill")
} compactTrailing: {
Text(timerInterval: context.state.estimatedDeliveryTime,
countsDown: true)
.frame(width: 40).monospacedDigit()
} minimal: {
Image(systemName: "box.truck.fill")
}
}
}
}
The Lock Screen presentation has limited vertical space. Avoid layouts taller than roughly 160 points. Use supplementalActivityFamilies to opt into .small (compact) or .medium (standard) sizing:
ActivityConfiguration(for: DeliveryAttributes.self) { context in
// Lock Screen content
} dynamicIsland: { context in
// Dynamic Island
}
.supplementalActivityFamilies([.small, .medium])
The Dynamic Island is available on iPhone 14 Pro and later. It has three presentation modes. Design all three, but treat the Lock Screen as the primary surface since not all devices have a Dynamic Island.
Always visible when a single Live Activity is active. Space is extremely limited -- show only the most critical information.
| Region | Purpose |
|---|---|
compactLeading | Icon or tiny label identifying the activity |
compactTrailing | One key value (timer, score, status) |
Shown when multiple Live Activities compete for space. Only one activity gets the minimal slot. Display a single icon or glyph.
Shown when the user long-presses the Dynamic Island.
| Region | Position |
|---|---|
.leading | Left of the TrueDepth camera; wraps below |
.trailing | Right of the TrueDepth camera; wraps below |
.center | Directly below the camera |
.bottom | Below all other regions |
Apply a subtle tint to the Dynamic Island border:
DynamicIsland { /* expanded */ }
compactLeading: { /* ... */ }
compactTrailing: { /* ... */ }
minimal: { /* ... */ }
.keylineTint(.blue)
Push-to-update sends Live Activity updates through APNs, which is more efficient than polling from the app and works when the app is suspended.
Pass .token as the pushType when starting the activity, then forward the push token to your server:
let activity = try Activity.request(
attributes: attributes,
content: content,
pushType: .token
)
// Observe token changes -- tokens can rotate
Task {
for await token in activity.pushTokenUpdates {
let tokenString = token.map { String(format: "%02x", $0) }.joined()
try await ServerAPI.shared.registerActivityToken(
tokenString, activityID: activity.id
)
}
}
Send an HTTP/2 POST to APNs with these headers and JSON body:
Required HTTP headers:
apns-push-type: liveactivityapns-topic: <bundle-id>.push-type.liveactivityapns-priority: 5 (low) or 10 (high, triggers alert)Update payload:
{
"aps": {
"timestamp": 1700000000,
"event": "update",
"content-state": {
"driverName": "Alex",
"estimatedDeliveryTime": {
"lowerBound": 1700000000,
"upperBound": 1700001800
},
"currentStep": "delivering"
},
"stale-date": 1700000300,
"alert": {
"title": "Delivery Update",
"body": "Your driver is nearby!"
}
}
}
End payload: Same structure with "event": "end" and optional "dismissal-date".
The content-state JSON must match the ContentState Codable structure exactly. Mismatched keys or types cause silent failures.
Start a Live Activity remotely without the app running (iOS 17.2+):
Task {
for await token in Activity<DeliveryAttributes>.pushToStartTokenUpdates {
let tokenString = token.map { String(format: "%02x", $0) }.joined()
try await ServerAPI.shared.registerPushToStartToken(tokenString)
}
}
Add NSSupportsLiveActivitiesFrequentUpdates = YES to Info.plist to increase the push update budget. Use for activities that update more than once per minute (sports scores, ride tracking).
Schedule a Live Activity to start at a future time. The system starts the activity automatically without the app being in the foreground. Use for events with known start times (sports games, flights, scheduled deliveries).
let scheduledDate = Calendar.current.date(
from: DateComponents(year: 2026, month: 3, day: 15, hour: 19, minute: 0)
)!
let activity = try Activity.request(
attributes: attributes,
content: content,
pushType: .token,
start: scheduledDate
)
style: parameter iOS 26+)Control persistence: .standard (persists until ended, default) or .transient (system may dismiss automatically). Use .transient for short-lived updates like transit arrivals. The style: parameter on Activity.request requires iOS 26+.
let activity = try Activity.request(
attributes: attributes, content: content,
pushType: .token, style: .transient
)
Live Activities automatically appear in macOS Tahoe menu bar (via iPhone Mirroring) and CarPlay Home Screen. No additional code needed — ensure Lock Screen layout is legible at smaller scales.
Broadcast updates to many Live Activities at once with .channel:
let activity = try Activity.request(
attributes: attributes, content: content,
pushType: .channel("delivery-updates")
)
DON'T: Put too much content in the compact presentation -- it is tiny. DO: Show only the most critical info (icon + one value) in compact leading/trailing.
DON'T: Update Live Activities too frequently from the app (drains battery). DO: Use push-to-update for server-driven updates. Limit app-side updates to user actions.
DON'T: Forget to end the activity when the event completes. DO: Always end activities on success, error, and cancellation paths. A leaked activity frustrates users.
DON'T: Assume the Dynamic Island is available (only iPhone 14 Pro+). DO: Design for the Lock Screen as the primary surface; Dynamic Island is supplementary.
DON'T: Store sensitive information in ActivityAttributes (visible on Lock Screen). DO: Keep sensitive data in the app and show only safe-to-display summaries.
DON'T: Forget to handle stale dates. DO: Check context.isStale in views and show fallback UI ("Updating..." or similar).
DON'T: Ignore push token rotation. Tokens can change at any time. DO: Use activity.pushTokenUpdates async sequence and re-register on every emission.
DON'T: Forget the NSSupportsLiveActivities Info.plist key. DO: Add NSSupportsLiveActivities = YES to the host app's Info.plist (not the extension).
DON'T: Use the deprecated contentState-based API for request/update/end. DO: Use ActivityContent for all lifecycle calls.
DON'T: Put heavy logic in Live Activity views. They render in a size-limited widget process. DO: Pre-compute display values and pass them through ContentState.
ActivityAttributes defines static properties and ContentStateNSSupportsLiveActivities = YES in host app Info.plistActivityContent (not deprecated contentState API)context.isStaleactivity.pushTokenUpdatesAlertConfiguration used for important updatesActivityAuthorizationInfo checked before startingreferences/live-activity-patterns.md for patterns and code examplesWeekly Installs
386
Repository
GitHub Stars
269
First Seen
Mar 3, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex383
gemini-cli380
amp380
cline380
github-copilot380
kimi-cli380
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
105,000 周安装