Live Activities

Integrate Live Activities into your iOS app to display real-time updates on the Lock Screen and Dynamic Island. AXP

View as Markdown

For the push API method, see the iOS Live Activities messaging guide. See also the iOS Live Activities feature guide.

App Setup

To support Live Activities, you must call restoreLiveActivityTracking once after takeOff with all the Live Activity types that you might track with Airship. This allows Airship to resume tracking any previously tracked activities across app inits and to automatically track the pushToStartToken that allows starting activities through a push notification.

Restore Live Activity tracking

Airship.takeOff(config, launchOptions: launchOptions)

Airship.channel.restoreLiveActivityTracking { restorer in
    await restorer.restore(forType: Activity<SportsActivityAttributes>.self)
    await restorer.restore(forType: Activity<SomeOtherAttributes>.self)
}
Note

Live Activities are not supported in Objective-C. Use Swift for Live Activity implementation.

After the restore call above, Airship will track the pushToStartTokens for the activity’s attribute types. You can then start a Live Activity through a push notification. Starting a Live Activity does not automatically track it. Instead, the app will be woken up and you must call through to Airship with the activity instance and the name.

Watching for Live Activities

There is no entry point into the app when it is started for a Live Activity being created. Instead, you need to query Live Activities on init and when a pushToStartToken update is received to track them through Airship. Airship provides an extension Activity<T>.airshipWatchActivities(activityBlock:) that can be used to do this for you.

In this example, we assume the gameID on our SportsActivityAttributes will be used to send updates through Airship after it is created:

Watch Live Activities

Airship.channel.restoreLiveActivityTracking { restorer in
    await restorer.restore(forType: Activity<SportsActivityAttributes>.self)
}

Activity<SportsActivityAttributes>.airshipWatchActivities { activity in
    Airship.channel.trackLiveActivity(activity, name: activity.attributes.gameID)
}
Note

Live Activities are not supported in Objective-C. Use Swift for Live Activity implementation.

Starting Live Activities

To start a Live Activity from the app, make sure to set the pushType to .token. After it is started, immediately track it with Airship.channel.trackLiveActivity(_:name:).

Start a Live Activity

let activity = try Activity.request(
    attributes: attributes,
    content: content,
    pushType: .token
)

Airship.channel.trackLiveActivity(
    activity,
    name: attributes.gameID
)
Note

Live Activities are not supported in Objective-C. Use Swift for Live Activity implementation.

Updating Live Activities

To update a Live Activity, use the standard ActivityKit APIs. First find the Activity instance then call update on it:

Update a Live Activity

guard
    let activity = Activity<SportsActivityAttributes>.activities.first(where: { $0.id == "sports-game-123" })
else {
    // not found
    return
}
activity.update(contentUpdate)
Note

Live Activities are not supported in Objective-C. Use Swift for Live Activity implementation.

Updating Before the User Accepts the Activity

The first few times your app starts a Live Activity, iOS shows Allow and Deny buttons and does not issue an update token until the user taps Allow. Until then, token-based push updates have nothing to target and the activity can look frozen.

Code-based updates do not need the token or the user’s acceptance. Wake your app with a background push that carries the content state, then apply it with update. This needs the Remote notifications background mode, and the push must have no alert.

Send a background push with your content state and an identifier for the activity:

{
  "aps": { "content-available": 1 },
  "my_live_activity": {
    "game_id": "sports-game-123",
    "content_state": { "homeScore": 2, "awayScore": 1 }
  }
}

Handle it in your push delegate:

Update a Live Activity from a background push

// Set once after takeOff.
Airship.push.pushNotificationDelegate = self

struct LiveActivityUpdate: Decodable {
    let gameID: String
    let contentState: SportsActivityAttributes.ContentState

    enum CodingKeys: String, CodingKey {
        case gameID = "game_id"
        case contentState = "content_state"
    }
}

func receivedBackgroundNotification(
    _ userInfo: [AnyHashable: Any]
) async -> UIBackgroundFetchResult {
    guard
        let payload = userInfo["my_live_activity"],
        let data = try? JSONSerialization.data(withJSONObject: payload),
        let update = try? JSONDecoder().decode(LiveActivityUpdate.self, from: data),
        let activity = Activity<SportsActivityAttributes>.activities.first(
            where: { $0.attributes.gameID == update.gameID }
        )
    else {
        return .noData
    }

    await activity.update(
        ActivityContent(state: update.contentState, staleDate: nil)
    )
    return .newData
}
Note

Live Activities are not supported in Objective-C. Use Swift for Live Activity implementation.

Both halves use standard Apple APIs. Activity.update(_:) is a supported code path that does not need the update token, and background push is a documented way to refresh content, so nothing in the App Store Review Guidelines restricts this.

Note

Background push is best-effort. iOS throttles it, does not guarantee delivery, and will not wake a force-quit app, so Apple recommends sending only a few per hour. Use this as a fallback before the user accepts the activity, not a primary update path. Once they tap Allow, an update token is issued and you can switch back to token-based updates.

Ending Live Activities

To end a Live Activity, use the standard ActivityKit APIs. First find the Activity instance then call end on it with a dismissal policy:

End a Live Activity

guard
    let activity = Activity<SportsActivityAttributes>.activities.first(where: { $0.id == "sports-game-123" })
else {
    // not found
    return
}

activity.end(contentUpdate, dismissalPolicy: .default)
Note

Live Activities are not supported in Objective-C. Use Swift for Live Activity implementation.