On June 22, 2020, Apple held its first online developer conference, WWDC20. The event was one of the most historic (I’m Dad) announcements of the ARM-based Mac chip (Intel), iOS 14 ATT (Facebook) and, of course, the Widget that’s being touted as the next top traffic entry point.

In the wake of August, we’ll take a look at widgets this time.

This article explores widgets in two dimensions and sections: First Look at Widgets and Widget Development. The first look section provides a brief overview of widgets for application decision makers. The development section walks you through the design and development widgets step by step.

At the beginning of the Widget peep

What is a Widget

In iOS 14, we have a dramatic new Home screen experience, one that is much more dynamic and personalized, with a focus on widgets. The content is the focus. This is very important: widgets are not mini-apps. Think of this as more projecting content from your app onto the Home screen rather than full mini-apps filled with tiny little buttons.

In short: Widgets are not mini applications. It’s a new home screen experience that quickly delivers what users care about

2. Features of widgets

  • GlanceableBe clear at a glance
  • RelevantLeek Song late early
  • PersonalizedAct according to actual circumstances

To design a good Widget, you need to understand all the features of the Widget

To describe Apple’s Glanceable, Relevant and Personalized, we use one idiom to describe Apple’s Glanceable, Relevant and Personalized

Let me just say a few features

1, Glanceable | be clear at a glance, take in everything in a glance

A good Widget should be visible at a glance.

The average person visits the “home screen” more than 90 times a day, but stays there for only a few minutes before switching to another App.

So widgets need to take advantage of the small screen to present the most important information, and be concise. Innovative design, quick browsing, and efficiency are at the heart of a good Widget.

Users don’t have to think about how to use the Widget, and they don’t have to click any buttons to get the information they care about most.

2, Relevant | Song leek night early

Apple wants widgets to be tightly integrated with the user’s actions, such as checking the weather when you wake up in the morning; Lunch is just the meal, the user wants someone to recommend nearby food; During the evening rush hour, users want to know driving directions; Good night, hope to record the next day’s journey.

To do this, Apple offers a feature called Smart Stacks, which is a collection of Widgets. The system will automatically display the widgets that the user needs most at the current point in time according to everyone’s habits.

! [](https://guojunliu.github.io/images/widget/3.gif)

3, Personalized tailoring

3.1 size

Widgets that provide personalized services to users, such as weather widgets, need to be able to provide different details of weather conditions for different users.

Apple offers three different sizes of widgets for this purpose

  • systemSmall
  • systemMedium
  • systemLarge

The size of systemSmall is 2*2 Icon, the size of systemMedium is 4*2 Icon, and the size of systemLarge is 4*4 Icon

3.2 Personalized Configuration

On the other hand, widgets need to be able to provide local weather conditions for users in different cities.

To this end, Apple provides developers with two types of widgets:

  • StaticConfiguration: For a widget that has no user-configurable properties, no user configuration is required and only user information is displayed. For example, a stock market widget that displays general market information, or a news widget that displays trending headlines.

  • IntentConfiguration: Speculations for widgets that have user-configurable properties that support user configuration and user intent. You use SiriKit custom intents to define properties. For example, a weather widget that requires a city zip code, or a package tracking widget that requires a tracking number.

Note that IntentConfiguration does not require coding, only simple configuration, Xcode will automatically generate the corresponding code and type for you.

3.3 Dark Mode

The Widget also supports the system’s dark mode

The nature of widgets

The essence of a Widget is a collection of stacked static views that are presented at different points in time

This is where the Widget’s core Timeline is introduced

As the name implies, a Timeline is a Timeline where events occur at corresponding points in time

Many widgets have predictable points in time at which it makes sense to update their content. For example, a widget that displays weather information might update the temperature hourly throughout the day. The stock market widget can be updated frequently during open market hours, but not completely on weekends. By planning these times in advance, generating different views to put into the timeline, WidgetKit will automatically refresh your widget when the appropriate time arrives.

This also means that widgets are basically not updated in real time

In addition, WidgetKit caches the Views structure information defined by Timelines to disk and then jit-renders it on refresh time. This enables the system to process Timelines information for many Widgets with very low power overhead.

4. User interaction

Sorry, no interaction!!

In order to achieve these features, Apple has also removed some features that limit widgets

  • Can’t interaction
  • Can’t play animation
  • Can’t play video
  • Scrolling is not supported
  • Active view refresh is not supported

The only support is for the user to click on the Widget to invoke the main App

There are two schemes for clicking to evoke the main App, which are as follows:

  • widgetURL
  • Link

WidgetURL evokes the App’s clickable areas as all areas of the Widget, a scheme suitable for simple elements, single logical widgets

For systemSmall type widgets, only widgetURL invocation is supported

For systemMedium and systemLarge, you can also use a more detailed Link invocation, which allows widgets to invoke different pages of the App by clicking on different elements, giving developers more room to play

For a simple example, widgetURL can be applied to weather widgets, blog widgets, and click-to-app;

Link can be used in the memo and calendar widgets. Click on a different memo and date to jump directly to the corresponding memo details and to-do details page

Widget at first glance summary

Widgets create a ripple on the stagnant iOS desktop, and there are bound to be a lot of apps vying for that fat piece of traffic.

But a closer look shows that Apple’s Widget is restrained and not overly aggressive,

As the saying goes: like is unbridled, but love is restraint. Widgets are not mini-apps. Widgets are designed to provide the user with the most information at the least cost. In order to minimize user costs (power, network, etc.) and improve user experience, Apple has made many restrictions on the technical level, limiting many functions, greatly reducing the status and importance of widgets, and reducing the enthusiasm and enthusiasm of developers to implement them

In fact, only a few of Apple’s new technologies can be applied to apps every year. Hopefully, this Widget will drive people to integrate their own apps and bring more traffic to their apps, as well as a better user experience.

The Widget development

Now for the big part, let’s design and write good widgets step by step

Before we get started, let’s take a look at the Widget development language. Apple specifically specifies that widgets can only be developed using SwiftUI

A, SwiftUI

So why did Apple choose WWDC 2019 to release SwiftUI, which is only a year old?

First, it was an Apple goal from the beginning to make widgets multiplatform, and SwiftUI was a killer in its ability to display across devices.

SwiftUI also makes it easy to use features like automatic layout and dark mode, reducing development costs such as adaptation. For widgets that don’t require a lot of elements, SwiftUI’s focus on layout features is most appropriate.

On the other hand, SwiftUI is the only way to achieve the Widget limitations we mentioned above. If we could use objective-C UIKit, our powerful developers would probably come up with countless hacks to ignore the limitations of Apple’s real widgets. For example, developers cannot use UIViewRepresentable to bridge UIKit;

Finally, Apple has its own self-interest. This year, Apple has raised the importance of Swift and SwiftUI to a new level. Swift is now independent of the Foundtion framework, so SwiftUI should be independent of UIKit. Forced use of SwiftUI makes it as easy as possible for developers to learn its content and apply it to iOS, iPadOS and macOS,

After all, Objective-C, which was number 20 in May, dropped out of the top 20 in June

It’s important to note that a Widget that uses any UIKit element will Crash

Add the widget target to your application

The widget extension template provides a starting point for creating widgets. A single widget extension can contain multiple widgets. For example, a sports application might have one widget that displays team information and another widget that displays game schedules. A widget extension can contain two widgets. Although it is recommended to include all widgets in a single widget extension, you can add multiple extensions if necessary.

  1. Open your application project in Xcode, then choose File > New > Target.

  2. From the Application Extension group, select Widget Extension, and then click Next.

  3. Enter your package name.

  4. If the widget provides user-configurable properties, select the Include Configuration Intent check box.

Click Finish.

Important: The widget supports not only Swift projects, but also Objective-C projects

Create better parts, we will be more a SmileEverydayWidget. Swift file, this is a widget can run up, because we are next to each method to analysis, so the file first full text display as follows

// // SmileEverydayWidget.swift // SmileEverydayWidget // // Created by steve on 2020/8/28. // import WidgetKit import SwiftUI struct Provider: TimelineProvider { public typealias Entry = SimpleEntry public func snapshot(with context: Context, completion: @escaping (SimpleEntry) -> ()) { let entry = SimpleEntry(date: Date()) completion(entry) } public func timeline(with context: Context, completion: @escaping (Timeline<Entry>) -> ()) { var entries: [SimpleEntry] = [] // Generate a timeline consisting of five entries an hour apart, starting from the current date. let currentDate = Date() for hourOffset in 0 .. < 5 { let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)! let entry = SimpleEntry(date: entryDate) entries.append(entry) } let timeline = Timeline(entries: entries, policy: .atEnd) completion(timeline) } } struct SimpleEntry: TimelineEntry { public let date: Date } struct PlaceholderView : View { var body: some View { Text("Placeholder View") } } struct SmileEverydayWidgetEntryView : View { var entry: Provider.Entry var body: some View { Text(entry.date, style: .time) } } @main struct SmileEverydayWidget: Widget { private let kind: String = "SmileEverydayWidget" public var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: Provider(), placeholder: PlaceholderView()) { entry in SmileEverydayWidgetEntryView(entry: entry) } .configurationDisplayName("My Widget") .description("This is an example widget.") } } struct SmileEverydayWidget_Previews: PreviewProvider { static var previews: some View { /*@START_MENU_TOKEN@*/Text("Hello, World!" )/*@END_MENU_TOKEN@*/ } }Copy the code

There are a lot of concepts and code here, so let’s explain them one by one

Third, the Widget API

Let’s start with the method with the main field,

@main
struct SmileEverydayWidget: Widget {
    private let kind: String = "com.steve.liu.smileEverydayWidget"

    public var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: Provider(), placeholder: PlaceholderView()) { entry in
            SmileEverydayWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("My Widget")
        .description("This is an example widget.")
        .supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
    }
}
Copy the code

We all know that methods with the main identifier are entrances to programs

This code declares a widget named SmileEverydayWidget using SwiftUI, where StaticConfiguration is the initialization method for the widget and takes several parameters:

  1. kind
  2. provider
  3. placeholder

Among them

Kind is a string that identifies the widget and should describe what the widget represents. That is the package name of the widget

Provider is the timeline provider

The PlaceholderView is a PlaceholderView

Some methods are also provided, for example

  • configurationDisplayName()Sets the name of the widget display
  • description()Sets the description of the widget
  • supportedFamilies()Sets the sizes supported by the widget

An important point here is that in order for an application’s widget to appear in the widget library, the user must launch the application containing the widget at least once after installing the application.

Four, WidgetEntryView

WidgetEntryView is the widget view using the SwiftUI layout

struct SmileEverydayWidgetEntryView : View {
    var entry: Provider.Entry

    var body: some View {
        Text(entry.date, style: .time)
    }
}
Copy the code

The widget view, for example, simply shows the current time

Next we can change the default layout to the layout we want, for example I set the font to display the text and the widget background

struct SmileEverydaWidgetEntryView : View {
    var entry: Provider.Entry

    var body: some View {

        return Text(entry.message)
            .background(Image(entry.backgroundImageStr))
            .font(.callout)
    }
}
Copy the code

Fifth, PlaceholderView

Each widget needs to provide a placeholder UI.

The placeholder UI is the default content of the widget.

It should represent your widget type, but that’s it.

There should be no user data in this user interface.

imagine

If the following scenario appears on the user’s home screen, your widget may not be far away from being removed

Six, TimelineEntry

We know that widgets are presented in a timeline, one entry at a time line

struct SimpleEntry: TimelineEntry {
    public let date: Date
}
Copy the code

A mandatory property of TimelineEntry is date, which is the specific time on the timeline of the entry

In addition, developers can customize various properties in TimelineEntry to provide data to the widget view

For example, I customize the Message and backgroundImageStr properties in TimelineEntry to display text and background images on the widget

struct SimpleEntry: TimelineEntry {
    public let date: Date
    public let message: String
    public let backgroundImageStr : String
}
Copy the code

Seven, TimelineProvider

The TimelineProvider is an object that provides the TimelineEntry collection described above

Let’s look at the code:

struct Provider: TimelineProvider { public typealias Entry = SimpleEntry public func snapshot(with context: Context, completion: @escaping (SimpleEntry) -> ()) { let entry = SimpleEntry(date: Date()) completion(entry) } public func timeline(with context: Context, completion: @escaping (Timeline<Entry>) -> ()) { var entries: [SimpleEntry] = [] // Generate a timeline consisting of five entries an hour apart, starting from the current date. let currentDate = Date() for hourOffset in 0 .. < 5 { let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)! let entry = SimpleEntry(date: entryDate) entries.append(entry) } let timeline = Timeline(entries: entries, policy: .atEnd) completion(timeline) } }Copy the code

There are two methods

  • snapshot()
  • timeline()

1. Snapshot

To display widgets in the widget library, WidgetKit requires the provider to provide a preview snapshot, called snapshot(), which mainly provides sample data, preferably real data, to show to the user when the timeline is not available

It should be noted that a snapshot is where the system needs to quickly display a single entry.

Therefore, your extension must return to view as soon as possible, because when it does, the user will see the real Widget in the nice Widget Gallery on iOS.

This is not a screen shot or image that we have to provide at design time. This is the real widget experience for users on iOS, iPadOS, and macOS.

In most cases, the first entry in the timeline and the snapshot can be returned as the same entry, so what you see in the Gadget library is what you get when the user adds it to the device.

For example, when we add a battery Widget in the Widget Gallery, what the Widget displays in the Widget Gallery is a snapshot of the real-time battery information of the current device, instead of some fake data. What is the data of the Widget at this time? The widget’s data is what it looks like when the user adds it to the home screen, improving the user experience.

contrast

Meas view and snapshot are placeholder solutions while the meas view is a PlaceholderView when the meas can’t fetch data quickly on the host screen, so loading or blank screen is not displayed. Snapshot is mainly used in Widget Gallery to improve user experience. Generally speaking, snapshot is the first frame of the timeline

2, timeline,

After requesting the initial snapshot, WidgetKit calls Timeline to request the provider’s regular timeline. A timeline consists of one or more timeline entries, TimelineEntry, and an overload policy, ReloadPolicy, that tells WidgetKit when to request subsequent timelines.

For overloaded policies, the following are provided

  • atEnd: refreshes the Timeline when it reaches the last time slice.
  • atAfter: means to refresh regularly after a certain time
  • never: Means that you don’t need to refresh later. When does the App need to tell the Widget that it needs to refresh

According to the above analysis, we can transform the TimelineProvider as follows

struct Provider: TimelineProvider { public typealias Entry = SimpleEntry public func snapshot(with context: Context, completion: @escaping (SimpleEntry) -> ()) {let date = date () let message = "Green, white dew is frost. The so-called girl is in the water side." let backgroundImageStr = "bg7" let entry = SimpleEntry(date: date, message: message, backgroundImageStr: backgroundImageStr) completion(entry) } public func timeline(with context: Context, completion: @escaping (Timeline<Entry>) -> ()) { var entries: [SimpleEntry] = [] var currentDate = Date() var nextUpdateDate = Calendar.current.date(byAdding: .second, value: 3, to: currentDate)! Let message = "Thick the reeds, white dew for frost. The so-called girl, in the water side. \n Back from the whirl, road resistance and long; Travel back from it, wan in the middle of the water. \n The time is desolate and the white dew is not half shewn. The so-called Girl is in the Mekong River." let backgroundImageStr = "bg" let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" var currentDateStr = "" var nextUpdateDateStr = "" let longUuid = UUID().uuidString let range: Range = longUuid.range(of: "-")! let location: Int = longUuid.distance(from: longUuid.startIndex, to: range.lowerBound) let uuid = longUuid.prefix(location) for i in 1 .. < 10 { var msg = "" currentDate = nextUpdateDate; nextUpdateDate = Calendar.current.date(byAdding: .second, value: 3, to: currentDate)! currentDateStr = formatter.string(from: currentDate) nextUpdateDateStr = formatter.string(from: NextUpdateDate) msg.append(message) msg.append("\n timeline ID "+ uuid); MSG. Append ("\n timeline "+ String(I +1) +" view ") MSG. Append ("\n view start time "+ currentDateStr) MSG nextUpdateDateStr) let entry = SimpleEntry(date: currentDate, message: msg, backgroundImageStr: backgroundImageStr+String(i)) entries.append(entry) print(String(i)) } let timeline = Timeline(entries: entries, policy: .atEnd) completion(timeline) } }Copy the code

In this code we provide a snapshot of an ancient poem

In addition, a timeline is generated, which contains 10 entries with an interval of 10 seconds

The entry text concatenation is displayed

  • Show the text (Book of Songs)
  • The timeline ID
  • Start time of this view
  • Next view start time

Let’s look at the effect of a real run

At this point we have a widget that can be displayed on the home screen

And can display different views according to the timeline.

Reload Timeline keeps the widget up to date

We need to update our widgets from time to time to keep them up to date and not out of date.

We already know that the essence of a widget is a series of stacked views, so updating a widget is updating those views.

For example, a widget with three views that predicts the weather forecast for the present and the next 3 hours shows the following steps:

But there is a very important problem is that the time line is predicted by us, and the prediction can be biased. For example, in the weather forecast, rain was forecast 2 hours later, but with the change of the weather, it became sunny 2 hours later. At this time, if we do not update the time line on the widget, we will provide wrong information to the user 2 hours later.

To do this, we need to re-display the new view when the information changes, as shown below:

In order for our widget information to be accurate, we first need to know how the widget is refreshed

Unfortunately, the refresh of widgets is completely controlled by WidgetCenter. There is no API for developers to actively refresh the Widget’s page, only to tell WidgetCenter that the Timeline needs to be refreshed

So instead of refreshing the Widget’s view directly, the Reload Timeline replaces the old Timeline by generating a new one. Instead of refreshing the Widget directly, WidgetCenter re-requests the Widget for the next phase of data.

The Reload Timeline is divided into two modes

  • System Reloads
  • App Reloads

1, the System Reloads

This behavior is initiated by the system and invokes the Reload Timeline once to request the data to be refreshed in the next phase from the Widget. In addition to initiating System Reloads on time, the System also dynamically decides the frequency of System Reloads for each different TimeLine. For example, the number of clicks directly determines the frequency of System Reloads. The higher the click rate, the faster the update frequency. Of course, there are also some behaviors triggered by the device environment, such as the device time changes, which trigger the System Reloads.

Obviously, this solution is not a good solution to our above problem

2, the App Reloads

This is when the App actively notifies the widget that you need to update your information. Here, there are two ways according to the current front and back state of App

  • The application runs in the foreground
  • The application runs in the background

When running in the foreground, the App can directly use the WidgetCenter API to Reload the Timeline. When the application is in the Background, Background Notification can be used to Reload the Timeline.

In addition, it is important to set a proper refresh strategy for Timeline

The right combination of these refresh mechanisms can greatly improve the accuracy of Widget information

Eight, interaction,

As mentioned earlier, there are two ways for widgets to interact with apps: SwiftUI widgetURL API and SwiftUI Link API

The essence of both are URL Schemes, just listen for SceneDelegate scene:openURLContexts

We won’t go into details of Schemes for efficient, fast and accurate delivery as we are all too familiar with them.

Design beautiful widgets

If you’ve seen this and understood the above, you already have the ability to develop widgets.

So what are the key points to add to your widget?

Remove extra App info: The system automatically displays your App’s name below the widget, so you don’t have to repeat the App’s name, Icon in the content, but instead use color, layout, and image to connect to your App

Succinct description. The descriptions displayed in the widget library help people understand the functionality of each widget. Starting with an action verb usually works well; For example, “View current weather conditions and location forecasts” or “track upcoming events and meetings.” Avoid including unnecessary phrases referencing the widget itself, such as “This widget shows…” , “Use this widget…” Or “Add this widget.”

Comfortable information density: at a glance. When content is sparse, widgets can seem redundant; When the content is too dense, the widget will not be able to browse. If you want to include a lot of information, avoid making the widget a collage of hard-to-parse items. Look for ways to organize content so that people can grasp key parts immediately and view relevant details over a longer period of time. You might also consider creating a larger widget and looking for places where you can replace text with graphics without losing clarity.

Use color wisely: Rich, beautiful colors catch the eye, but they must not stop people from absorbing the information from a widget at a glance. Using color enhances the appearance of the widget without competing with the widget’s content.

Use system font, support system functions: for example, support dark mode; Use SF Pro and use system fonts; Text is scalable.

Design a realistic preview to display in the widget library: Highlighting the look and function of a widget helps people make informed decisions and encourages them to add widgets. You can show the real data in the widget preview, but if the data takes too long to generate or load, show the real simulated data.

Design placeholder content to help people identify your widget. The widget displays placeholder content when loading data. An effective preview can be created by combining the static parts of the UI with translucent shapes that represent the actual content. For example, you can use rectangles of different widths to suggest lines of text, and use rings or squares instead of glyphs and images.

Image fit screen size: Make sure images don’t compress under large widgets and widgets

Ten surrounding trees, such as tillers

Just to summarize

An excellent widget can completely improve user experience, become a good traffic entry point, and bring great business value to the App.

But it’s not easy to design a good widget.

In this article, I hope you can design more good widgets.

This Widget ends here. Swastikas are not easy to spread.

Like me you pay attention to me,

You judge me if you have something to say,

Give it a thumbs up if you don’t

Demo

  • SmileEveryday

reference

  • Meet WidgetKit
  • Human Interface Guidelines
  • Creating a Widget Extension
  • Keeping a Widget Up To Date
  • Apple Widget: The next top Traffic entry point?