I was looking into configurable control widgets and realised that the code mentioned in the WWDC session and documentation about it is partial.
If you have found this blog post, I have got good news for you. You can save yourself some time and examine the code for configurable control widgets created step by step.
No more guesswork, just straight-up, working code.
If you are creating the control widget from scratch, here is the first version if this series:

One method needed from the ControlWidgetConfiguration
is the ability to prompt for user configuration.
This method shows a configuration UI as soon as the widget is added.
App Intents
Here is where I got stuck to have two different intents: SelectTimerIntent
and ToggleTimerIntent
. Let's break them down.
SelectTimerIntent
This intent is used to choose which timer we want to work with. It is a ControlConfigurationIntent
, which means it is used to configure the widget. The timer
parameter allows the user to select a specific timer.
ToggleTimerIntent
This intent is used to actually start or stop the timer. It is a SetValueIntent
, which is perfect for toggling states. The value
parameter represents whether the timer should be running or not, and the timer
parameter specifies which timer we are controlling.
The SelectTimerIntent
handles configuration, while ToggleTimerIntent
handles the action.
Building the Timer Entity
Before we can create the widget, we need to define what a timer is. Here is an example of a Timer
struct:
This struct conforms to both Identifiable
(so we can easily distinguish between timers) and AppEntity
(so we can use it with the intents).
We also need a way to query the timers for the configuration:
This query allows to fetch specific timers by ID and suggest all available timers to the user.
Managing Timer State
To keep track of whether a timer is running or not, we use a simple TimerState
struct:
And to manage the timers, we have a TimerManager
singleton:
In a real timer app, you would replace these placeholder implementations with actual timer logic. Yeah, I know. Fill in the blanks!
The TimerToggle
Widget
Finally, here is the TimerToggle
widget:
We use AppIntentControlConfiguration
with the ConfigurableProvider
(we will look at this next). The configuration takes a closure that receives a TimerState
and returns a ControlWidgetToggle
.
The toggle uses the timer's name, its running state, and a ToggleTimerIntent
for its action. We provide a custom label that shows "Running" or "Stopped" along with a timer icon.
We set a display name and description for our widget and call promptsForUserConfiguration()
to ensure the user sets up the widget.
Configuring the Provider
The ConfigurableProvider
is where we handle the selection of the timer:
This provider uses the SelectTimerIntent
to get the current timer and its state. It provides both a preview value (for when the widget is being configured) and the current value (for when the widget is actually in use).
Here is how it looks:
Moving Forward
Putting in the missing code from the documentation, we have created a configurable Control Widget for iOS 18. You can play around with the logic for each intent, and update the control accordingly.
Happy widgeting!