Mobile Development 14 min read

Mastering iOS Live Activities: From Setup to Real‑Time Updates

This guide walks you through Apple’s Live Activity feature introduced at WWDC22, covering the required ActivityKit framework, project configuration for Objective‑C/Swift mixed or pure Swift apps, data modeling, creation, updating, ending, push‑token handling, server‑side push payloads, permission checks, and common pitfalls such as network‑image restrictions.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Mastering iOS Live Activities: From Setup to Real‑Time Updates

Introduction

At WWDC22 Apple introduced Live Activity to let apps display real‑time information on the lock screen. The feature is implemented via the ActivityKit framework and requires SwiftUI for the UI.

Scenario & Configuration

Live Activity is available on iOS 16.1+ (lock‑screen) and iPhone 14 Pro+ (Dynamic Island). Compared with iOS 16.0 widgets, Live Activities appear in the notification area, support custom views, and refresh via push or local updates. Limitations include an 8‑hour maximum duration, foreground‑only creation, a 4 KB push payload limit, and restrictions on creating multiple cards simultaneously.

Project Setup

OC & Swift mixed project : Create a Swift file in the main app to bridge to ActivityKit, which is Swift‑only. Add a new Target for the Live Activity, link it with the main app, and ensure the Target contains the UI and update logic.

Pure Swift project : No bridging file is needed; the same steps apply.

Both setups require adding NSSupportsLiveActivities = YES to the app’s Info.plist.

Data Model

struct ActivityWidgetAttributes: ActivityAttributes {
    public struct ContentState: Codable, Hashable {
        var process: Double
        var title: String
        // … other dynamic fields
    }
    var icon: String // static field
}

The ContentState holds mutable data, while static fields are defined directly in the attribute struct.

Main Program

Creating a Live Activity:

let initialState = ActivityWidgetAttributes.ContentState(process: 0.6, title: "this is a title")
let attributes = ActivityWidgetAttributes(icon: "XiaoBu")
myActivity = try Activity.request(attributes: attributes, contentState: initialState, pushType: .token)

Updating the activity:

let updateState = ActivityWidgetAttributes.ContentState(nickName: "Augus123")
let alert = AlertConfiguration(title: "111", body: "2222", sound: .default)
await myActivity?.update(using: updateState, alertConfiguration: alert)

Ending the activity with a dismissal policy:

await myActivity?.end(using: nil, dismissalPolicy: .immediate)

State Monitoring & Push Token Handling

Use activityStateUpdates to observe lifecycle changes and pushTokenUpdates to retrieve the push token, which must be sent to the backend for remote updates.

for await token in activity.pushTokenUpdates {
    // send token to server
}

From iOS 17.2 onward you can also use pushToStartTokenUpdates to obtain a token without launching the activity.

Permissions

Live Activity availability can be checked with ActivityAuthorizationInfo().areActivitiesEnabled. The value is read‑only and cannot be set programmatically.

Server‑Side Push Payload

{
  "aps": {
    "timestamp": 1666667682,
    "event": "update",
    "content-state": {"process": 0.7, "title": "更新的title"},
    "alert": {"title": "Track Update", "body": "Tony Stark is now handling the delivery!"}
  }
}

Required fields: TEAM_ID, AUTH_KEY_ID, TOPIC (your bundle identifier with .push-type.liveactivity), and the device token. Use the APNs sandbox or production host accordingly.

Technical Challenge: Network Images

Live Activity extensions cannot perform network requests. To display remote images you must download them in the main app (e.g., via URLSession) and store them in an App Group container, then read the file from the extension.

private func downloadImage(from url: URL) async throws -> URL? {
    guard var destination = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.test.liveactivityappgroup") else { return nil }
    destination = destination.appendingPathComponent(url.lastPathComponent)
    if FileManager.default.fileExists(atPath: destination.path()) { return destination }
    let (source, _) = try await URLSession.shared.download(from: url)
    try FileManager.default.moveItem(at: source, to: destination)
    return destination
}

In the extension, read the image from the shared container and display it with SwiftUI’s Image(uiImage:).

References

Live Activities UI – https://developer.apple.com/design/human-interface-guidelines/live-activities

Live Activities API – https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities

Live Activity Push Notifications – https://developer.apple.com/documentation/activitykit/starting-and-updating-live-activities-with-activitykit-push-notifications

How to fetch an image in live activity – https://forums.developer.apple.com/forums/thread/716902

mobile developmentiOSpush notificationsSwiftUIActivityKitLive Activity
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.