WidgetKit is Apple’s framework that allows developers to create widgets for the home screen and Today view on iOS devices. Widgets provide glanceable information and can be interactive, allowing users to engage with content directly from the home screen. This guide will help you understand the basics of WidgetKit and how to create a simple widget for an iOS app.

Prerequisites

  • Xcode 12 or later
  • Basic knowledge of Swift and SwiftUI

Step 1: Setting Up the Project

  1. Create a New Xcode Project:
    • Open Xcode and create a new project.
    • Select the “App” template.
    • Name your project (e.g., “MyWidgetApp”).
    • Choose Swift as the language and SwiftUI as the user interface.
  2. Add a Widget Extension:
    • Go to File > New > Target.
    • Choose the “Widget Extension” template.
    • Name your widget (e.g., “MyWidget”).
    • Make sure “Include Configuration Intent” is unchecked for this simple example.

Step 2: Understanding the Widget Structure

When you create a widget extension, Xcode generates several files. The main files to focus on are:

  • MyWidget.swift
  • MyWidgetEntryView.swift
  • MyWidgetTimelineProvider.swift

Step 3: Creating the Widget Timeline Provider

The timeline provider is responsible for providing the timeline entries that define the content of your widget. For this example, we’ll create a simple provider that shows the current date and time.

  1. MyWidgetTimelineProvider.swift:
import WidgetKit
import SwiftUI

struct SimpleEntry: TimelineEntry {
    let date: Date
}

struct MyWidgetTimelineProvider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date())
    }

    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> Void) {
        let entry = SimpleEntry(date: Date())
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<SimpleEntry>) -> Void) {
        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)
    }
}

Step 4: Designing the Widget View

The widget view displays the content defined by the timeline entries. We’ll create a simple view that shows the current date and time.

  1. MyWidgetEntryView.swift:

import WidgetKit
import SwiftUI

struct MyWidgetEntryView: View {
    var entry: MyWidgetTimelineProvider.Entry

    var body: some View {
        VStack {
            Text("Current Time")
                .font(.headline)
            Text(entry.date, style: .time)
                .font(.largeTitle)
        }
        .padding()
    }
}

@main
struct MyWidget: Widget {
    let kind: String = "MyWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: MyWidgetTimelineProvider()) { entry in
            MyWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("My Widget")
        .description("This is a simple widget showing the current time.")
        .supportedFamilies([.systemSmall, .systemMedium])
    }
}

Step 4: Designing the Widget View

The widget view displays the content defined by the timeline entries. We’ll create a simple view that shows the current date and time.

  1. MyWidgetEntryView.swift:

import WidgetKit
import SwiftUI

struct MyWidgetEntryView: View {
    var entry: MyWidgetTimelineProvider.Entry

    var body: some View {
        VStack {
            Text("Current Time")
                .font(.headline)
            Text(entry.date, style: .time)
                .font(.largeTitle)
        }
        .padding()
    }
}

@main
struct MyWidget: Widget {
    let kind: String = "MyWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: MyWidgetTimelineProvider()) { entry in
            MyWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("My Widget")
        .description("This is a simple widget showing the current time.")
        .supportedFamilies([.systemSmall, .systemMedium])
    }
}

Step 5: Adding a Widget to Your App

  1. Running the Widget:
    • Build and run your app on an iOS device or simulator.
    • Go to the home screen and enter the widget editing mode (long press on the home screen and tap the “+” button).
    • Find your widget in the widget gallery and add it to your home screen.

Step 6: Customizing the Widget

Widgets can be customized with different sizes and configurations. Here are a few customization options:

  1. Multiple Sizes:
    • In the MyWidget.swift file, you can specify which widget sizes your widget supports.
    • supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
  2. Dynamic Content:
    • You can modify the SimpleEntry struct to include more data and update the view accordingly.
    • For example, add a String property for a message and display it in the view.
  3. User Configurations:
    • You can create configurable widgets by enabling the “Include Configuration Intent” option when adding a widget target.
    • This allows users to customize the widget’s content through a configuration interface.

Creating a widget with WidgetKit involves setting up a timeline provider, designing the widget view, and configuring the widget in your app. Widgets can display dynamic, glanceable content on the home screen, providing a rich user experience. With the basics covered in this guide, you can now start exploring more advanced features and customizations for your widgets.