Creating a Simple Widget in SwiftUI: A Step-by-Step Tutorial

In this tutorial, we'll walk through the process of creating a basic widget for iOS using SwiftUI and the WidgetKit framework. We'll build a widget that displays the current time and a fun emoji. Let's dive in!

Step 1: Setting Up the Widget

First, we need to create a new target for our widget. In Xcode, go to File > New > Target and select "Widget Extension". Name it "WidgetTestWidget" and make sure to select SwiftUI as the interface.

Step 2: Implementing the TimelineProvider

The TimelineProvider is responsible for providing the content for our widget. Let's break down the Provider struct:

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), emoji: "🤓")
    }

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

    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        var entries: [SimpleEntry] = []

        let currentDate = Date()
        for hourOffset in 0 ..< 5 {
            let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
            let entry = SimpleEntry(date: entryDate, emoji: "🤓")
            entries.append(entry)
        }

        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
    }
}
  • The placeholder function provides a sample entry for when the widget is first added.
  • getSnapshot is called when the system needs a quick snapshot of the widget's state.
  • getTimeline generates a timeline of entries. In this case, we're creating 5 entries, each an hour apart.

Step 3: Defining the Entry

The SimpleEntry struct represents a single instance of our widget's content:

struct SimpleEntry: TimelineEntry {
    let date: Date
    let emoji: String
}

Step 4: Creating the Widget View

Now, let's design how our widget will look:

struct WidgetTestWidgetEntryView : View {
    var entry: Provider.Entry

    var body: some View {
        VStack {
            Text("Hello Welcome to William Jiamin's Channel!")
            Text("Time:")
            Text(entry.date, style: .time)
            Text("Emoji:")
            Text(entry.emoji)
        }
    }
}

This view displays a welcome message, the current time, and an emoji.

Step 5: Configuring the Widget

Finally, we'll set up the main widget structure:

struct WidgetTestWidget: Widget {
    let kind: String = "WidgetTestWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: Provider()) { entry in
            if #available(iOS 17.0, *) {
                WidgetTestWidgetEntryView(entry: entry)
                    .containerBackground(.fill.tertiary, for: .widget)
            } else {
                WidgetTestWidgetEntryView(entry: entry)
                    .padding()
                    .background()
            }
        }
        .configurationDisplayName("My Widget")
        .description("This is an example widget.")
    }
}

Here, we're using a StaticConfiguration and providing our custom view. We also add a background color for iOS 17 and above.

Step 6: Adding a Preview

To see how our widget looks, we can add a preview:

#Preview(as: .systemSmall) {
    WidgetTestWidget()
} timeline: {
    SimpleEntry(date: .now, emoji: "🤓")
    SimpleEntry(date: .now, emoji: "😎")
}

This preview shows how the widget will appear in the small size, with two different emojis.

Conclusion

And there you have it! We've created a simple but functional widget that displays the current time and a fun emoji. This widget updates every hour, showing how dynamic content can be displayed in iOS widgets.

Remember to add your widget to your app's capabilities and test it thoroughly on different devices and in different sizes. Happy coding!