Build or review Live Activities and Dynamic Island experiences with ActivityKit, including lifecycle, layouts, push updates, and scheduled delivery. Use for time-sensitive Lock Screen or Dynamic Island state; route ordinary widgets to widgetkit.
ActivityKit owns real-time, glanceable Live Activities on the Lock Screen and
Dynamic Island. Route ordinary timeline widgets to widgetkit and generic APNs
setup to push-notifications; keep the Live Activity lifecycle, token handling,
and payload contract here.
Modern ActivityContent lifecycle APIs require iOS 16.2+ unless noted. The
server's aps.content-state must decode into the exact
ActivityAttributes.ContentState shape, including coordinated custom date or
range encoding.
For attributes, local request/update code, push-to-update, push-to-start,
channel payloads, and exact APNs headers, read
Lifecycle, updates, and push.
For dismissal policies, terminal cleanup, Lock Screen layouts, and every
Dynamic Island region, read
Ending and presentation.
For multiple activities, authorization, rotating tokens, background behavior,
Simulator/device verification, previews, and Info.plist keys, read
Concurrency, state, and testing.
For a new Live Activity:
Enable the host-app capability and set NSSupportsLiveActivities = YES.
Separate immutable ActivityAttributes from a small, Codable, HashableContentState; encode a representative server fixture before integrating APNs.
Build ActivityConfiguration for the Lock Screen first, then compact,
minimal, and expanded Dynamic Island presentations.
Check ActivityAuthorizationInfo.areActivitiesEnabled, request with
ActivityContent, and exercise local update plus every terminal end path.
For remote delivery, observe every emitted token and validate update, end,
and push-to-start payloads against the same Codable contract.
Core contract
Keep static identity in the outer attributes and only changing display state in
ContentState:
import ActivityKit
struct DeliveryAttributes: ActivityAttributes {
let orderNumber: Int
struct ContentState: Codable, Hashable {
var stage: String
var estimatedDelivery: ClosedRange<Date>
}
}
Use ActivityContent for request, update, and end. Set staleDate whenever the
display can become misleading, and render a safe fallback from
ActivityViewContext.isStale. Treat Lock Screen content as public: never place
secrets or sensitive detail in attributes or state.
Lifecycle and delivery rules
End on success, user/app cancellation, sign-out, unrecoverable app error, and
terminal server failure. Apply a final truthful state before ending when one
exists.
Distinguish the active lifetime (up to 8 hours), system-ended Lock Screen
presence (up to 4 additional hours), and an app-ended .default dismissal
linger (up to 4 hours after ending).
Use .token for per-activity remote updates. Observe
activity.pushTokenUpdates for the full activity lifetime because tokens rotate.
Push-to-start uses Activity<Attributes>.pushToStartTokenUpdates; it is not an
app APNs token or an activity update token. Its payload requires an alert.
Live Activity pushes require apns-push-type: liveactivity. Device-token
pushes use <bundle-id>.push-type.liveactivity as the topic. Priority controls
delivery urgency; only aps.alert requests visible alert behavior.
NSSupportsLiveActivitiesFrequentUpdates requests a larger system-managed
budget, not a guaranteed cadence. Check frequentPushesEnabled.
Do not fetch network or location data from the widget extension view. Compute
display state in the app/server and deliver it through ActivityKit.
Presentation rules
Design the Lock Screen first; Dynamic Island is unavailable on some devices.
Compact leading/trailing regions contain only identity plus one critical value.
Minimal presentation must work as a single glyph when activities compete.
Expanded regions must tolerate content changes without relying on fixed sizes.
Render stale and terminal states explicitly; avoid pretending old progress is live.
Test push delivery and Dynamic Island behavior on physical hardware.
Availability-sensitive features
Feature
Availability and constraint
Push-to-start
iOS 17.2+
style: request parameter
iOS 18+; .standard for persistent work, .transient only for short-lived expanded presentation
Channel/broadcast updates
iOS 18+; update/end only, not start
Supplemental activity families
iOS 18+
Scheduled start
iOS 26+
Paired Mac and CarPlay presentation
iOS 26+; validate compact layouts and noninteractive CarPlay behavior
Use availability guards around each newer API. Read the lifecycle reference for
complete request and payload examples rather than combining signatures from
different OS branches.
Common Mistakes
Using deprecated contentState request/update/end overloads instead of
ActivityContent.
Ignoring token rotation or registering only the first emitted token.
Assuming APNs priority creates an alert without aps.alert.
Leaving an activity alive after a terminal state.
Treating Dynamic Island as the only presentation surface.
Omitting stale UI, overloading compact regions, or exposing sensitive data.
Assuming frequent-update capability guarantees a fixed update frequency.
Review Checklist
Static attributes and dynamic ContentState have a small, tested Codable contract.
Host app contains NSSupportsLiveActivities = YES and authorization is checked.
Request, update, and end use ActivityContent.
Every terminal path ends the activity with an intentional dismissal policy.
Lock Screen, stale, compact, minimal, expanded, and terminal states are covered.
Update and push-to-start tokens are collected from the correct async sequences.
APNs headers, alert semantics, and content-state fixtures are validated.
Newer APIs have explicit availability guards.
Extension views perform no network or location work.
Simulator previews and physical-device delivery checks both exist.
1---2name: activitykit3description: Build or review Live Activities and Dynamic Island experiences with ActivityKit, including lifecycle, layouts, push updates, and scheduled delivery. Use for time-sensitive Lock Screen or Dynamic Island state; route ordinary widgets to widgetkit.4---56# ActivityKit78ActivityKit owns real-time, glanceable Live Activities on the Lock Screen and9Dynamic Island. Route ordinary timeline widgets to `widgetkit` and generic APNs10setup to `push-notifications`; keep the Live Activity lifecycle, token handling,11and payload contract here.1213Modern `ActivityContent` lifecycle APIs require iOS 16.2+ unless noted. The14server's `aps.content-state` must decode into the exact15`ActivityAttributes.ContentState` shape, including coordinated custom date or16range encoding.1718## Contents1920- [Choose the workflow](#choose-the-workflow)21- [Core contract](#core-contract)22- [Lifecycle and delivery rules](#lifecycle-and-delivery-rules)23- [Presentation rules](#presentation-rules)24- [Availability-sensitive features](#availability-sensitive-features)25- [Common mistakes](#common-mistakes)26- [Review checklist](#review-checklist)27- [References](#references)2829## Choose the workflow3031- For attributes, local request/update code, push-to-update, push-to-start,32 channel payloads, and exact APNs headers, read33 [Lifecycle, updates, and push](references/lifecycle-updates-and-push.md).34- For dismissal policies, terminal cleanup, Lock Screen layouts, and every35 Dynamic Island region, read36 [Ending and presentation](references/ending-and-presentation.md).37- For multiple activities, authorization, rotating tokens, background behavior,38 Simulator/device verification, previews, and Info.plist keys, read39 [Concurrency, state, and testing](references/concurrency-state-and-testing.md).4041For a new Live Activity:42431. Enable the host-app capability and set `NSSupportsLiveActivities = YES`.442. Separate immutable `ActivityAttributes` from a small, `Codable`, `Hashable`45 `ContentState`; encode a representative server fixture before integrating APNs.463. Build `ActivityConfiguration` for the Lock Screen first, then compact,47 minimal, and expanded Dynamic Island presentations.484. Check `ActivityAuthorizationInfo.areActivitiesEnabled`, request with49 `ActivityContent`, and exercise local update plus every terminal end path.505. For remote delivery, observe every emitted token and validate update, end,51 and push-to-start payloads against the same Codable contract.5253## Core contract5455Keep static identity in the outer attributes and only changing display state in56`ContentState`:5758```swift59import ActivityKit6061struct DeliveryAttributes: ActivityAttributes {62 let orderNumber: Int6364 struct ContentState: Codable, Hashable {65 var stage: String66 var estimatedDelivery: ClosedRange<Date>67 }68}69```7071Use `ActivityContent` for request, update, and end. Set `staleDate` whenever the72display can become misleading, and render a safe fallback from73`ActivityViewContext.isStale`. Treat Lock Screen content as public: never place74secrets or sensitive detail in attributes or state.7576## Lifecycle and delivery rules7778- End on success, user/app cancellation, sign-out, unrecoverable app error, and79 terminal server failure. Apply a final truthful state before ending when one80 exists.81- Distinguish the active lifetime (up to 8 hours), system-ended Lock Screen82 presence (up to 4 additional hours), and an app-ended `.default` dismissal83 linger (up to 4 hours after ending).84- Use `.token` for per-activity remote updates. Observe85 `activity.pushTokenUpdates` for the full activity lifetime because tokens rotate.86- Push-to-start uses `Activity<Attributes>.pushToStartTokenUpdates`; it is not an87 app APNs token or an activity update token. Its payload requires an alert.88- Live Activity pushes require `apns-push-type: liveactivity`. Device-token89 pushes use `<bundle-id>.push-type.liveactivity` as the topic. Priority controls90 delivery urgency; only `aps.alert` requests visible alert behavior.91- `NSSupportsLiveActivitiesFrequentUpdates` requests a larger system-managed92 budget, not a guaranteed cadence. Check `frequentPushesEnabled`.93- Do not fetch network or location data from the widget extension view. Compute94 display state in the app/server and deliver it through ActivityKit.9596## Presentation rules9798- Design the Lock Screen first; Dynamic Island is unavailable on some devices.99- Compact leading/trailing regions contain only identity plus one critical value.100- Minimal presentation must work as a single glyph when activities compete.101- Expanded regions must tolerate content changes without relying on fixed sizes.102- Render stale and terminal states explicitly; avoid pretending old progress is live.103- Test push delivery and Dynamic Island behavior on physical hardware.104105## Availability-sensitive features106107| Feature | Availability and constraint |108|---|---|109| Push-to-start | iOS 17.2+ |110| `style:` request parameter | iOS 18+; `.standard` for persistent work, `.transient` only for short-lived expanded presentation |111| Channel/broadcast updates | iOS 18+; update/end only, not start |112| Supplemental activity families | iOS 18+ |113| Scheduled start | iOS 26+ |114| Paired Mac and CarPlay presentation | iOS 26+; validate compact layouts and noninteractive CarPlay behavior |115116Use availability guards around each newer API. Read the lifecycle reference for117complete request and payload examples rather than combining signatures from118different OS branches.119120## Common Mistakes121122- Using deprecated `contentState` request/update/end overloads instead of123 `ActivityContent`.124- Ignoring token rotation or registering only the first emitted token.125- Assuming APNs priority creates an alert without `aps.alert`.126- Leaving an activity alive after a terminal state.127- Treating Dynamic Island as the only presentation surface.128- Omitting stale UI, overloading compact regions, or exposing sensitive data.129- Assuming frequent-update capability guarantees a fixed update frequency.130131## Review Checklist132133- [ ] Static attributes and dynamic `ContentState` have a small, tested Codable contract.134- [ ] Host app contains `NSSupportsLiveActivities = YES` and authorization is checked.135- [ ] Request, update, and end use `ActivityContent`.136- [ ] Every terminal path ends the activity with an intentional dismissal policy.137- [ ] Lock Screen, stale, compact, minimal, expanded, and terminal states are covered.138- [ ] Update and push-to-start tokens are collected from the correct async sequences.139- [ ] APNs headers, alert semantics, and `content-state` fixtures are validated.140- [ ] Newer APIs have explicit availability guards.141- [ ] Extension views perform no network or location work.142- [ ] Simulator previews and physical-device delivery checks both exist.143144## References145146- [Lifecycle, updates, and push](references/lifecycle-updates-and-push.md)147- [Ending and presentation](references/ending-and-presentation.md)148- [Concurrency, state, and testing](references/concurrency-state-and-testing.md)149- [ActivityKit documentation](https://sosumi.ai/documentation/activitykit)
Run npx skillmds@latest add thiennc-tesoglobal/activitykit in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Build or review Live Activities and Dynamic Island experiences with ActivityKit, including lifecycle, layouts, push updates, and scheduled delivery. Use for time-sensitive Lock Screen or Dynamic Island state; route ordinary widgets to widgetkit. It is listed under Coding & Dev Tools on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
thiennc-tesoglobal (@thiennc-tesoglobal) published this skill. Their other Agent Skills are listed on their SkillMD profile.