WidgetKit Series 3 : Solve the padding issue contentMarginsDisabled

In this tutorial, we'll walk through the process of creating a basic widget for iOS using SwiftUI and WidgetKit. We'll build a widget that displays the current date and an emoji, updating every hour.

Setting Up the Widget

First, let's break down the main components of our widget:

  1. Provider: Manages the timeline of our widget's content.
  2. Entry: Defines the data structure for our widget's content.
  3. EntryView: Describes the layout and appearance of our widget.
  4. Widget: Configures and presents our widget.

The Provider

The Provider struct conforms to TimelineProvider and is responsible for generating the content timeline for our widget:

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

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

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

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

        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
    }
}

This provider creates a timeline with five entries, each an hour apart.

The Entry

Our DateEntry struct defines the data for each timeline entry:

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

The EntryView

The WidgetTestWidgetEntryView struct defines how our widget looks:

struct WidgetTestWidgetEntryView : View {
    var entry: DateEntry

    var body: some View {
        ZStack {
            ContainerRelativeShape()
                .fill(LinearGradient(
                    gradient: Gradient(colors: [.cyan, .green]),
                    startPoint: .topLeading,
                    endPoint: .bottomTrailing
                ))

            VStack {
                HStack {
                    Text("\(entry.emoji)")
                    Text(entry.date.formatted(.dateTime.weekday(.wide)))
                }
            }
        }
    }
}

This view creates a gradient background and displays the emoji and weekday.

The Widget

Finally, we define our widget using the Widget protocol:

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(Color.black)
            }
        }
        .configurationDisplayName("My Widget")
        .description("This is an example widget.")
        .contentMarginsDisabled()
    }
}

This configuration sets up our widget, handling both iOS 17+ and earlier versions.

Preview

To see how our widget looks, we can use the Preview provider:

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

This preview shows our widget in the small size with two different emojis.

Conclusion

And there you have it! We've created a simple yet functional widget that displays the current weekday and an emoji, updating every hour. This example demonstrates the basics of working with WidgetKit and SwiftUI to create engaging, glanceable experiences for iOS users.

Remember, widgets are meant to provide quick, relevant information. As you build upon this example, consider what information would be most useful to your users at a glance.

Happy coding!