paint-brush
Hacking Widgets the Apple Way by@marcushaldd
1,065 reads
1,065 reads

Hacking Widgets the Apple Way

by Daria LeonovaOctober 26th, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Apple widgets have become an essential part of user interaction, offering quick access to personalized and timely information. This article delves into the philosophy behind widget design, technical implementation using WidgetKit, efficient widget updates, Smart Stack placement, and design considerations. Learn how to create widgets that provide essential information at a glance and follow Apple's design guidelines. Harness the power of WidgetKit to create engaging and useful widgets for your users.
featured image - Hacking Widgets the Apple Way
Daria Leonova HackerNoon profile picture


Apple consistently expands the functionality of widgets with each update, making them more and more useful and popular among users. By today we have widgets on all devices — from Watches to Macs; widgets are presented in various formats — on the lock screen, and live activities. Interactivity (the ability to control the widget via buttons), a nightstand mode, and animations have been added recently.


Today, I will try to refresh your memory on the philosophy of widgets and their design. Let’s talk about the structure of a widget, what value it can bring to the user, and how Apple envisions it. I will highlight some technical aspects that may be useful if you’re just starting with your first widget or perhaps want to breathe new life into an old one.


Philosophy

Let’s start with an ideal example of how a widget should work from Apple’s perspective.


You pick up your phone to browse Instagram, and at a glance (1), you see a weather widget. It provides you with all the essential information you need in just one look: the weather in your (2) city: it will stop raining in 15 minutes (3), and the sun will come out. These are the three important principles that good widgets should follow:


  1. At a glance: Don’t overload your widget with functionality. Just one glance should be enough to provide you with all the necessary information. After all, the app can be opened with a single tap. If you want to know the detailed week-long weather forecast or the number of sunny days, you can simply open the app.


  2. Personalization: Data should be relevant to the specific user. You only care about the weather outside your window, not somewhere else.


  3. Timeliness: A widget is not a big static app icon. Show data that is interesting to the user at the current moment in time.


By the way, while developing, you’ll notice that the entire technical implementation is based on time-related aspects.




Technical implementation

Let’s now talk about how the widget’s technical implementation works. In short:


  1. You provide the data.

  2. The system sequentially renders the user interface (UI) based on this data and displays it on the home screen.

  3. When the data runs out, the system requests new data —> go back to step 1.





Now, let's dive into more details.


Timeline

When we talk about data, we refer to a Timeline. Timeline is an array of objects with a time reference. Each object contains all the data that is required to render the widget, plus the time when this data becomes relevant. Going back to the weather example, an object would include the air temperature, precipitation, and the time at which this weather will be relevant.


If we talk about widget as a View, then the Timeline is an array of ViewModels conforming to the TimelineEntry protocol. The only strict requirement for a TimelineEntry is time reference.


struct WeatherEntry: TimelineEntry { 
  // TimelineEntry
  var date: Date
  // Properties
  var temperature: Double
  var isRaining: Bool 
}


TimelineProvider

We provide the system with a timeline by implementing the TimelineProvider, which is another protocol with methods for generating timelines. The system, in turn, handles the rendering.


In simple terms, we write the “script” for when and what to display — the timeline. The system, using the UI Widget, draws a “comic book” — a set of cards that it later presents to the user. The system itself keeps track of time and updates the widget.


Please note that precise alignment with the time you specified in the TimelineEntry date is not guaranteed.


In addition to providing a timeline, the TimelineProvider asks you to implement the methods getSnapshot(in:completion:) and placeholder(in:). In the first method, you return a timelineEntry for rendering a skeleton in case the real data hasn’t loaded yet. The second method, `placeholder`, returns a timelineEntry for the widget gallery. Try to use this data to show your widget’s capabilities to the user as comprehensively as possible.


TimelineReloadPolicy

In addition to the data for display, the timeline has one important property — policy: TimelineReloadPolicy, which determines how the system will obtain new data. It comes in three options:


  1. atEnd - WidgetKit will request a new timeline when the current data set reaches its end.
  2. after(Date) - WidgetKit will request a new timeline at the specific moment in time.
  3. never - WidgetKit will keep the last available data on the screen and will not request any new data.


Furthermore, the timeline can be forcibly updated without waiting for its TimelineReloadPolicy. We are mostly interested in 2 following cases:


  • Calling the method WidgetCenter.shared.reloadTimelines(ofKind: “com.weatherwidget.rainstatus”) within the app itself.
  • User interaction with an interactive widget (iOS 17).


Efficient Widget Updates

It’s important to note that updating a widget (requesting and calculating a new Timeline) is a time-consuming process. The system imposes restrictions on the update frequency, so you can’t request a new update every minute.


There is a so-called “budget” allocated for each widget. You can read about it in detail here. But in brief, the number of available updates per day depends on the “benefit” of the widget. The system takes into account, for instance:

  • how often the user looks at the screen with your widget;
  • when the widget was last updated;
  • how significantly the geolocation (if used) has changed;


The documentation says that, on average, a widget can count on only 40–70 updates per day. It’s worth carefully considering the data update algorithm and choosing an appropriate TimelineReloadPolicy to stay within these limits.


The good news is that widget updates triggered from the app or due to user interaction are “free.” Therefore, try to perform most of the work, data preparation and updates, when the app is in the foreground.


You can find a good and detailed example of optimizing the number of updates in the documentation as well.


Smart Stack

In addition to the frequency of updates, you may also be interested in the frequency of your widget being displayed in the Smart Stack.



Users can group widgets into a stack known as the Smart Stack. The system automatically selects the most relevant widget and displays it at the top. Moreover, starting from iOS 15, the Smart Stack can even suggest widgets that the user hasn’t added to the stack (Widget Suggestions).


To help the system understand how useful at any given moment your widget is, you can use the following methods:


  • You have the ability to set a relevance value for each TimelineEntry. Its purpose is to indicate how potentially important relative to others the current entry is.


For example, if it’s going to rain in the next hour, it would be great for the user to know this before leaving home without an umbrella. Therefore, for the timelineEntry containing precipitation information, you can set a higher relevance score than for others. All other things being equal, Smart Stack will move your widget to the top of the stack, and the user will stay dry.


  • You can take advantage of App Intents. The framework allows the content and functions of the application to integrate with system services such as Siri and Shortcuts. This is especially useful if you want the system to automatically add your widget to the Smart Stack.


How does it work?


You provide the system with information about user actions (intents) within your app, and based on the gathered preferences and habits of the user, the system configures and displays your widget. It’s easier to understand this with an example.


Let’s say your app is called “BookAndFilmAdviser” — it suggests what to read or watch. Therefore, you have two widget configurations — one for books and one for films. Let’s assume every Friday your user looks for a new film to watch. By receiving information about these actions through App Intents, iOS will create a widget for your app in the films configuration and place it at the top of the stack next Friday.


Layout

Regarding design, there’s a good video from WWDC.


From what I remember:


  1. Follow Human Interface Guidelines.
  2. Use Standard Fonts. This is important for making your widget’s appearance harmonious with other icons on the home screen. It also means automatic dynamic type support.
  3. Use ContainerRelativeShape for corner radius.
  4. Test your widget in various environments.
  5. Avoid adding a title: the app’s name is already present beneath it on the home screen.
  6. Include an icon only if your app displays third-party content, similar to Apple TV or Podcasts.


In general, draw inspiration from the built-in system widgets in iOS to create a design that feels native and familiar to users. And here is an example of good non-system widgets:




Inspiration

In the end, I would just like to share useful links that will help you in the creation of the perfect widget.


Firstly, you can create widgets in different configurations, as in the example about “BooksAndFilmsAdvisor”. Secondly, even if you support iOS below 17, and you want to handle user taps, you can look at widgetURL. Well, if your users have updated their phones, you can please them with new animations and real buttons. Thirdly, do not forget about adapting the design to all platforms and environments — everything is not as difficult as it may seem. Fourth, WidgetKit is not only widgets, but also Live Activities and Watch Complications.


I hope that all that I have collected for you will inspire new ideas and achievements. Let your creativity unfold!


Bonus for those who have read to the end

Even though there’s no possibility to update the widget every second, you can still display a ticking timer.