SwiftUI WidgetKit Chinese translation of apple official documentation

  • See Apple Document for the original Document
  • DeepL machine was used for manual check
    • Widgets = widgets, some are manually changed, and some are named “widgets”
    • The second article is a little tricky, but it can be done 😂
  • Making the address

added

  • There are many layers of official Apple documents. Currently, there is no good solution. If necessary, please Clone to local and use global search.
  • File name toOfficial English name beginning (Chinese translation ends).mdEasy to search
  • Please see the big guy to little brother point a Star……
    • Looking for a job (._.) Some stars have some confidence 😂

The following three articles are released, and other apis can be found in GitHub repository

1. Create a widget extension

Add and configure an extension to display your app’s content on the iOS home screen and macOS notification center.

An overview of the

Widgets visually display relevant content, allowing users to quickly access your application for more details. Your application can provide a variety of widgets to let users focus on the information that is most important to them.

Users can add multiple copies of the same widget, customizing each widget to their unique needs and layout. If you allow customization of widgets, users can customize each Widget individually.

Widgets support multiple sizes; You can choose the size that best fits the content of your application. However, with limited available space, focus your widgets on presenting the information that people value most.

Adding widgets to your application requires only a small amount of setup. Widgets display their content using the SwiftUI view. For more information, see SwiftUI.

Add a widget to your application

The Widget extension template makes it easy to quickly create a Widget. A widget extension can contain multiple widgets.

For example, a sports app might have a Widget that displays team information and another Widget that displays the game schedule. A single Widget extension can contain both types of 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 and choose File > New > Target.
  2. From the application Extension group, select the Widget extension, and then click Next.
  3. Enter the name of the extension.
  4. If the widget provides user-configurable properties, select the Include Configuration Intent checkbox checkbox.
  5. Click Finish.

Adding Configuration Information

The widget extension template provides an initial widget implementation that conforms to the widget protocol.

The Widget’s body property determines whether the Widget has user-configurable properties.

There are two configurations to choose from.

  • StaticConfiguration: For a Widget that has no user-configurable properties. “For example, a stock market Widget that displays general market information, or a news Widget that displays trending headlines.”

  • IntentConfiguration. For a Widget with user-configurable properties, you can use SiriKit custom intent to define the properties. You use SiriKit custom intents to define properties. “For example, a weather Widget needs a city zip code or zip code, or a parcel tracking Widget needs a tracking number.”

The Include Configuration Intent check box determines which Configuration Xcode uses. When you select this check box, Xcode usage will be configured with the default Settings;

Otherwise, it uses static configuration. To initialize the configuration, provide the following information.

  • kind. Identify the string for the Widget. This is the identifier of your choice and should describe what the Widget represents.
  • Provider: in accordance withTimelineProviderThe object. A meetTimelineProviderObject, which can produce a timeline that tellsWidgetKitWhen to render a Widget. The timeline contains a custom that you defineTimelineEntryType. The timeline target identifies the date you want WidgetKit to update the Widget content. The view that contains your Widget in a custom type needs to render properties.
  • Placeholder. A SwiftUI view that WidgetKit uses to render the Widget for the first time. Placeholders are a generic representation of your Widget, with no specific configuration or data.
  • Content Closure. A closure that contains the SwiftUI view.WidgetKitIt is called to render the contents of the Widget, passing one from the providerTimelineEntryParameters.
  • Custom Intent (Custom configuration). A custom intent that defines a user configurable property. For more information about adding customizations, see Making Configurable Widgets.

Use modifiers to provide additional configuration details, including displaying the name, description, and families supported by the Widget.

The following code shows a game Widget with a generic, non-configurable state.

@main // Declaration is the main component
struct GameStatusWidget: Widget { // Declare as Widget // instead of some View
    var body: some WidgetConfiguration { // Widget configuration items
        StaticConfiguration( // StaticConfiguration
            kind: "com.mygame.game-status".// Unique identifier
            provider: GameStatusProvider(), // Timeline provider
            placeholder: GameStatusPlaceholderView(a)// placeholder
        ) { entry in // What is displayed
            GameStatusView(entry.gameStatus)
        }
        .configurationDisplayName("Game Status") // Display the name
        .description("Shows an overview of your game status") / / description
        .supportedFamilies([.systemSmall, .systemMedium, .systemLarge]) // Size of supported widgets}}Copy the code

In this example, the Widget uses the GameStatusPlaceholder view as the placeholder view, and the GameStatusView is closed as the content.

The placeholder view displays a generic representation of your Widget, giving users a general idea of what the Widget displays. Do not include actual data in placeholder views “for example, use gray boxes to represent lines of text, or gray circles to represent images.” “Skeleton plan”

The Provider generates a timeline for the Widget and includes game state details in each entry. When each timeline date arrives, WidgetKit calls a content closure to display the widget’s contents. Finally, the modifier specifies the name and description to display in the widget gallery and allows the user to select small, medium, or large versions of the widget.

Notice how the @main attribute is used on this widget. This property indicates that the GameStatusWidget is the entry point to the widget extension, meaning that the extension contains a widget. To support multiple widgets, see Declaring multiple widgets in your application extension.

Provide Timeline Entries for Provider configuration

The Provider generates a timeline of timeline entries, each specifying the date and time to update the widget content. The game state widget can define its timeline entry to contain a string representing the game state, as follows:

struct GameStatusEntry: TimelineEntry {
    var date: Date
    var gameStatus: String
}
Copy the code

To display your widget in the widget gallery, WidgetKit asks the provider to provide a preview snapshot. You can identify this preview request by examining the isPreview property of the context parameter passed to the snapshot(for:with: Completion 🙂 method. When isPreview is true, WidgetKit displays your widget in the widget library. In response, you need to quickly create a preview snapshot. If your widget needs to spend time generating or retrieving assets or information from the server, use sample data instead.

In the code below, the Provider implements the snapshot method by displaying an empty state when the game state part has not yet fetched the state from the server.

struct GameStatusProvider: TimelineProvider {
    var hasFetchedGameStatus: Bool
    var gameStatusFromServer: String

    func snapshot(with context: Context, completion: @escaping (Entry)- > ()) {let date = Date(a)let entry: GameStatusEntry

        ifcontext.isPreview && ! hasFetchedGameStatus { entry =GameStatusEntry(date: date, gameStatus: "—")}else {
            entry = GameStatusEntry(date: date, gameStatus: gameStatusFromServer)
        }
        completion(entry)
    }
Copy the code

After requesting the initial snapshot, WidgetKit calls timeline(for:with: Completion 🙂 to ask the provider for a regular timeline. A timeline consists of one or more timeline entries and an overload policy that tells WidgetKit when to request subsequent timelines. The following example shows how the Provider for the game state widget generates a timeline consisting of a single entry containing the current game state from the server and a reload strategy to request a new timeline within 15 minutes.

struct GameStatusProvider: TimelineProvider {
    func timeline(with context: Context, completion: @escaping (Timeline<GameStatusEntry>)- > ()) {// Create a timeline entry for "now".
        let date = Date(a)let entry = GameStatusEntry(
            date: date,
            gameStatus: gameStatusFromServer
        )

        // Create a date for the next 15 minutes.
        let nextUpdateDate = Calendar.current.date(byAdding: .minute, value: 15, to: date)!

        // Create a timeline with entries & reload policy with dates.
        // For the next update.
        let timeline = Timeline(
            entries:[entry],
            policy: .after(nextUpdateDate)
        )

        // Call Completion to pass the timeline to WidgetKit.
        completion(timeline)
    }
}
Copy the code

In this case, if the Widget doesn’t have the current state from the server, can it store a reference to Completion that says no more updates? , makes an asynchronous request to the server to retrieve the game state, and calls Completion when the request is complete. For more details on generating timelines, including handling network requests in widgets, see Keeping a Widget Up To Date.

Display content in widgets

Widgets use the SwiftUI view to define their content, usually by calling other SwiftUI views. As shown in the figure above, the widget’s configuration contains closures called by WidgetKit to render the widget’s contents. When users add your Widget from the Widget library, they select a specific size (small, medium, or large) from the family your Widget supports. The Widget’s content must be able to render each of the specifications that the Widget supports. WidgetKit sets the size “Family” in the SwiftUI environment and additional properties, such as the color scheme (light or dark).

In the configuration of the game state Widget shown above, content is closed using GameStatusView to display the state. Since the Widget supports all three widget specifications, it uses widgetFamily to decide which particular SwiftUI view to display, as shown in the figure.

struct GameStatusView : View {@Environment(\.widgetFamily) var family: WidgetFamily // Get environment variables
    var gameStatus: GameStatus

    @ViewBuilder
    var body: some View {
        switch family {
        case .systemSmall: GameTurnSummary(gameStatus)
        case .systemMedium: GameStatusWithLastTurnResult(gameStatus)
        case .systemLarge: GameStatusWithStatistics(gameStatus)
        default: GameDetailsNotAvailable()}}}Copy the code

For small widgets, the view used by the Widget shows a simple summary of whose turn it is in the game. For medium, it shows the status, which represents the result of the previous round. For larger players, since there is more space, it can display each player’s run statistics. If the spec is of unknown type, the default view is displayed, indicating that the game state is not available.

Note

A view declares its body with @ViewBuilder because it uses a different type of view.

For configurable widgets, the provider conforms to the IntentTimelineProvider. This provider performs the same functionality as the TimelineProvider, but it combines user-defined values on the widget. These custom values are available to the Intent Timeline provider in the configuration parameters passed to snapshot(for:with: Completion 🙂 and timeline(for:with:completion:). You typically include user-configured values as properties of your custom timeline entry type for use by the widget’s view.

Important

Widgets display only read-only information and do not support interactive elements such as scroll elements or switches. WidgetKit omits the interactive elements when rendering the contents of the widget.

When the user interacts with your widget, WidgetKit activates your app and passes a URL you specify. When your application is active, handle the URL by taking the user to the relevant location.

Add dynamic content to widgets

While the widget’s display is a view-based snapshot, you can use various SwiftUI views that are constantly updated as your widget is visible. For more information about providing dynamic content, see “Keep Widgets up to date.”

Declare multiple widgets in an application extension

The GameStatusWidget example above uses the @main attribute to specify a single entry point for the widget extension. To support multiple widgets, declare a widgetBundle-compliant structure that groups widgets in its body property. Add the @main attribute to the Widget bundle structure to tell WidgetKit that your extension supports multiple widgets. For example, if the game app has a second widget to show the health of the character and a third widget to show the leaderboard, it will group them like here.

@main
struct GameWidgets: WidgetBundle {@WidgetBundleBuilder
    var body: some Widget {
        GameStatusWidget(a)CharacterDetailWidget(a)LeaderboardWidget()}}Copy the code

2. Make a configurable widget

Give users the option to customize their widgets by adding custom SiriKit intent definitions to your project.

An overview of the

To make it easy for users to access the most relevant information, widgets can provide customizable properties. For example, a user can select a specific stock for a stock quote widget or enter a tracking number for a package delivery widget. Widgets define customizable properties by using custom intent definitions, the same mechanism Siri Suggestions and Siri shortcuts use to customize these interactions.

Add configurable properties to your widgets.

  1. Add a custom intent definition that defines configurable properties to your Xcode project.
  2. Use in your widgetIntentTimelineProviderTo incorporate the user’s choices into your timeline entry.
  3. If the property depends on dynamic data, implement oneIntentsExtension.

If your app already supports Siri Suggestions or Siri shortcuts, and you have a custom intent, you’ve probably done most of the work. Otherwise, consider using the work you’ve done for your widget to add support for Siri suggestions or Siri shortcuts. For more information on how to make the most of intent, see SiriKit. The next section describes how to add configurable properties to widgets that display information about the characters in the game.

Add custom intent definitions for your project

In your Xcode project, select File > New File and select SiriKit Intent Definition File. Click “Next” and save the file when prompted. Xcode creates a new.intenttDefinition file and adds it to your project.

Xcode generates code from an “Intent Definition file” file. Use this code in a target “target”.

  • Treat the Intent definition file as a member of the target.
  • Specify the specific Intents to include in the target by adding the class name of the intent to the Supported Intents section of the target attribute.

If you include the Intent definition file in the framework, you must also include it in the target that contains the application. In this case, to avoid type duplication in the application and framework, select “Do not generate Classes” for the application target in the Target Members section of the File inspector.


Add and configure a custom intent that lets the user select a character in the game.

  1. In the project navigator, select the intent file. Xcode displays an empty intent-definition editor.
  2. Select Editor > New Intent and select the Intent under Custom Intents.
  3. Change the name of the custom intent to SelectCharacter. Notice that the Custom Class field of the Property Inspector shows the class name that you use when referencing the intent in your code. In this case, it is SelectCharacterIntent.
  4. Set the Category to View, and select the “Intent is eligible for Widgets “check box to indicate that the Intent is eligible for widgets.
  5. Under “Parameters “, add a new parameter named as a string, which is a configurable setting for the widget.

After you add a parameter, configure the details for it. If a parameter provides the user with a static list of choices, select the Add Enumeration menu item to create a static enumeration.

For example, if a parameter specifies the avatars of a character, and the list of possible avatars is an immutable set, you can use a static enumeration to specify the available choices in the intent definition file.

If the list of possible avatars can change, or is dynamically generated, use a type with dynamic options instead.

In this case, the character properties depend on the dynamic list of characters available in the application. To serve dynamic data, create a new type.

  1. Click “bottom left + button” and select “New Type”. Xcode adds a new type to the Types section of the editor.

  2. Change the name of the type to GameCharacter. Add a new name attribute and select String from the Type pop-up menu.

  3. Select SelectCharacter Intent.

  4. In the intent editor, select the “Options are dynamically provided” check box to indicate that your code provides a dynamic list of items for this parameter.

The GameCharacter type describes the characters that the user can select. In the next section, you’ll add code to dynamically supply the character list.

Note

The order of the parameters in the intent determines the order in which they are displayed when the user edits the widget. You can reorder by dragging items in the list.


Add an Intent extension for your project

To provide character lists dynamically, you need to add an Intents extension to your application. When the user edits the widget, WidgetKit loads the Intents extension to provide dynamic information.

Add an Intents extension.

  1. Select File > New > Target and select Intents Extension.

  2. Click “Next”.

  3. Enter a name for your Intents extension and set the Starting Point to None.

  4. Click “Finish”. If Xcode prompts you to activate the new schema, click Activate.

  5. Click the root object to find the newly created object on Targets in the left sidebar. In its “General” TAB, add an entry in the “Supported Intents” section and set “Class Name” to “SelectCharacterIntent”.

  6. In the Project Navigator, select the custom intent definition file that you added earlier.

  7. Use the File Inspector to add the Intent File to the Intent extension target.

Important

In the file inspector, verify that the included application, widget extension, and Intent extension all contain the Intent Intent Definition file.

Implement an intent handler to provide dynamic values

When users edit widgets with custom intents that provide dynamic values, the system needs an object to provide those values. It identifies this object by requiring the Intents extension to provide a handler for the intent.

When Xcode creates the Intents extension, it adds a file called Intenthandler. swift to your project, which contains a class called IntentHandler. This class contains a method that returns a handler. You will extend this handler to provide values for widget customization.

Based on the intention of the custom definition file, Xcode will generate a protocol, namely SelectCharacterIntentHandling, the handler must comply with this agreement. Add this conformance to the IntentHandler class declaration. (to view the details of the agreement, and other types of Xcode automatically generated, please select SelectCharacterIntentHandling and select Navigate > Jump to Definition).

class IntentHandler: INExtension.SelectCharacterIntentHandling {... }Copy the code

When a handler provides dynamic options, it must implement a method called provide[Type] OptionalCollection(for:with:), where [Type] is the Type name from the intent definition file. If this method is missing, Xcode reports a build error and provides a fix for adding protocol stubs.

Build your project and use fix-it to add the stub. This method includes a completion handler that you call, passing in an INObjectCollection. Note the GameCharacter type; This is a custom type in the intent definition file. Xcode generates the code that defines it as follows.

public class GameCharacter: INObject {
    @available(iOS 13.0, macOS 10.16, watchOS 6.0*),@NSManaged public var name: String?
}
Copy the code

Notice the name attribute, which also comes from the intent definition file for the custom type you added. In order to achieve the provideCharacterOptionsCollection (with that:) method, the widget USES a exists in the game in the project structure. This structure defines a list of available roles and their details, as shown below.

struct CharacterDetail {
    let name: String
    let avatar: String
    let healthLevel: Double
    let heroType: String

    static let availableCharacters = [
        CharacterDetail(name: "Power Panda", avatar: "🐼", healthLevel: 0.14, heroType: "Forest Dweller"),
        CharacterDetail(name: "Unipony", avatar: "🦄", healthLevel: 0.67, heroType: "Free Rangers"),
        CharacterDetail(name: "Spouty", avatar: "🐳", healthLevel: 0.83, heroType: "Deep Sea Goer")]}Copy the code

In the intent handler, iterate through the availableCharacters array to create a GameCharacter object for each role. For simplicity, a GameCharacter’s identity is the character’s name. The game character array is put into an INObjectCollection, which the handler passes to the completion handler.

class IntentHandler: INExtension.SelectCharacterIntentHandling {
    func provideCharacterOptionsCollection(for intent: SelectCharacterIntent, with completion: @escaping (INObjectCollection
        
         ? , Error?)
         -> Void) {

        // Iterate over the available characters
        // Each has a GameCharacter.
        let characters: [GameCharacter] = CharacterDetail.availableCharacters.map { character in
            let gameCharacter = GameCharacter(
                identifier: character.name,
                display: character.name
            )
            gameCharacter.name = character.name
            return gameCharacter
        }

        // Create a collection of character arrays.
        let collection = INObjectCollection(items: characters)

        // Call the completion handler to pass the collection.
        completion(collection, nil)}}Copy the code

Once you have configured the intent definition file and added the intent extension to your application, users can edit widgets to select specific characters to display. WidgetKit uses the information in the intent definition file to automatically create a user interface for editing widgets.

Once the user edits the widget and selects a character, the next step is to include that selection in the widget’s display.

Handles user-defined values

To support configurable properties, the widget uses the IntentTimelineProvider configuration. For example, the character-detail widget defines its configuration as follows.

struct CharacterDetailWidget: Widget {
    var body: some WidgetConfiguration {
        IntentConfiguration(
            kind: "com.mygame.character-detail",
            intent: SelectCharacterIntent.self,
            provider: CharacterDetailProvider(),
            placeholder: CharacterPlaceholderView()
        ) { entry in
            CharacterDetailView(entry: entry)
        }
        .configurationDisplayName("Character Details")
        .description("Displays a character's health and other details")
        .supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
    }
}
Copy the code

The SelectCharacterIntent parameter determines the user-customizable properties of the widget.

Configure to use the CharacterDetailProvider to manage the timeline events for the widget. For more information about timeline providers, see “Keep Widgets up to date.”

After the user edits the widget, WidgetKit passes the user-defined value to the provider when requesting the timeline entry. You typically include relevant details from the intent in the timeline entry generated by the provider.

In this example, the provider uses a helper method to look up CharacterDetail using the role name in the intent, and then creates a timeline of entries containing the role details.

struct CharacterDetailProvider: IntentTimelineProvider {
    func timeline(for configuration: SelectCharacterIntent, with context: Context, completion: @escaping (Timeline<CharacterDetailEntry>)- > ()) {// Access the custom properties of the intent
        let characterDetail = lookupCharacterDetail(for: configuration.character.name)

        // Constructs a timeline entry for the current date, with character details
        let entry = CharacterDetailEntry(date: Date(), detail: characterDetail)

        // Create a timeline and call the completion handler. Set it to Never - Never refresh
        // The policy indicates that the included application will use the WidgetCenter method.
        // Reload the widget timeline when details change
        let timeline = Timeline(entries: [entry], policy: .never)
        completion(timeline)
    }
}
Copy the code

When you include user-defined values in a timeline entry, your widget can display the appropriate content.

3. Keeping a Widget Up To Date

Plan a timeline for your widgets to display timely and relevant information using dynamic views and update the timeline as things change.

An overview of the

Widgets use the SwiftUI view to display their content. WidgetKit renders the view on your behalf in a separate process. Therefore, your widget extension will not remain active even if the widget is on screen. Although your widgets are not always active, there are several ways to keep their content up to date.

Generate a timeline for predictable events

Many widgets have predictable points in time at which it makes sense to update their content. For example, a widget that displays weather information can update the temperature hourly throughout the day. A stock market widget can update its content frequently during opening hours, but not at all during the weekend. By planning these times in advance, WidgetKit will automatically refresh your widget when the appropriate time arrives.

When you define your widget, you implement a custom TimelineProvider. WidgetKit takes a timeline from your provider and uses it to track when your widget is updated. A timeline is an array of TimelineEntry objects. Each entry in the timeline has a date and time, as well as additional information that the widget needs to display its view. In addition to the timeline entry, the timeline also specifies a refresh policy that tells WidgetKit when to request a new timeline.

Here is an example of a game widget that shows a character’s health level.

When health falls below 100%, the character regenerates at a rate of 25% per hour. For example, when a character’s health level is 25%, it takes 3 hours to fully recover to 100%. The following figure shows how WidgetKit requests the timeline from the provider, rendering the widget at each time specified in the timeline entry.

When WidgetKit initially requests a timeline, the provider creates a timeline with four entries. The first entry represents the current time (Now), followed by three hourly entries. With the refresh policy set to the default atEnd, WidgetKit requests a new timeline after the last date in the timeline entry. When each date in the timeline arrives, WidgetKit calls the widget’s content closure to display the results. After the last timeline entry, WidgetKit repeats the process, asking the provider to provide a new timeline. Since the role has reached 100% health, the provider responds with a single entry and refresh policy set to Never for the current time. With this setup, WidgetKit does not request another timeline until the application uses WidgetCenter to tell WidgetKit to request a new timeline.

In addition to the atEnd and never policies, the provider can specify a different date if the timeline might change before or after reaching the end of the entry. For example, if a dragon will appear in two hours to fight a game character, the provider sets the reload strategy to after(_:), passing a date in the next two hours. The following figure shows how WidgetKit requests a new widget after rendering the widget at 2 hours.

Due to fighting dragons, it takes an extra 2 hours to reach 100% healing. The new timeline consists of two entries, one for the current time and one for the next 2 hours. The timeline specifies an atEnd for the refresh policy, indicating that no more known events are likely to change the timeline.

When the role has reached 100% health after two hours, WidgetKit asks the provider to provide a new timeline. Because the health of the role has been recovered, the provider generates the same final timeline as in the first figure above. When the user plays a game and the character’s health changes, the app uses WidgetCenter to let WidgetKit refresh the timeline and update widgets.

In addition to specifying a date before the end of the timeline, the provider can also specify a date after the end of the timeline. This is useful when you know that the state of the widget will not change until later. For example, the stock market widget could create a timeline when the market closes on Friday and specify when the market will open on Monday using the afterDate() refresh strategy. Because the market is closed over the weekend, there is no need to update the widget before the market opens.


Notify WidgetKit when the timeline changes

When something affects the current timeline of the widget, your application can tell WidgetKit to request a new timeline. In the game widget example above, if the application receives a push notification indicating that a teammate has given a healing potion to the character, the application can tell WidgetKit to reload the timeline and update the widget’s contents. To reload a particular type of widget, your application uses WidgetCenter, as shown here.

WidgetCenter.shared.reloadTimelines(ofKind: "com.mygame.character-detail")
Copy the code

The kind parameter contains the same string as the WidgetConfiguration value used to create the widget.

If your widget has user-configurable properties, avoid unnecessary reloads by using WidgetCenter to verify that a widget with the appropriate Settings exists.

For example, when the game receives a push notification that a character has received a healing potion, it verifies that a widget displays that character before reloading the timeline.

In the code below, the application calls getCurrentConfigurations(_:) to retrieve the list of widgets configured by the user. It then iterates over the resulting WidgetInfo object to find a part that has the intent configured by the role receiving the healing potion. If it finds one, the application calls reloadTimelines(ofKind:) for the widget’s category.

WidgetCenter.shared.getCurrentConfigurations { result in
    guard case .success(let widgets) = result else { return }

    // Walk through the WidgetInfo element to find the elements that match.
    // The character from the push notification.
    if let widget = widgets.first(
        where: { widget in
            let intent = widget.configuration as? SelectCharacterIntent
            returnintent? .character == characterThatReceivedHealingPotion } ) {WidgetCenter.shared.reloadTimelines(ofKind: widget.kind)
    }
}
Copy the code

If your application uses WidgetBundle to support multiple widgets, you can use WidgetCenter to reload the timeline of all widgets. For example, if your widget requires users to log in to an account, but they have logged out, you can reload all the widgets with a call.

WidgetCenter.shared.reloadAllTimelines()
Copy the code

Display dynamic date

Because your widget extension is not always running, you cannot directly update the contents of the widget. Instead, WidgetKit renders the view of your widget on your behalf and displays the results. However, some SwiftUI views allow you to display continuously updated content while your widget is visible.

Using text view in your widget, you can display the date and time on the screen to keep it up to date.

The following example shows the combinations available.

To display the relative time of automatic updates

let components = DateComponents(minute: 11, second: 14)
let futureDate = Calendar.current.date(byAdding: components, to: Date())!

Text(futureDate, style: .relative)
// Displays:
// 11 min, 14 sec

Text(futureDate, style: .offset)
// Displays:
// -11 minutes
Copy the code

Use the.relative style to show the absolute difference between the current date and time and the specified date, whether the date is in the future or in the past.

The offset style (.offset) shows the difference between the current date and time and the specified date, with a minus (-) prefix for future dates and a plus (+) prefix for past dates.

To continue displaying the auto-update timer.

let components = DateComponents(minute: 15)
let futureDate = Calendar.current.date(byAdding: components, to: Date())!

Text(futureDate, style: .timer)
// Displays:
/ / 15:00
Copy the code

For future dates, the timer is displayed as a countdown time until the current time reaches the specified date and time, and counts up after the date has passed.

Display absolute date or time.

// Absolute Date or Time
let components = DateComponents(year: 2020, month: 4, day: 1, hour: 9, minute: 41)
let aprilFirstDate = Calendar.current(components)!

Text(aprilFirstDate, style: .date)
Text("Date: \(aprilFirstDate, style: .date)")
Text("Time: \(aprilFirstDate, style: .time)")

// Displays:
// April 1, 2020
// Date: April 1, 2020
// Time: 9:41AM
Copy the code

Finally, the time interval between the two dates is displayed.

let startComponents = DateComponents(hour: 9, minute: 30)
let startDate = Calendar.current.date(from: startComponents)!

let endComponents = DateComponents(hour: 14, minute: 45)
let endDate = Calendar.current.date(from: endComponents)!

Text(startDate ... endDate)
Text("The meeting will take place: \(startDate ... endDate)")

// Displays:
// 9:30AM-2:45PM
// The meeting will take place: 9:30AM-2:45PM
Copy the code

Use the map view to display the location

The widget displays the map using SwiftUI MapView. To display a map in a widget, do the following.

  1. willUIWidgetWantsLocationThe key is added to your widget extension’s info.plist file with a value of true.
  2. Include usage-purpose strings in the info.plist file that contains the application.
  3. Request permission to access location information in the containing application.
  4. For more information about requesting location authorization and using destination strings, see Requesting Authorization for Location Services.

Update the background network request after completion

It can initiate background network requests when your widget extension is active, such as when providing a snapshot or timeline.

This process is similar to how an application handles this type of request, described in “Downloading files in the background.”

Instead of restoring your app, WidgetKit directly activates your widget’s extension.

To the result of the request processing network, use the onBackgroundURLSessionEvents (matching: _ 🙂 modifier to your widget configuration, and perform the following actions.

  • Store a reference to the completion parameter. You call the completion handler after you have processed all the network events.

  • Use the identifier argument to find the URLSession object that you use when making background requests. If your widget extension is terminated, recreate the URLSession using the identifier.

    After the call onBackgroundURLSessionEvents (), System will call you to provide URLSession URLSessionDelegate URLSession (_ : downloadTask: didFinishDownloadingTo:) method. When all the events are delivered, the system will call the client urlSessionDidFinishEvents (forBackgroundURLSession:) method. After completion of the network request to refresh your widget timeline, from your client urlSessionDidFinishEvents implementation call WidgetCenter method. As soon as you finish handling these events before calling stored in onBackgroundURLSessionEvents completion handler in the ().

    If you could see this, that would be the biggest encouragement for me. Thanks for reading, see you later (._.)