跪拜 Guibai
← All articles
Frontend · iOS · APP

Live Activities Are a System Renderer, Not an App Feature

By _瑞 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Live Activities are the only API surface for persistent, real-time information on the iPhone's most visible screen real estate, but the architecture inverts normal app development: the system owns the rendering, the app is just a configuration pipe. Misunderstanding the token lifecycle or the update-vs-start semantics silently breaks delivery at scale, and the debugging surface is hostile because the renderer gives no feedback.

Summary

The Dynamic Island is a system-controlled display region around the front sensor cutout, and third-party apps reach it exclusively through the Live Activities framework. A Live Activity's UI lives in a Widget Extension, written in SwiftUI, and renders without the app process ever waking up. Updates flow from a server through APNs using a dedicated liveactivity push type and token, never through the app itself.

Three token types govern the lifecycle: a per-activity token for updates and endings, and an app-level push-to-start token (iOS 17.2+) that can launch a new activity even when the app isn't running. The server must prioritize updating existing activities and fall back to starting new ones only when an update fails with a 410 or 400, because Apple caps the number of concurrent activities and each start creates a new one that doesn't replace the old.

Silent failures dominate debugging. An APNs 200 response means nothing if the attributes-type string doesn't match the ActivityAttributes struct name character-for-character, or if a content-state field is mistyped or sent as a string instead of a number. The system rendering step is a complete observability blind spot, so every other link in the chain needs logging.

Takeaways
Third-party Dynamic Island integration is exclusively done through the Live Activities framework, which also displays a persistent card on the lock screen.
The Live Activity UI runs in a separate Widget Extension process using SwiftUI; the main app process is never involved in rendering.
Three APNs token types exist: per-activity tokens for update/end, and an app-level push-to-start token (iOS 17.2+) for creating activities when the app is closed.
Always prefer updating an existing activity's token; fall back to starting a new activity only when the update returns a 410 or 400 from APNs.
Each start creates a new activity without replacing old ones, and Apple enforces an undisclosed cap on concurrent activities.
A token for an ended activity will still return APNs 200 on update but produce no visible result, creating a silent failure that requires time-based token expiry (roughly 8 hours).
ContentState fields must match the server JSON exactly in name and type; a single character mismatch or sending a number as a string causes a silent decode failure with no error.
On iOS 26, tapping the compact or minimal Dynamic Island states launches the app but dispatches no deep link parameters; precise routing only works from the expanded state or lock screen.
Images cannot be loaded from URLs in the Widget Extension; all assets must be pre-bundled or stored in an App Group container, and ContentState has a 4 KB payload limit.
Time-based values like countdowns and linear progress can be computed locally by the system after the initial push, removing the need for continuous updates.
Conclusions

The entire architecture treats the app as a dumb configuration pipe: the system owns the rendering, the server owns the data, and the app just hands off a token. This is a fundamentally different mental model from most iOS features where the app is the center of the universe.

Apple's decision to return APNs 200 for updates to expired activities while silently dropping the content on the device creates a dangerous false-positive that will corrupt any server's state unless explicit time-based token expiry is built in.

The iOS 26 behavior change that strips deep link parameters from compact and minimal state taps is a breaking change with no documentation, and it forces any app that relies on routing to push users into the expanded state or lock screen card.

The 4 KB ContentState limit and ban on network-loaded images mean any dynamic image strategy requires pre-bundling assets and sending only an index, which is a constraint most developers won't discover until they try to ship a feature that worked fine in their head.

Concepts & terms
Live Activity token
A per-activity APNs token bound to the topic <bundleID>.push-type.liveactivity, used to update or end a specific running Live Activity. Distinct from the standard device token and the push-to-start token.
Push-to-start token
An app-level APNs token (iOS 17.2+) that can create a new Live Activity even when the app is not running. One per app, independent of any existing activity.
Widget Extension
A separate process and target in an iOS app that hosts WidgetKit-based UI, including Live Activity views. It runs independently of the main app and is the only place SwiftUI-based Live Activity interfaces can be defined.
ContentState
The dynamic, Codable portion of an ActivityAttributes model that gets replaced wholesale on each push update. Field names and types must match the server's content-state JSON exactly, or the system silently discards the update.
App Group
An iOS capability that creates a shared container between the main app and its Widget Extension, allowing data to be passed between the two otherwise sandboxed processes.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗