Streaming is available in most browsers,
and in the Developer app.
-
Add intelligence to your widgets
Discover how to you can add intelligence to your widgets in Smart Stacks. We'll show you how to use the new Widget Suggestions API in tandem with Smart Rotate to create more valuable widget experiences for people throughout the day. Whether you inform the system of new, timely information or teach the system to learn common patterns, adopting these APIs can help people discover your widget and allows you to influence how the system surfaces content from your app around system spaces.
Resources
Related Videos
WWDC21
-
Download
♪ Bass music playing ♪ ♪ Daniel Li: Welcome to "Add intelligence to your widgets." My name’s Daniel, and I’m an engineer on the Proactive Intelligence team at Apple.
In this session, I’ll show you how your widgets can work with the system to provide intelligent experiences for your users so your widgets can show up right when they need them.
Widgets live in many places.
From the Home screen on iOS to Notification Center on macOS, widgets provide users with information that they can quickly view with just a glance.
With iPadOS 15, widgets come to the iPad Home screen as well.
There are now more places than ever for your widgets to call home.
I’ll first talk about what it means for widgets to be intelligent in the system, and then I’ll show you how you can enable intelligence for your widgets by donating relevant information with the available APIs.
Let’s first go over widget intelligence.
On iPhone and iPad, widgets can be placed together in a Smart Stack, where they can be scrolled through to provide different kinds of information in one place.
When a user edits a stack, in addition to being able to add, remove, or reorder widgets, they can also view and manage the stack’s intelligent behaviors.
iOS 14 introduced Smart Rotate.
Smart Rotate allows the system, at the right time, to automatically scroll to a widget that is already in a stack.
To know when to rotate to a widget, the system uses relevance signals that the widget provides alongside its timeline entries.
It can also rotate to a widget by learning patterns in the user’s behavior.
It does so by receiving donations from the app whenever the user views information corresponding to the widget.
With Smart Rotate, if I have a Weather widget, a Reminders widget, and a Calendar widget in a stack, the Weather widget might tell the system to suggest itself in the morning when I wake up to give me an overview of the day.
At 9:30, the Calendar widget may tell the system that I have a meeting coming up at 10, so my stack might rotate to Calendar.
In the afternoon, one of my reminders has a due date of 3:00 p.m., so the widget might tell the stack to rotate to Reminders.
And in the evening, the Weather widget might tell the system to show me the weather again because it just started raining, so I know to take an umbrella if I go out for dinner.
iOS 15 introduces a new feature called Widget Suggestions, a brand-new way for users to discover your widget and receive proactive and relevant information.
For Smart Stacks with Widget Suggestions enabled, the system can insert a new widget into a stack that doesn’t already contain it.
The system can do this given an explicit donation from your app.
And like with Smart Rotate, the system can also make a Widget Suggestion by learning the user’s behavioral patterns in your app.
With Widget Suggestions, if I only had a Weather widget and a Reminders widget in my stack, I would still see Smart Rotations to these widgets throughout the day.
But since I have that meeting at 10:00, and I don’t have the Calendar widget in my stack, the Calendar app may tell the system to proactively insert the widget into my stack at 9:30 so I don’t forget.
After the widget is no longer relevant, the system will remove it from my stack.
When I finish my evening workout, the system might show me the Fitness widget so I can get a quick summary of my day’s progress in closing my rings.
And afterward, the stack will again return to its original state.
Since apps tell the system to temporarily insert widgets when they’re useful, they can help users discover widgets that they may not even know existed.
Now let’s see how you can adopt both Smart Rotate and Widget Suggestions using the available APIs to donate relevant signals to the system.
When donating, you should consider all the possible information your widget can show and determine which types are more suitable for a suggestion than others.
The end goal when choosing how to make donations to the system is to provide timely, glanceable information with obvious value to the user, so you should consider highlighting situations that users may find more interesting than others.
A weather widget, for example, could consider imminent rainfall to be a situation suitable for suggestion as opposed to, say, a point in time where only the temperature has changed by an unremarkable amount.
You can adopt Smart Rotate and Widget Suggestions for your widgets in three separate ways.
First, your app can donate an INRelevantShortcut to tell the system when it should create a Widget Suggestion for one of your widgets.
Second, your widget can provide relevance scores through TimelineEntryRelevance to enable Smart Rotations when the widget is in a stack.
And third, if your widget is configurable, when your app donates the configuration intent using INInteraction, the system can learn the user’s behavioral patterns to promote your widget through both Smart Rotations and Widget Suggestions.
Each of these APIs enhances your widget’s presence in powerful ways but they all work a bit differently, so let’s break it down.
Let’s start with donating INRelevantShortcuts for Widget Suggestions.
Whether you use an intent to configure your widget or not, when your app becomes aware of a highly relevant situation to show your widget to the user, donating an INRelevantShortcut allows the system to proactively insert your widget into one of the user’s stacks if they don’t already have your widget on their Home screen.
A relevant shortcut can either specify a period of time that it’s relevant or have the system determine when to insert the widget based on the user’s behavioral patterns.
INRelevantShortcut supports both static and intent-configured widgets but the donation will look a little different for each configuration type, so let’s take a look at how to adopt for both types.
If your widget supports StaticConfiguration, simply create a new INRelevantShortcut without an intent and set the widgetKind property to the widget’s kind string.
This tells the system which of your app’s widgets to suggest.
Then, optionally set the relevanceProviders property to an array of INRelevanceProviders.
This tells the system when to suggest your widget.
I’ll explain this in more detail in a bit.
If your widget supports IntentConfiguration, create an instance of the intent used for configuration with the necessary parameters.
Then create the INRelevantShortcut by passing in the intent as an INShortcut.
And of course, set the widgetKind and relevanceProviders properties just as you would for a static widget.
To donate a relevant shortcut, include it in an array of relevant shortcuts to set in the default INRelevantShortcutStore.
To update the donated shortcuts, you can replace the array of relevant shortcuts in the store to a new array.
This means that you can invalidate a previously donated relevant shortcut by omitting it in the new array.
And donating a relevant shortcut that contains an intent can also surface the intent on the Siri watch face.
To tell the system when it can insert your widget, set an array of INRelevanceProviders on your relevant shortcut.
There are two options here.
First, you can use INDateRelevanceProvider to supply start and end dates for a fixed, known time period of relevance, such as an upcoming event or breaking news.
You can even use multiple providers to specify more than one relevant time period.
Second, you can also choose to supply an empty array of relevance providers to tell the system that your widget has something new to show, and the system will choose a time to suggest the widget based on when the user typically uses your app.
All other relevance providers support INRelevantShortcuts for Siri watch face and are not supported by Widget Suggestions.
Now I’ll walk you through what an adoption might look like using an example app.
We built an app called Cards that lets you view the transactions and balances on all of your credit cards and includes a widget that shows recent purchases.
Let’s see how we can enable Widget Suggestions for the Recent Purchases widget by donating INRelevantShortcuts in our app.
We want to suggest the widget after a recent purchase has just been made on one of our cards.
Since the widget is configured with an intent, in order to allow Widget Suggestions, we’re first going to make sure that our intent supports donations in our intent definition file.
Here’s our intent editor for our ViewRecentPurchases intent, which tells our widget which credit card to show and which merchant category to filter the purchases by.
We want to make sure that "Intent is eligible for Siri Suggestions" is checked.
This enables suggestions for our widget on the Home screen as well as suggestions in other places around iOS if we choose to donate them, but we’ll get to that later with INInteractions.
Then we need to create a parameter combination that includes the necessary parameters to form the intent.
Since we need both the credit card and the merchant category to configure our widget, we’ll create a supported combination with both card and category parameters.
Now we can create the intent in our code.
In this code, our app was just notified that the user has just made a purchase.
To make a donation, we start by creating an empty array of INRelevantShortcuts, in case we want to donate multiple shortcuts.
We initialize the intent that we used to configure the Recent Purchases widget, and set any parameters that we might need.
Here, we want to set the card parameter on the intent to a card object referring to the card that the recent purchase was made on, and set the category to all to have the widget show all purchases on that card.
Then we create an INShortcut with the intent and create an INRelevantShortcut with it.
We set the shortcut role to be the information shortcut role, since the user views information in the widget, and set the widgetKind.
We provide an INDateRelevanceProvider starting now and ending in 30 minutes, or 1800 seconds, so that the system can remove it 30 minutes right after the purchase was made.
Then we add the shortcut to our array of relevantShortcuts.
Finally, we donate the shortcut by setting our relevantShortcuts array in the default INRelevantShortcutStore and handle any errors in the completion handler.
Now whenever a recent purchase is made, the system can make a Widget Suggestion with the widget by proactively inserting it into a Smart Stack.
Now let’s talk about TimelineEntryRelevance.
When your widget provides timelines to WidgetKit, you can specify the relevance of your widget by providing TimelineEntryRelevance in each timeline entry.
Annotating an entry with relevance indicates how worthy the widget is of a Smart Rotation at the time of the entry.
This worthiness of rotation is relative to all other entries in the timelines that the widget provides, so you should determine which entries may be more relevant to the user than others.
TimelineEntryRelevance is an optional struct property on TimelineEntry, along with the date of the entry.
The relevance struct contains a score and duration.
The struct’s float score represents how relevant this entry is compared to others.
The higher the score, the more likely the system is to rotate to the widget.
When determining what scores to return, know that positive scores indicate timeline entries that are eligible for Smart Rotate.
A score of zero tells the system that the widget should not be rotated to, such as when the widget doesn’t have any information to show and rotating to the widget would not be a positive experience for the user.
Remember that the significance of any provided score is relative to others in the widget’s timelines.
Whatever scaling you decide to use for your relevance scores, just make sure that it’s consistent across the timelines you return.
We’ll go over an example in a just a bit.
The duration in the relevance struct specifies how long the relevance score is valid.
The system uses this, along with the entry’s date, to determine the valid timeframe in which to rotate the stack to the widget.
After the duration passes, the system will treat the score as zero until relevance is provided again in a future entry.
You can specify a duration of zero to keep the score valid until the next entry that provides relevance.
Let’s take a look at what this looks like in code.
Here we have our CardRecentPurchasesEntry struct, which conforms to the TimelineEntry protocol.
Since the protocol requires an optional relevance property, we can declare the relevance property in our struct.
Now when we create the timeline entry, we can attach a TimelineEntryRelevance instance to our entries when we create our timeline.
Now let’s work through what a timeline might look like for the Recent Purchases widget and see some examples for scores we can provide for our timeline relevance.
Here’s an example timeline that the Recent Purchases widget might return throughout a day.
We might think, perhaps, that a user cares more about purchases that involve higher spending.
We can then make our scores for our purchases scale linearly with the amount spent.
Let’s leave the relevance durations as 0 for now.
At 8:15, we would return a score of 0, since there’s no purchases to show.
At 9:41, our 52 dollar and 60 cent purchase means that our score would be 52.6, and so on.
Plotting it on a graph, we see that our shopping spree has a relatively high score compared to other scores, so the system will prioritize rotating to our widget more with this entry than with other entries.
The other entries still have positive scores, so the system could still rotate to the widget at those times, though with a lower priority.
And since we set our durations to 0, the relevance scores stay valid until the next timeline entry with a relevance update.
We could just as well have chosen another scoring mechanism.
Let’s say we return a score of 50 if the purchase is above 50 dollars, and 1 for purchases below.
Once again, we return 0 if there’s no purchases at all.
For our grocery and shopping purchases we returned 50, and for our spending at the Soup Diner and the movie theater, we return 1.
With the score graph, we can see that the score rises to 50 at 9:41 and stays there until 6:52.
Since 50 is the highest possible score in our timeline, the system is most likely to show our widget at this time.
Since our widget shows recent purchases, we don’t want the system to rotate to our widget with purchases made a long time ago.
So let’s only allow the system to rotate to our widget within the 30 minutes after the purchase was made.
To achieve this, we can use the duration property on the relevance struct.
We set a relevance duration of 30 minutes, or 1800 seconds, for each entry.
By setting the duration of relevance to be half an hour, we’re telling the system to mark the entries as eligible for rotation for half an hour, and then mark them as ineligible for rotation afterward.
In these examples, we’re basing our score off of the purchase amount, but really, timeline relevance scores can be whatever you like.
We could have also used which credit card it is, where the purchase was made, when the purchase was made, or something else entirely.
There’s no one right way to do it.
Now onto donating INInteractions.
In your app, you can create and donate an intent used to configure your widget to enable both Smart Rotations and Widget Suggestions through INInteractions.
An app donates an INInteraction every time the user views information in the app corresponding to information that the widget shows.
Each donation is a data point for the system’s behavioral model that learns when the user tends to view information in the app.
Whenever the model predicts that the user may want to perform the interaction again, the system creates a suggestion for the intent.
And as long as the donated intent is the same one used to configure your widget, the system will automatically produce both Smart Rotations and Widget Suggestions from the prediction, along with other suggestions around the system.
To get going with donating INInteraction in the Cards project, just as with donating INRelevantShortcuts, we want to make sure that our intent is eligible for suggestions, and create a supported combination with the parameters necessary to configure our widget.
Here, also make sure to eventually design the suggestion UI for when your donation shows up in other locations in the system.
In the Cards app, whenever our user views all of the recent purchases for a card, we can donate our widget’s intent to let the system know that the user is currently interested in the information that our widget shows.
First, we initialize our intent.
We set the card parameter to the card that the user is currently viewing, and set the category to be all, since the user is currently viewing all transactions for this card.
Then we’ll wrap our intent with an INInteraction and call the donate method.
Now the system will know that the user has just looked at the recent purchases for this card in our app.
After a while, the system can learn when the user tends to do this, and suggest the matching widget at the most opportune times.
Not only do the INInteraction donations you make enable Smart Rotations and Widget Suggestions, but they also allow the system to show your intent as a Siri Suggestion on the Lock Screen, in Spotlight, and in the Siri Shortcut Suggestions widget.
This is true even if your widget doesn’t adopt intents.
To learn more about what donating INInteractions can do for your app and getting even more out of the on-device intelligence, be sure to check out the talk, “Donate intents and expand your app’s presence.” So, INRelevantShortcut, TimelineEntryRelevance, and INInteraction are the three ways in which you can support Smart Rotate and Widget Suggestions for your widgets.
Due to other donations in the system, along with performance considerations, a donation is not a guarantee that your widget will be surfaced.
But by adopting these APIs in a cohesive manner, you can work with the system to give your widget the best shot possible at being shown.
While developing, it may also be helpful to bypass the system’s limitations for surfacing widgets by turning on WidgetKit Developer Mode, found in Developer Settings in the Settings app.
So to wrap up, Widget Suggestions are a brand-new way for your widgets to proactively show up right when your users need them, who may not even know they existed.
And for your widgets already added to a stack, Smart Rotate lets them be a reliable and dependable part of your user’s day.
By working with the system and leveraging on-device intelligence, these features allow your widget to be more discoverable and more proactive, and contribute to an intelligent Home screen experience.
We’re incredibly excited to see how you add intelligence to your widgets.
Thank you and have a wonderful WWDC.
♪
-
-
9:14 - Donate INRelevantShortcuts for Widget Suggestions
// Donate INRelevantShortcut for Widget Suggestions in app // User has just made a purchase var relevantShortcuts: [INRelevantShortcut] = [] let intent = ViewRecentPurchasesIntent() intent.card = Card(identifier: card.identifier) intent.category = .all if let shortcut = INShortcut(intent: intent) { let relevantShortcut = INRelevantShortcut(shortcut: shortcut) relevantShortcut.shortcutRole = .information relevantShortcut.widgetKind = “CardRecentPurchasesWidget” let dateProvider = INDateRelevanceProvider(start: Date(), end: Date(timeIntervalSinceNow: 1800)) relevantShortcut.relevanceProviders = [dateProvider] relevantShortcuts.append(relevantShortcut) } INRelevantShortcutStore.default.setRelevantShortcuts(relevantShortcuts) { (error) in if let error = error { print("Failed to set relevant shortcuts. \(error))") } else { print("Relevant shortcuts set.") } }
-
12:35 - Adopting TimelineEntryRelevance for Smart Rotate
// Appending TimelineEntryRelevance to a TimelineEntry in widget extension for Smart Rotate struct CardRecentPurchasesEntry: TimelineEntry { let date: Date let relevance: TimelineEntryRelevance? let card: IntentCard? let category: PurchaseCategory } let relevance = TimelineEntryRelevance(score: 16.29, duration: 1800) let entry = CardRecentPurchasesEntry(date: Date(), relevance: relevance, card: card, category: category)
-
17:01 - Donate INIntents through INInteraction for Widget Suggestions and Smart Rotations
// Donate INIntent in a card's purchases list in the app .onAppear { let intent = ViewRecentPurchasesIntent() intent.card = Card(identifier: card.id.uuidString, displayString: card.name) intent.category = .all let interaction = INInteraction(intent: intent, response: nil) interaction.donate { error in if let error = error { print(error.localizedDescription) } } }
-
-
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.