Apple introduced Widgets in iOS 14 and brought a fresh look that changed our phoneâs home screens. The framework evolved through the years, adding a powerful means of keeping users updated with their data.
iOS 17 takes widgets to the next level by introducing interactivity. Users can now interact with your app in a new, innovative way that wasnât possible before.
By making your appâs essential actions available in a widget, your users have a more convenient and engaging way to interact with your app.
In this tutorial, youâll add interactive widgets to the Trask app using SwiftUI.
If youâre thinking about learning SwiftUI, widgetsâ simple views are a great place to start with.
This tutorial covers the following topics.
- What interactive widgets are and how they work.
- How to create interactive widgets with a SwiftUI animation.
- Different types of interactive widgets that you can create.
- Best practices for designing and developing interactive widgets.
Although there are no strict prerequisites, a basic knowledge of SwiftUI and WidgetKit might be helpful. Anyway, donât worry, youâll have a quick recap to start off on the right foot.
Getting Started
Download the starter project by clicking the Download Materials button at the top or bottom of this tutorial. Open the starter project (Trask.xcodeproj) in the Starter folder.
Build and run the project, and you should see the Trask initial screen.
Trask is a general tracker app that tracks different tasks/things/habits during the day, such as the number of glasses of water, medicine, yoga, and so on.
The first time you launch the app, Trask creates some sample data so you can see the different types of tasks you can create.
- Task with multiple steps.
- âTODOâ task with just one step.
When tapping the plus button, the app advances the task, and once it reaches its target, it passes in the done state.
The user can delete tasks by swiping left on them and can add new ones using the button at the bottom of the View.
Recapping WidgetKit
Before you get into the hot topic of this tutorial, familiarize yourself with some basic concepts on WidgetKit to build common terminology for the rest of the tutorial.
Adding an iOS Widget
Trask comes with a static widget to follow the status of a selectable task.
Add an instance of the widget to see how it looks.
- Build and run the project.
- Minimize the app.
- Long press on an empty area of the screen.
- Then tap the + button, search for Trask, and select the widget available.
Youâre now ready to jump into the code structure to see how it works.
Widget Code Structure
The TraskWidgets folder of the starter project contains all the files related to the widget.
-
TaskIntent
is an intent conforming to theWidgetConfigurationIntent
protocol. Here, the intent allows the task selection in the Edit Widget menu. -
TaskStatusWidget
is the actual widget. Four parts compose the widget file. -
TaskTimelineProvider
specifies when iOS should refresh the widget screen. -
TaskEntry
represents the model of the widget view. It contains a date iOS uses to update the widget view with the task item. -
TaskStatusWidgetEntryView
defines the widget view using SwiftUI. It contains a timeline entry as a parameter, and it should lay out the widget based on this parameter value. -
TaskStatusWidget
binds all the parts together within aWidgetConfiguration
. - Finally,
TraskWidgetBundle
declares all the extensionâs widgets.
Data Sharing With The App
As you may see, the widget code is contained in a separate Xcode target, and iOS runs the widget in a process different from the app. This detail might seem subtle, but itâs crucial when considering that the app and the widget need to share the same data. The widget code canât simply call some functions in the app target. Among the different possibilities, Trask uses a UserDefault
store on an App Group container shared between the app and the widget.
Timeline Provider
Timeline is a key concept of Widgets. To preserve battery and system resources, iOS doesnât constantly run your widget. Instead, it asks your timeline provider to generate a series of timeline entries to render your widget and present it at the right time.
Your TaskTimelineProvider
defines three methods.
-
placeholder(in:)
should return some sample data to render the placeholder UI while waiting for the widget to be ready. SwiftUI applies a redaction effect to this view. -
snapshot(for:in:)
provides the data to render the widget in the gallery presented when choosing a widget. -
timeline(for:in:)
is the main method that returns the timeline entries to present at the specified time.
Updating Widgets
As said above, the timeline(for:in:)
returns the array of entries at the specified time, but what happens after the last widget view is presented? Enter the widget update strategy!
When returning the timeline of entries, you also provide one strategy for updating the timeline. You may choose between the three options below.
-
.atEnd
recomputes the timeline after the last date in the timeline passes. -
.after(_:)
specifies approximately when to request a new timeline. -
.never
tells the system to never recompute the timeline. The app will prompt WidgetKit when a new timeline is available.
In our case, the Trask timeline provider returns the .never
policies since there is no need for the widget to update its view. The only way to update the status of a task is through the app when the user taps to step a taskâ¦until the next chapter. :]
Making the Widget Interactive
Wowâ¦that was a long warmup, but now youâre ready to add interaction to the Trask status widget.
Types of Interactivity
Starting with iOS 17, iPadOS 17 and macOS 14, Apple allows two main ways of interactivity with your widget: buttons and toggles.
- Buttons are suitable to represent an action on the widget content.
- Toggles better identify a binary actionable state on/off. Such as our TODO task status.
Note: On a locked device, buttons and toggles are inactive, and iOS doesnât perform actions until the user unlocks his device.
As the first improvement, youâll add a step button to the Trask Status Widget so users can progress their favorite tasks without opening the app.