Skip to main content

29 - Live Activties

iOS Live Activities

Starting with iOS 16 and iPadOS 17, Live Activities can display up-to-date information for your apps that appear in glanceable locations such as the Lock Screen, the Dynamic Island, and iPhone StandBy, allowing people to view the progress of an activity, event, or task at a glance.

Lock Screen

On the Lock Screen, the system uses the Lock Screen presentation to display a banner at the bottom of the screen.

The Lock Screen presentation appears on all kinds of devices.

alt text

Alert Banner

On an unlocked device that doesn’t support the Dynamic Island (Before iPhone14 Pro), if your device receives a Live Activity update including an alert configuration, it will show the Lock Screen presentation of your Live Activity as a banner that overlays the Home Screen or another active app.

alt text

Dynamic Island

On unlocked Devices that support the Dynamic Island (iPhone 14 Pro and later), the system displays Live Activities in the Dynamic Island using several presentations.

The Dynamic Island is available on the ‌iPhone 14‌ Pro, ‌iPhone 14‌ Pro Max, ‌iPhone 15‌, ‌iPhone 15‌ Plus, ‌iPhone 15 Pro‌, and ‌iPhone 15 Pro‌ Max.

Compact Presentation

When there’s only one ongoing Live Activity, the system uses the compact presentation. It’s composed of two elements: one that displays on the leading side of the TrueDepth camera, and one that displays on the trailing side.

alt text

Minimal Presentation

When multiple Live Activities from several apps are active, the system uses the circular minimal presentation to display two of them in the Dynamic Island.

The system chooses a Live Activity from one app to appear attached to the Dynamic Island while presenting a Live Activity from another app detached from the Dynamic Island.

alt text alt text

Expanded Presentation

When people touch and hold a Live Activity in a compact or minimal presentation, the system displays the expanded presentation. alt text alt text

StandBy

On iPhone in StandBy (iOS17 and later), a Live Activity takes up minimal space at the top of the screen to leave space for widgets by default. alt text

When you tap the minimal presentation, the system scales the Lock Screen appearance by 2x to fill the entire screen.

alt text

Actions on Live Activities

Tap to Launch Your App

People tap a Live Activity to launch your app.

You can use widgetURL(_:) to create a deep link into your app from the Lock Screen, compact leading, compact trailing, and minimal presentations.

The expanded presentation offers additional options to create deep links into your app for more utility using SwiftUI’s Link.

If you don’t explicitly provide a deep link into your app with widgetURL(_:) or Link, the system launches your app and passes a NSUserActivity object to the scene(_:willContinueUserActivityWithType:) and scene(_:continue:) callbacks.

https://developer.apple.com/documentation/SwiftUI/Link

The NSUserActivity object’s activityType is NSUserActivityTypeLiveActivity,

Quick Actions with Buttons or Toggles

Like widgets, starting with iOS 17 and iPadOS 17, Live Activities can contain SwiftUI buttons and toggles to provide quick actions.

To add a toggle or button to a Live Activity, adopt the App Intents framework and use the initializers for Button and Toggle that take an app intent.

https://developer.apple.com/documentation/AppIntents

References

Add Live Activities to you app

  • Create a Widget Extension with “Include Live Activity”
  • Add Info.Plist Entries to Your Main Targets
  • Define the Static and Dynamic Data of Your Live Activity
  • Add Live Activities UI to the widget extension
  • Start/Update/End a Live Activity in Your App

Create a Widget Extension with “Include Live Activity”

Create a widget extension if you haven’t added one to your app and make sure to select “Include Live Activity”.
Xcode -> File -> New -> Target... , select Widget Extension alt text

Click Next

alt text

Make sure to select “Include Live Activity” , then Click Finish.

Add Info.Plist Entries to Your Main Targets

  • Add the NSSupportsLiveActivities key, set its value to true.
  • Optionally, Add NSSupportsLiveActivitiesFrequentUpdates key, set its value to true
<key>NSSupportsLiveActivities</key>
<true/>
<key>NSSupportsLiveActivitiesFrequentUpdates</key>
<true/>

alt text

Define the Static and Dynamic Data of Your Live Activity

The ActivityAttributes protocol describes the content that appears in your Live Activity.

An ActivityAttributes model include static data that doesn't change in the lifecycle of one Live Activity and dymanic data that changes over time, and this dynamic data can also be updated by Live Activity push notifications.

The inner ContentState type of ActivityAttributes represent the dynamic data.

In the following Xcode default live activity template code example, emoji is the dynamic data, and name is the static data.

import ActivityKit
import WidgetKit
import SwiftUI

struct LiveActivityAttributes: ActivityAttributes {
public struct ContentState: Codable, Hashable {
// Dynamic stateful properties about your activity go here!
var emoji: String
}

// Fixed non-changing properties about your activity go here!
var name: String
}

After you define the data that appears in the Live Activity with the ActivityAttributes structure, add code to return an ActivityConfiguration in your widget implementation.

The following example is the Xcode default live activity template code. It defines four kinds of UI style,including:

  • Lock Screen UI
  • Dynamic Island Expanded UI
  • Dynamic Island Compact UI
  • Dynamic Island Minimal UI
struct LiveActivityLiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: LiveActivityAttributes.self) { context in
// Lock screen/banner UI goes here
VStack {
Text("Hello \(context.state.emoji)")
}
.activityBackgroundTint(Color.cyan)
.activitySystemActionForegroundColor(Color.black)

} dynamicIsland: { context in
DynamicIsland {
// Expanded UI goes here. Compose the expanded UI through
// various regions, like leading/trailing/center/bottom
DynamicIslandExpandedRegion(.leading) {
Text("Leading")
}
DynamicIslandExpandedRegion(.trailing) {
Text("Trailing")
}
DynamicIslandExpandedRegion(.bottom) {
Text("Bottom \(context.state.emoji)")
// more content
}
} compactLeading: {
Text("L")
} compactTrailing: {
Text("T \(context.state.emoji)")
} minimal: {
Text(context.state.emoji)
}
.widgetURL(URL(string: "http://www.apple.com"))
.keylineTint(Color.red)
}
}
}

Start a Live Activity

You can only start a Live Activity in your app when the app is in the foreground.

Pass the ActivityAttributes , ActivityContent and optional PushType (.token) parameters to request(attributes:content:pushType:) function

INFO

With the PushType(.token), you can get a push token after the activity is created, Then, you can use the push token to update or end this activity using ActivityKit push notifications.

func startActivity() {
guard ActivityAuthorizationInfo().areActivitiesEnabled else {
print("You can't start live activity.")
return
}

do {
let atttribute = LiveActivityAttributes(name:"APNsPush")
let initialState = LiveActivityAttributes.ContentState(emoji: "😇")
let activity = try Activity<LiveActivityAttributes>.request(
attributes: atttribute,
content: .init(state:initialState , staleDate: nil),
pushType: .token
)
self.currentActivity = activity
} catch {
print(error)
}
}

Update a Live Activity

When you start a Live Activity, you can update the data of the Live Activity using the update(_:) method of the Activity object you received when you started the Live Activity.

You can update or end a Live Activity from your app while it runs in the background — for example, by using Background Tasks.

For important updates, use the update(_:alertConfiguration:) function to display an alert on iPhone, iPad, and a paired Apple Watch that tells a person about new Live Activity content.

  • On Apple Watch, the system uses the title and body attributes for the alert.
  • On iPhone and iPad with the Dynamic Island, the system shows the expanded Live Activity in the Dynamic Island.
  • On iPhone and iPad without the Dynamic Island, the system shows the Lock Screen presentation as a banner when your app is not in the foreground. If you app is in the foreground, the system only vibrates without displaying the banner UI.
unc updateActivity(alert:Bool) {
Task {
guard let activity = currentActivity else {
return
}

var alertConfig: AlertConfiguration? = nil
let contentState: LiveActivityAttributes.ContentState = LiveActivityAttributes.ContentState(emoji: "🥰")

if alert {
alertConfig = AlertConfiguration(title: "Emoji Changed", body: "Open the app to check", sound: .default)
}

await activity.update(ActivityContent(state: contentState, staleDate: Date.now + 15, relevanceScore: alert ? 100 : 50), alertConfiguration: alertConfig)
}
}

End a Live Activity

You can end a Live Activity using the end(_:dismissalPolicy:) method of the Activity object you received when you started the Live Activity.

Always include an updated Activity.ContentState to ensure the Live Activity shows the latest and final content update after it ends. This is important because the Live Activity can remain visible on the Lock Screen for some time after it ends.

A Live Activity that ended remains on the Lock Screen until the person removes it or the system removes it automatically. The automatic removal depends on the dismissal policy you provide to the end(_:dismissalPolicy:) function.

There are three kinds of dismissal policies you can use:

  • default

With the default dismissal policy, the Live Activity appears on the Lock Screen for some time after it ends to allow a person to glance at their phone to see the latest information. A person can choose to remove the Live Activity at any time, or the system removes it automatically four hours after it ended.

  • immediate

With the immediate dismissal policy, the Live Activity will be removed from Lock Screen immediately.

  • after(_:)

Alternatively, use after(_:) to specify a date within a four-hour window. The system removes the ended Live Activity after the given date or after four hours from the moment the Live Activity ended — whichever comes first.

func endActivity(dismissTimeInterval: Double?) {
Task {
guard let activity = currentActivity else {
return
}
let finalState = LiveActivityAttributes.ContentState(emoji: "✋✋")
let dismissalPolicy: ActivityUIDismissalPolicy
if let dismissTimeInterval = dismissTimeInterval {
if dismissTimeInterval <= 0 {
dismissalPolicy = .immediate
} else {
dismissalPolicy = .after(.now + dismissTimeInterval)
}
} else {
dismissalPolicy = .default
}

await activity.end(ActivityContent(state: finalState, staleDate: nil), dismissalPolicy: dismissalPolicy)
}
}

https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities