| name | ios-widgets-and-live-activities |
| description | iOS home/lock-screen widgets and Live Activities applied — WidgetKit (Widget, TimelineProvider, TimelineEntry, reload policy + refresh budget), the system & accessory widget families, interactive widgets via App Intents, ActivityKit Live Activities (lock-screen + Dynamic Island compact/minimal/expanded, push updates, 8/12-hour limits), Control Center & Lock Screen controls (iOS 18), StandBy, iOS 26 Liquid Glass widget rendering + tinting, and the iOS 27 / WWDC 2026 Liquid Glass v2 retune + user transparency slider + Live Activities on Mac/CarPlay, with the concrete WidgetKit/ActivityKit SwiftUI APIs. Use when designing or building a home-screen widget, a Lock Screen accessory, a Control Center control, or a Live Activity / Dynamic Island for a native iOS app. |
| tags | ["ios","widgets","widgetkit","live-activities","dynamic-island","swiftui","hig"] |
iOS widgets & Live Activities — glanceable, timely, deep-linked
Source: Apple HIG Widgets + Live Activities, developer.apple.com WidgetKit / ActivityKit / App Intents references, WWDC23 Bring widgets to new places / Design dynamic Live Activities, WWDC24 Extend your app's controls across the system, WWDC25 WidgetKit foundations + Live Activities essentials + iOS 26 Liquid Glass widget rendering, and WWDC26 / iOS 27 (Liquid Glass v2 retune + system transparency slider, the Xcode 27 WidgetKit: Implementing Liquid Glass Design notes, Dynamic-Island Siri, Live Activities on Mac/CarPlay) — cross-checked across multiple research passes (June 2026). Currency: iOS 26 is the shipping baseline; iOS 27 (WWDC 2026) is the current cycle — iOS 27 refines Liquid Glass rather than reinventing WidgetKit, so most of this skill carries forward; §3 and §13 flag what's new. Apple's frame: a widget shows timely, glanceable info and deep-links into the app; a Live Activity tracks a live, time-bounded event. Both are SwiftUI-only, non-scrolling, no video.
1. WidgetKit basics
A widget is a SwiftUI extension whose state is driven by a timeline of dated entries the system renders on its own schedule — your code does not run when the widget is on screen.
Widget — the declaration. Its body returns a configuration: StaticConfiguration (no user options) or AppIntentConfiguration (user-configurable via an App Intent, iOS 17+; replaced the old Intents-Extension flow).
TimelineEntry — one dated snapshot (let date: Date + your data).
TimelineProvider (or AppIntentTimelineProvider) — supplies placeholder (redacted first paint), snapshot (Gallery/transient), and getTimeline → a Timeline(entries:policy:).
- Reload policy —
.atEnd (reload after the last entry), .after(date) (reload at a date), .never (only reloadTimelines/reloadAllTimelines from the app, e.g. on a data change).
- Refresh budget — the system meters background reloads. Practical guidance (not contractual): ~40–70 reloads/day, roughly every 15–60 min; a visible, frequently-viewed widget gets more. Never assume real-time — push the entries you can predict; reload from the app on real changes. Verify-at-source flag: the 40–70 number is widely cited but Apple does not publish a fixed figure (and WWDC 2026 / iOS 27 did not change this); treat as an order-of-magnitude budget, not a guarantee.
2. Widget families (sizes & surfaces)
Set with .supportedFamilies([...]). Render the right layout per widgetFamily (an @Environment value) — never one layout stretched.
WidgetFamily | Surface | Use for |
|---|
.systemSmall | Home Screen / Today / StandBy | one metric + one tap target |
.systemMedium | Home Screen | a metric + a small chart or 2–3 rows |
.systemLarge | Home Screen | richer summary, small list |
.systemExtraLarge | iPad Home Screen only | wide dashboard |
.accessoryCircular | Lock Screen · StandBy · Watch | a gauge/ring or one glyph+value |
.accessoryRectangular | Lock Screen · StandBy · Watch | 2–3 lines of text or a tiny chart |
.accessoryInline | Lock Screen (above clock) · Watch | one line of text + an SF Symbol |
Exact point sizes vary per device — read Apple Design Resources / measure on-device; don't hard-code per-model pixels. Accessory families are monochrome/vibrant — design for a single tint, use AccessoryWidgetBackground() for the standard translucent backing, not a custom fill.
3. Liquid Glass widget rendering (iOS 26 baseline → iOS 27 v2)
On iOS 26 the Home Screen composites widgets onto Liquid Glass, and the user picks an appearance — Default, Dark, Clear (transparent glass), or Tint (monochrome, tinted to a chosen hue).
- Provide a
containerBackground(for: .widget) — the system masks/inset-clips it and, in accented/tinted modes, may replace it with glass. A widget with no containerBackground looks broken in the Smart Stack and StandBy.
- Read the mode and adapt:
@Environment(\.widgetRenderingMode) is .fullColor, .accented, or .vibrant. In .accented/tinted modes the system flattens content toward a tint (often white); design so the layout reads with color removed.
- Images: tag with
.widgetAccentedRenderingMode(_:) — .fullColor (keep media in color), .accented / .accentedDesaturated / .desaturated (let the system tint it). Put the brand color in the content, not a background that glass will discard.
iOS 27 (WWDC 2026) — Liquid Glass v2. iOS 27 refines, doesn't replace, the above. Apple retuned the material to diffuse complex content behind it more effectively, with more depth and separation (the more-dimensional look) and a system-wide transparency slider the user drags from near-clear to fully frosted/tinted — so your widget's legible-contrast budget is now user-variable, not fixed. Design for the worst case: readable at maximum clarity and maximum tint. The widgetRenderingMode modes, containerBackground, and accented-image rules are unchanged.
- Verify-at-source flag (new iOS 27 WidgetKit glass APIs). A community-extracted Xcode 27: WidgetKit — Implementing Liquid Glass Design note lists new modifiers —
glassEffect() / glassEffect(_:in:) (e.g. .rect(cornerRadius:)), GlassEffectContainer(spacing:), glassEffectUnion(id:namespace:), widgetAccentable(_:) (primary vs accent groups), containerBackgroundRemovable(_:), widgetTexture(_:) (.glass / .paper), supportedMountingStyles(_:) (.recessed / .elevated), and a .glass button style. Not confirmed against Apple's own published WidgetKit reference — confirm each identifier before relying on it.
4. Interactive widgets via App Intents (iOS 17+)
Widgets can contain Button and Toggle controls — but only ones bound to an AppIntent. No arbitrary closures run in the widget process; SwiftUI won't execute a closure or mutate a binding there. The intent runs in the background, then WidgetKit reloads the timeline to show the new state. Good for: log a quick item, toggle a state, advance a step. Anything richer → deep-link into the app (§7).
struct LogWaterIntent: AppIntent {
static var title: LocalizedStringResource = "Log Water"
func perform() async throws -> some IntentResult {
await Store.shared.logWater(250); return .result()
}
}
Button(intent: LogWaterIntent()) { Label("+250 ml", systemImage: "drop.fill") }
5. Live Activities (ActivityKit)
A Live Activity tracks one live, user-initiated, time-bounded event (a timer, a delivery, a workout) on the Lock Screen and in the Dynamic Island. Not for ambient/marketing content — that's a notification or a widget.
ActivityAttributes = static identity (set once); its nested ContentState = the changing values.
- Start:
Activity.request(attributes:content:pushType:) (foreground). Update: activity.update(_:) with new ActivityContent (optional AlertConfiguration to surface a banner + haptic). End: activity.end(_:dismissalPolicy:) (.default / .immediate / .after(date)).
- Remote updates: request with
pushType: .token, then your server pushes APNs liveactivity payloads. High-rate updates need the Info.plist key NSSupportsLiveActivitiesFrequentUpdates (and NSSupportsLiveActivities to enable the feature at all). Verify-at-source flag: an iOS 26 WidgetPushHandler / channel-push path is reported in WWDC25 coverage and is not contradicted by WWDC 2026 material — still single-source; confirm the identifier before relying on it.
- Limits: active for up to 8 hours (system ends it after that unless your app ends it sooner; a
staleDate marks content outdated earlier). After it ends it stays on the Lock Screen up to 4 more hours → 12-hour max visibility. The Dynamic Island clears immediately on end.
supplementalActivityFamilies lets the same activity render in the Apple Watch Smart Stack.
- iOS 27 (WWDC 2026) — new render surfaces. The same Live Activity now also surfaces on Mac (menu-bar / Dynamic-Island-style presentation) and in CarPlay, and Gemini-powered Siri responses/actions render in the Dynamic Island (iPhone 14 Pro+) — so your compact/expanded regions can be pre-empted by Siri. Build region-agnostic content; don't hard-code phone-only geometry. Verify-at-source flag: the Mac/CarPlay surfaces are from WWDC 2026 keynote/press coverage, not yet a published ActivityKit reference — confirm the exact opt-in API before relying on it.
6. Dynamic Island regions
One DynamicIsland { } builder declares every presentation; the system chooses which to show.
- Compact —
compactLeading + compactTrailing (this app is the only active one).
- Minimal —
minimal (a tiny capsule/circle, shown when several activities compete).
- Expanded (long-press) —
DynamicIslandExpandedRegion(.leading / .trailing / .center / .bottom).
ActivityConfiguration(for: FastAttributes.self) { ctx in
FastLockScreenView(state: ctx.state)
.activityBackgroundTint(.black.opacity(0.25))
} dynamicIsland: { ctx in
DynamicIsland {
DynamicIslandExpandedRegion(.leading) { Image(systemName: "timer") }
DynamicIslandExpandedRegion(.trailing) { Text(ctx.state.kcalLeft, format: .number).monospacedDigit() }
DynamicIslandExpandedRegion(.center) { Text("Fasting") }
DynamicIslandExpandedRegion(.bottom) { ProgressView(value: ctx.state.fraction) }
} compactLeading: { Image(systemName: "timer") }
compactTrailing: { Text(ctx.state.endDate, style: .timer).monospacedDigit() }
minimal: { Image(systemName: "timer") }
.widgetURL(URL(string: "greg://today"))
}
Keep compact to a glyph + one short value; the timer Text(style:)/timerInterval: styles count down without reloads.
7. Tap-through & deep links
Widgets don't navigate inside themselves.
- Whole
systemSmall → one destination: .widgetURL(url).
- Medium/Large → per-element targets with
Link(destination:) (plus an optional widgetURL fallback).
- Handle the URL in
onOpenURL / scene routing. Tappable regions must look tappable and be comfortably sized — no dense grids of tiny hit areas.
8. Control Center & Lock Screen controls (iOS 18+)
Controls extend an app into Control Center, the Lock Screen, and the Action button — also WidgetKit, but a distinct type.
ControlWidget → ControlWidgetButton (one-shot action) or ControlWidgetToggle (binary state). Both run via App Intents; a toggle pairs with a SetValueIntent (Bool), and live state comes from a ControlValueProvider.
struct StartFastControl: ControlWidget {
var body: some ControlWidgetConfiguration {
StaticControlConfiguration(kind: "studio.theft.greg.fast") {
ControlWidgetToggle("Fast", isOn: FastState.isActive,
action: ToggleFastIntent()) { on in
Label(on ? "Fasting" : "Start fast", systemImage: "timer")
}
}
}
}
A control is a single tap target — no layout. Reflect real app state so it never lies.
9. StandBy & other surfaces
- StandBy (charging, landscape) shows
.systemSmall and accessory families large-and-glanceable; ensure they read at distance and respect the night/red dim.
- Apple Watch Smart Stack reuses accessory families (
containerBackground gives the dark material there).
- Lock Screen = accessory families only; Home Screen = system families.
10. Widget vs Live Activity — which is right
- Widget — persistent, ambient, glanceable; refreshes on a budget. "What's my state right now."
- Live Activity — a specific running event with a start and an end, updated frequently/by push, gone when done.
- Neither for: long lists, scrolling, video, free-form chat, or anything needing immediate per-second freshness without push.
11. Mistakes that read as cheap / amateur
One layout scaled across families instead of a per-widgetFamily design · no containerBackground (broken-looking in StandBy/Smart Stack/iOS 26 glass) · color carried only in a background that tinted/accented mode discards · non-tabular digits that jitter on a timer (use .monospacedDigit() / Text(style: .timer)) · trying to run real logic on tap instead of an App Intent or deep link · tiny crowded tap targets · expecting real-time reloads and burning the budget · a Live Activity used for ambient content (should be a widget/notification) · accessory widgets designed in full color that vanish in monochrome.
12. Greg application — roadmap F21 (home-screen widgets)
Greg = native SwiftUI iOS 26 nutrition app; cobalt #2C50F0 + Figtree, hero metric "calories left" + a ring; Progress has streak + weight trend. Concrete build:
systemSmall "Calories left" — big rounded .monospacedDigit() number over a thin macro ring; containerBackground(.fill.tertiary, for: .widget); whole-widget widgetURL("greg://log"). systemMedium — same ring + a 3-bar protein/carbs/fat strip + a Link "Log" button (or an App Intent quick-add).
- Lock Screen
.accessoryCircular — a Gauge/ring of fraction-of-budget-used (monochrome-safe, no color-only meaning). .accessoryRectangular — "1,840 left · P/C/F" two lines.
- Live Activity + Dynamic Island for an active fast or meal-prep timer: Lock Screen card with the countdown + calorie-progress ring; compact =
timer glyph + Text(style: .timer); expanded .bottom = ProgressView(value: fraction); widgetURL("greg://today"). End on completion with .default.
- Color rule: put cobalt in the content (ring stroke, number), expect Tint/accented modes to flatten it — never rely on a cobalt background. Tag any food image
.widgetAccentedRenderingMode(.fullColor). Keep all of it inside §11's amateur-tells list.
- iOS 27 check: the v2 transparency slider makes the glass behind the widget user-variable — verify the "calories left" number and ring stay legible at both maximum clarity and full tint, not just the default. Live Activity timer must read on the Lock Screen, in the Dynamic Island, and on the Mac menu bar / CarPlay (§5) without phone-only geometry.
Pairs with: ios-components (buttons/controls), ios-color-and-materials (tint/glass/vibrancy), ios-charts-and-data-visualization (the ring/gauge), ios26-hig-patterns (Liquid Glass system behavior).