Token导航 LogoToken导航TokenDH.com
前端设计只读github未标认证来源可访问许可证需确认审计通过

live-activities现场活动

Agent Skill

live-activities 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

11,072

周安装

466

GitHub Stars

502

下载量

3,877
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:live-activities(现场活动)
来源仓库:https://github.com/dpearson2699/swift-ios-skills
仓库路径:skills/live-activities
安装命令:
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill live-activities
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill live-activities

简介

使用 ActivityKit 的 iOS 实时锁屏和动态岛小部件。

  • 定义 Activity 属性
  • 具有静态数据和动态 ContentState
  • ;使用 Activity.request() 启动活动,用activity.update()更新,并以 Activity.end() 结束
  • 设计三种动态岛演示文稿:紧凑(图标 + 一个值)、最小(单个字形)和扩展(具有前导、尾随、中心、底部的多区域布局)
  • 通过 APN 推送更新,pushType: .token,通过activity.pushTokenUpdates转发token
  • 并发送与您的 ContentState 匹配的 JSON 有效负载
  • 结构
  • 使用 context.isStale 处理过时的日期
  • 显示备用 UI;始终结束活动以防止锁屏混乱
  • iOS 26 增加了计划活动、瞬态样式、Mac 菜单栏支持以及基于频道的广播更新推送

SKILL.md

Live Activities and Dynamic Island

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.

Contents

Workflow

1. Create a new Live Activity

  1. Add NSSupportsLiveActivities = YES to the host app's Info.plist.
  2. Define an ActivityAttributes struct with a nested ContentState.
  3. Create an ActivityConfiguration in the widget bundle with Lock Screen content and Dynamic Island closures.
  4. Start the activity with Activity.request(attributes:content:pushType:).
  5. Update with activity.update(_:) and end with activity.end(_:dismissalPolicy:).
  6. Forward push tokens to your server for remote updates.

2. Review existing Live Activity code

Run through the Review Checklist at the end of this document.

ActivityAttributes Definition

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

Stale Date

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
)

Activity Lifecycle

Starting

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

Updating

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

Ending

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.

Lock Screen Presentation

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

Lock Screen Sizing

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])

Dynamic Island

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.

Compact (Leading + Trailing)

Always visible when a single Live Activity is active. Space is extremely limited -- show only the most critical information.

RegionPurpose
compactLeadingIcon or tiny label identifying the activity
compactTrailingOne key value (timer, score, status)

Minimal

Shown when multiple Live Activities compete for space. Only one activity gets the minimal slot. Display a single icon or glyph.

Expanded Regions

Shown when the user long-presses the Dynamic Island.

RegionPosition
.leadingLeft of the TrueDepth camera; wraps below
.trailingRight of the TrueDepth camera; wraps below
.centerDirectly below the camera
.bottomBelow all other regions

Keyline Tint

Apply a subtle tint to the Dynamic Island border:

DynamicIsland { /* expanded */ }
    compactLeading: { /* ... */ }
    compactTrailing: { /* ... */ }
    minimal: { /* ... */ }
    .keylineTint(.blue)

Push-to-Update

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.

Setup

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

APNs Payload Format

Send an HTTP/2 POST to APNs with these headers and JSON body:

Required HTTP headers:

  • apns-push-type: liveactivity
  • apns-topic: <bundle-id>.push-type.liveactivity
  • apns-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.

Push-to-Start

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

Frequent Push Updates

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).

iOS 26 Additions

Scheduled Live Activities (iOS 26+)

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
)

ActivityStyle (iOS 16.1+ type, 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
)

Mac Menu Bar & CarPlay (iOS 26+)

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.

Channel-Based Push (iOS 18+)

Broadcast updates to many Live Activities at once with .channel:

let activity = try Activity.request(
    attributes: attributes, content: content,
    pushType: .channel("delivery-updates")
)

Common Mistakes

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.

Review Checklist

  • ActivityAttributes defines static properties and ContentState
  • NSSupportsLiveActivities = YES in host app Info.plist
  • Activity uses ActivityContent (not deprecated contentState API)
  • Activity ended in all code paths (success, error, cancellation)
  • Lock Screen layout handles context.isStale
  • Dynamic Island compact, expanded, and minimal implemented
  • Push token forwarded to server via activity.pushTokenUpdates
  • AlertConfiguration used for important updates
  • ActivityAuthorizationInfo checked before starting
  • ContentState kept small (serialized on every update)
  • Tested on device (Dynamic Island differs from Simulator)
  • Ensure ActivityAttributes and ContentState types are Sendable; update Live Activity UI on @MainActor

References

  • See references/live-activity-patterns.md for patterns and code examples

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

36.92%
按下载量换算1,431

Claude

30.8%
按下载量换算1,194

Cursor

19.9%
按下载量换算772

Gemini CLI

9.13%
按下载量换算354

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

只读

该 Skill 主要提供规则、说明或参考内容,本身偏只读;真正读写文件、联网或执行命令仍取决于宿主 Agent 的任务。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills