2024 App Services
WWDC24 · 10 min · App Services
Bring your Live Activity to Apple Watch
Bring Live Activities into the Smart Stack on Apple Watch with iOS 18 and watchOS 11. We’ll cover how Live Activities are presented on Apple Watch, as well as how you can enhance their presentation for the Smart Stack. We’ll also explore additional considerations to ensure Live Activities on Apple Watch always present up-to-date information.
Watch at developer.apple.com ↗Chapters
Code shown on screen · 7 snippets
Existing Live Activity views
struct DeliveryLiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(
for: DeliveryActivityAttributes.self
) { context in
DeliveryActivityContent(context: context)
} dynamicIsland: { context in
DynamicIsland {
DynamicIslandExpandedRegion(.leading) {
DeliveryExpandedLeadingView(context: context)
}
DynamicIslandExpandedRegion(.trailing) {
DeliveryExpandedTrailingView(context: context)
}
DynamicIslandExpandedRegion(.bottom) {
DeliveryExpandedBottomView(context: context)
}
} compactLeading: {
DeliveryCompactLeading(context: context)
} compactTrailing: {
DeliveryCompactTrailing(context: context)
} minimal: {
DeliveryMinimal(context: context)
}
}
}
} Preview Live Activities with Xcode Previews
extension DeliveryActivityAttributes.ContentState {
static var shippedOrder: DeliveryActivityAttributes.ContentState {
.init(
status: .shipped,
courierName: "Johnny"
)
}
static var packedOrder: DeliveryActivityAttributes.ContentState {
.init(
status: .packed,
courierName: "Contacting Courier...")
}
}
#Preview(
"Dynamic Island Compact",
as: .dynamicIsland(.compact),
using: DeliveryActivityAttributes.preview
) {
DeliveryLiveActivity()
} contentStates: {
DeliveryActivityAttributes.ContentState.packedOrder
DeliveryActivityAttributes.ContentState.shippedOrder
} Add .supplementalActivityFamilies to indicate support for the Smart Stack
struct DeliveryLiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(
for: DeliveryActivityAttributes.self
) { context in
DeliveryActivityContent(context: context)
} dynamicIsland: { context in
DynamicIsland {
DynamicIslandExpandedRegion(.leading) {
DeliveryExpandedLeadingView(context: context)
}
DynamicIslandExpandedRegion(.trailing) {
DeliveryExpandedTrailingView(context: context)
}
DynamicIslandExpandedRegion(.bottom) {
DeliveryExpandedBottomView(context: context)
}
} compactLeading: {
DeliveryCompactLeading(context: context)
} compactTrailing: {
DeliveryCompactTrailing(context: context)
} minimal: {
DeliveryMinimal(context: context)
}
}
.supplementalActivityFamilies([.small])
}
} Customize view layout for the small activity family
struct DeliveryActivityContent: View {
(\.activityFamily) var activityFamily
var context: ActivityViewContext<DeliveryActivityAttributes>
var body: some View {
switch activityFamily {
case .small:
DeliverySmallContent(context: context)
case .medium:
DeliveryMediumContent(context: context)
@unknown default:
DeliveryMediumContent(context: context)
}
}
} Preview customized layouts for the Smart Stack
#Preview("Content", as: .content, using: DeliveryActivityAttributes.preview) {
DeliveryLiveActivity()
} contentStates: {
DeliveryActivityAttributes.ContentState.packedOrder
DeliveryActivityAttributes.ContentState.shippedOrder
} Use isLuminanceReduced to remove bright elements with Always On Display
struct DeliveryGauge: View {
(\.isLuminanceReduced) private var isLuminanceReduced
var context: ActivityViewContext<DeliveryActivityAttributes>
var body: some View {
Gauge(value: context.state.progressPercent) {
GaugeLabel(context: context)
}
.tint(isLuminanceReduced ? .gaugeDim : .gauge)
}
} For Live Activities with a light appearance, use a light preferredColorScheme
struct DeliveryActivityContent: View {
(\.activityFamily) var activityFamily
var context: ActivityViewContext<DeliveryActivityAttributes>
var body: some View {
switch activityFamily {
case .small:
DeliverySmallContent(context: context)
.preferredColorScheme(.light)
case .medium:
DeliveryMediumContent(context: context)
@unknown default:
DeliveryMediumContent(context: context)
}
}
}