WidgetKit Series 4 : Creating a Dynamic Day of the Week Widget in SwiftUI

In this tutorial, we'll walk through the process of creating a dynamic widget for iOS that displays the current day of the week along with a corresponding emoji. This widget updates throughout the week, providing a fun and informative addition to your users' home screens.

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. DateEntry: Represents the data for each widget update.
  3. WidgetTestWidgetEntryView: Defines the widget's user interface.
  4. WidgetTestWidget: Configures the widget itself.

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())
    }

    func getSnapshot(in context: Context, completion: @escaping (DateEntry) -> ()) {
        let entry = DateEntry(date: Date())
        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)
            entries.append(entry)
        }

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

This provider creates a timeline with entries for the next 5 hours, ensuring our widget stays up-to-date throughout the day.

The DateEntry

Our DateEntry struct holds the date information and provides a computed emoji property based on the day of the week:

struct DateEntry: TimelineEntry {
    let date: Date

    var emoji: String {
        let weekday = Calendar.current.component(.weekday, from: date)
        switch weekday {
        case 1: return "😎" // Sunday
        case 2: return "😫" // Monday
        case 3: return "😊" // Tuesday
        case 4: return "🤓" // Wednesday
        case 5: return "🤔" // Thursday
        case 6: return "🥳" // Friday
        case 7: return "😴" // Saturday
        default: return "🤖"
        }
    }
}

This clever use of a computed property allows us to display a different emoji for each day of the week.

The Widget View

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 {
                Text("\(entry.emoji)")
                    .font(.system(size: 50))
                Text(entry.date.formatted(.dateTime.weekday(.wide)))
                    .font(.title3)
                    .fontWeight(.bold)
                    .foregroundStyle(.white.opacity(0.7))

                Text(entry.date.formatted(.dateTime.day()))
                    .font(.largeTitle)
                    .bold()
                    .foregroundStyle(.white.opacity(0.7))
            }
            .padding()
        }
    }
}

This view creates a visually appealing widget with a gradient background, the day's emoji, the name of the day, and the date.

The Widget Configuration

Finally, we define our widget using the WidgetTestWidget struct:

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 with the appropriate provider and view, and includes compatibility checks for different iOS versions.

Conclusion

With this code, we've created a dynamic, visually appealing widget that displays the current day of the week along with a fun emoji. The widget updates throughout the week, providing users with a quick and enjoyable way to check the day at a glance.

Remember to add this widget to your app's target and enable it in the capabilities section of your project settings. Happy coding!