Logo for tanaschita.com

How to build a configurable widget with WidgetKit and SwiftUI

Learn how to allow users to customize their widgets.

05 Sep 2022 · 4 min read

In the previous article on building widgets for iOS applications with WidgetKit and SwiftUI, we looked at the basics on how to develop widgets that are not dependent on any user properties.

In this article, we'll dive into widgets that display content depending on some user input like their location, their preferences or any other input that allows users to customize their widget.

Sponsorship logo
Using Proxyman to inspect network traffic
Proxyman is a native debugging proxy that can act as a man-in-the-middle between your application and web server. You can use its powerful toolkit to inspect network calls and debug your application on Mac, iOS Simulator, or remote devices effortlessly.
CLICK TO LEARN MORE

Steps to support configurable widgets

To add configurable properties to a widget, the following steps are required:

  1. Adding a custom intent definition that defines configurable properties.
  2. Configuring the widget by using IntentConfiguration.
  3. Optionally implementing an Intents extension in case the properties depend on dynamic data.

Adding a custom intent definition

As a first step, we add an .intentdefinition file to the Xcode project by selecting File > New File > SiriKit Intent Definition File.

Widget intent definition.
Widget intent definition.

When selecting the intent definition file, Xcode shows an intent definition editor where we can configure parameters that allow users to customize their widgets.

Parameters can be:

  • a static list of choices
  • generated dynamically

To provide a static list of choices, we can choose the Add Enum menu item to create a static enumeration.

Static widget properties.
Static widget properties.

If the choices can very, we can provide dynamic data by generating a new type.

Dynamic widget properties.
Dynamic widget properties.

Adding an Intents Extension

To provide data dynamically, we need to add am Intents extension by following those steps:

  1. Select File > New > Target > Intents extension in Xcode's menu.
  2. Enter a name and select none for Starting Point.
  3. Choose Activate when Xcode prompts about activating the new scheme.
  4. Add the intent we created earlier in the Supported Intents section in the General tab of the extension's target.
Intents Extension.
Intents Extension.
  1. Make sure that the app, the widget extension and the Intents extension all include the .intentdefinition file in the Target Membership section.
Target membership of the .intentdefinition file
Target membership of the `.intentdefinition` file

Implementing an intent handler

When adding an Intent extension, Xcode created a IntentHandler.swift file for us. We'll extend this handler to provide values for the widget's customization. Based on the custom intent definition file, Xcode generates a protocol, SelectCategoryIntentHandling, that the handler must conform to.

class IntentHandler: INExtension, SelectCategoryIntentHandling {
func provideCategoryOptionsCollection(for intent: SelectCategoryIntent, with completion: @escaping (INObjectCollection<NSString>?, Error?) -> Void) {
let categories = WidgetQuotesCategory.allCases.map { QuotesCategory(identifier: $0.identifier, display: $0.title) }
completion((INObjectCollection(items: categories))
}
override func handler(for intent: INIntent) -> Any {
return self
}
}

In the provideCategoryOptionsCollection method, we provide available categories.

Handling custom values

Once users edit a widget and select a category, the next step is to reflect their choice in the widget's content. Instead of a TimelineProvider like we used in the previous article on building widgets for iOS applications with WidgetKit and SwiftUI, we now use an IntentTimelineProvider.

func getTimeline(for configuration: SelectCategoryIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
loadQuoteOfTheDay(for: configuration.category) { quoteOfTheDay in
let entry = ExampleTimelineEntry(date: Date(), quoteOfTheDay: quoteOfTheDay)
let timeline = Timeline(entries: [entry], policy: .after(Date.tomorrow) )
completion(timeline)
}
}

The only difference is that now we get the SelectCategoryIntent passed in as parameter, so we can access the selected category and provide appropriate results.

Sponsorship logo
Using Proxyman to inspect network traffic
Proxyman is a native debugging proxy that can act as a man-in-the-middle between your application and web server. You can use its powerful toolkit to inspect network calls and debug your application on Mac, iOS Simulator, or remote devices effortlessly.
CLICK TO LEARN MORE

Newsletter

Image of a reading marmot
Subscribe

Like to support my work?

Say hi

Related tags

Articles with related topics

widgetkit

swiftui

swift

ios

Building widgets for iOS applications with WidgetKit and SwiftUI

Learn to create widgets to show your app's content on the home screen.

29 Aug 2022 · 7 min read

Latest articles and tips

© 2022 tanaschita.com

Privacy policy

Impressum