Live Activities
Integrate Live Activities into your iOS app to display real-time updates on the Lock Screen and Dynamic Island. AXP
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)
}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)
}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
)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)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
}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.
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)Live Activities are not supported in Objective-C. Use Swift for Live Activity implementation.