Intent Configuration lets the user personalise the widget
by long pressing it and tapping Edit Widget.
Instead of showing the same thing to everyone,
each user can configure what they want to see.
Static vs Intent Configuration
StaticConfiguration → same for everyone, no customisation
AppIntentConfiguration → user can configure it from home screen
From the Project — 3 Parts
1. FocusWidgetIntent → defines what can be configured
2. SessionOption → the options the user can pick from
3. AppIntentConfiguration → wires it all into the widget
Part 1 — The Intent
struct FocusWidgetIntent: WidgetConfigurationIntent {
static var title: LocalizedStringResource = "Focus Session"
static var description = IntentDescription("Choose which session to display.")
@Parameter(title: "Session")
var session: SessionOption?
}
WidgetConfigurationIntent → tells system this is a widget intent
@Parameter → the thing the user can actually change
session: SessionOption? → optional because user may not have picked yet
Part 2 — The Options
enum SessionOption: String, AppEnum {
case focus, breakTime, plan, learn, review
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Session"
static var caseDisplayRepresentations: [SessionOption: DisplayRepresentation] = [
.focus: "Focus",
.breakTime: "Break",
.plan: "Plan",
.learn: "Learn",
.review: "Review"
]
}
AppEnum → tells system to show these as a list of options in the editor
typeDisplayRepresentation → label for the type e.g. "Session"
caseDisplayRepresentations → human readable label for each option
Part 3 — Wiring it in
struct SampleWidget: Widget {
var body: some WidgetConfiguration {
AppIntentConfiguration(kind: kind, intent: FocusWidgetIntent.self, provider: Provider()) { entry in
SampleWidgetEntryView(entry: entry)
}
}
}
StaticConfiguration → replaced with AppIntentConfiguration
intent: FocusWidgetIntent.self → tells system which intent to use
everything else stays the same
How the Intent Flows into the Provider
func timeline(for configuration: FocusWidgetIntent, in context: Context) async -> Timeline<SimpleEntry> {
let index = selectedIndex(for: configuration.session)
let entry = makeEntry(from: FocusState.all[index], index: index, date: Date())
return Timeline(entries: [entry], policy: .never)
}
configuration.session → whatever the user picked in the editor
selectedIndex() → maps it to an array index
makeEntry() → builds the entry from that index
The Full Flow
User long presses widget
→ taps Edit Widget
→ sees "Session" picker (from @Parameter)
→ picks "Break"
→ system calls timeline() with configuration.session = .breakTime
→ widget updates to show Break / Rest / Recharge
selectedIndex — Mapping Intent to Data