Create Widget in iOS: Build Widget for Recipe App with Widget Extensions and Swift
As you might know the Notification Center framework now has been deprecated on creating and managing app extensions that implement widgets. So now we have to use WidgetKit. With WidgetKit, users can get an important information without the need to open your app. This is by putting widgets on the iOS Home screen or macOS Notification Center.
To implement a widget, you have to add a widget extension to your application. What you do is configuring the widget with a timeline provider and use SwiftUI views to display the content of the widget. The timeline provider then tells WidgetKit when to update the content of your widget.
Note: this is a summary of the full article which you can read here.
We will make a widget for a simple application called HeartyRecipe. This is an app that provides a recipe list and its details. The widget we created will show a random recipe every one hour. You can download the starter project here.
Step 1: Add a Widget Target to the App
The first step is creating a Widget Extension target in your project. The Widget Extension template provides a starting point for creating your widgets.
- Open the sample app project in Xcode, choose File > New > Target.
- From the Application Extension group, select Widget Extension, then click Next.
- Enter the name of your extension. Here I am going to name it “HeartyRecipeWidget”.
- We don’t need the configuration in this tutorial, so make sure to uncheck the Include Configuration Intent checkbox. Then, click Finish.
On the project navigator, you can see that Xcode now has created the HeartyRecipeWidget folder. Open the HeartyRecipeWidget.swift then. This file stores the skeleton code that is required to build your widget.
Data of your widget will be provided through a TimelineEntry
that can be updated based on your app’s use case. The first thing you should see in HeartyRecipeWidget.swift is a TimelineProvider
. The getSnapshot(context:completion:)
method is used to provide TimelineEntry
to present a widget view that is used in transient situations (such as in the widget gallery).
After that, you have to see the getTimeline(context:completion:)
method. This method provides an array of timeline entries, one for the present time and others for future times based on your widget’s update interval. In this tutorial, we will update the widget with a random recipe every hour. So, the timeline entries in this widget will be one hour apart from each other.
Next, see the placeholder(context:)
method. This method provides a TimelineEntry
to display our widget on the lock screen and before rendering the widget’s content.
Below that is the code that defines the TimelineEntry
(we will be updating this later). Then, there is HeartyRecipeWidgetEntryView
that defines the visual. And finally, there is HeartyRecipeWidget
struct marked with the @main
annotation. That annotation means that this is where the entry point of the widget is.
Step 2: Create Embedded Framework
We have to create an embedded framework if the main app and Widget extension need to access the same code or resources.
- Create a new Xcode project using Framework template. Click File > New > Project; then from the Framework & Library group, click Framework. Enter the name of your framework. I named it “HeartyRecipeHelper”. Make sure that the selected language is Swift.
- Place shared code in the embedded framework. The main app and the Widget extension will use entities in the Model folder. Therefore, we have to put the Model folder in the embedded framework. To make any structs or other entities in the Model folder available to be accessed by the other targets, they must be made explicitly
public
. - Place shared assets in the embedded framework. In this sample project, the recipe data is provided from a static JSON file which is Recipes.json (where the
imageURL
field contains the name of the image file inside the Assets.xcassets bundle). Therefore, to make the assets available from the other target, we have to move the images that are referred to in Recipes.json. - Consume the framework. We have to link the framework target that is the sample app that earlier we added the Widget Extension to, using workspace. This is by creating a new project workspace and put the HeartyRecipe app project and HeartyRecipeHelper framework into that workspace. You also have to link the HeartyRecipeHelper framework with the HeartyRecipe.
Read the full article to see the detailed instructions.
Step 3: Create Widget View
Now we can define the user interface of our widget. We will be putting the code in a separate swift file. In the HeartyRecipeWidget folder, create a new swift file and name it “HeartyRecipeWidgetView.swift”. Make sure it is added as a part of the HeartyRecipeWidgetExtension target.
Add the content of our widget like this:
Now, go to Data.swift in the Model folder in HeartyRecipeHelper, then add the getRandomRecipe()
like this following code.
We can now modify the TimelineEntry
so that it includes an instance of RecipeBaseClass
.
We have to update the code, so we provide a RecipeBaseClass
instance wherever we create a TimelineEntry
. This is also where we will use the getRandomRecipe()
method.
SimpleEntry(date: Date(), recipe: getRandomRecipe())
In HeartyRecipeWidgetEntryView
, replace the default Text
component and use the HeartyRecipeWidgetView
, also get the RecipeBaseClass
from the entry
.
Finally, update the widget’s name and its description. Since for now we only support small widgets, we also have to update the configuration by adding supportedFamilies
. We can do that by updating the StaticConfiguration
.
Then, test the widget in the simulator by first setting the active scheme to be the HeartyRecipeWidgetExtension.
You should be able to see the widget in the simulator. Press it and it will open the HeartyRecipe app!
Thank you for reading! You can find the completed app project here.
For complete and detailed tutorial about it, you can read in: Build Widget Recipe App with Widget Extensions and SwiftUI