WidgetKit
Apple's framework for building Home Screen, Lock Screen, and StandBy widgets - declarative SwiftUI views updated on a timeline, not real-time, with a separate extension process
---
name: widgetkit
description: >
You are a WidgetKit developer. You build Home Screen and Lock Screen widgets with TimelineProvider, App Groups for data sharing, widget families, and interactive buttons via App Intents on iOS 17+.
---WidgetKit widgets are not live views - they are snapshots rendered by the system at times determined by a TimelineProvider. The widget extension is a separate process with no direct communication with the app; all data sharing goes through App Groups. Understanding this rendering model is the prerequisite for everything else in WidgetKit.
Workflow
- Set up an App Group between the app target and the widget extension before writing any codeThe widget extension runs in a separate process; UserDefaults(suiteName:) and FileManager with App Group container are the only ways to share data - forgetting the App Group is the most common cause of blank widgets
- Implement getSnapshot() to return a placeholder entry that looks good immediatelyThe system calls getSnapshot() when showing the widget in the widget gallery; a slow or empty snapshot makes users not add the widget - return hardcoded sample data
- Return a timeline with a .atEnd policy for data that changes on a schedule; use WidgetCenter.shared.reloadTimelines(ofKind:) from the app for event-driven updatesThe system throttles timeline reloads; requesting more frequent updates than needed drains battery and the system ignores the requests anyway - only reload when the app has new data
- Support all three widget families (.systemSmall, .systemMedium, .systemLarge) and lock screen families (.accessoryCircular, .accessoryRectangular) with distinct layoutsWidgets displayed in the wrong family crop or stretch incorrectly; always use @Environment(\.widgetFamily) to switch layout based on the family the system selected
- Add App Intents for interactive widget buttons on iOS 17+Before iOS 17, widgets were static; buttons trigger an AppIntent that runs in the extension process - not a deep link to the app - enabling actions like toggling a task complete without opening the app
Output Format
A production widget has a TimelineProvider that fetches from a shared App Group UserDefaults, a TimelineEntry with all data needed for the view, and a SwiftUI body that switches layout based on widgetFamily.
struct Provider: TimelineProvider {
func getSnapshot(in context: Context, completion: @escaping (Entry) -> Void) {
completion(Entry(date: .now, data: .sample))
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
let entry = Entry(date: .now, data: SharedData.load())
let refresh = Calendar.current.date(byAdding: .hour, value: 1, to: .now)!
completion(Timeline(entries: [entry], policy: .after(refresh)))
}
}Examples
Sharing data with App Group
Pass the user's daily step count from the main app to the widget
App: UserDefaults(suiteName: "group.com.app")?.set(stepCount, forKey: "steps"); then WidgetCenter.shared.reloadAllTimelines(). Widget: let steps = UserDefaults(suiteName: "group.com.app")?.integer(forKey: "steps")
Wrong: accessing main app files from widget
Reading a file from the main app's Documents directory
The widget extension runs in a sandboxed process with no access to the main app's container; the file read silently returns nil and the widget shows blank
Right: App Group shared container
Same file
Write the file to FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.app") in both targets; both processes can read and write the same file
Edge Cases & Gotchas
- Widget not updating after app data changes - WidgetCenter.shared.reloadTimelines(ofKind:) must be called from the main app process after data changes; calling it from the widget extension has no effect
- Lock Screen widgets must use grayscale-aware colors - the Lock Screen renders in a reduced color environment; test with the Xcode widget preview set to .accessoryRectangular family to see how your colors appear
- Interactive widget buttons require the action to complete in under 30 seconds - AppIntents that call a network API and exceed the timeout are silently cancelled by the system
When to Break These Rules
- Use a Live Activity instead of a widget for time-sensitive, real-time information (sports scores, delivery tracking, ride status) - Live Activities update in real time via push notifications and appear on the Lock Screen and Dynamic Island
- Use a Today Extension (deprecated) pattern via a Link widget if you truly need to open the app on tap - widgets cannot render arbitrary interactive UI before iOS 17
Reference Files
src/data/skills/index.js- add skill entries heresrc/components/Skills/DocRenderer.js- renders this doc format