Widgetkit Series 4-5: Creating a Date and Emoji Widget in SwiftUI
In this tutorial, we'll walk through the process of creating a custom widget for iOS using SwiftUI and WidgetKit. Our widget will display the current date along with a corresponding emoji for each day of the week. Let's break down the code and explain each part in detail.
Setting up the Provider
We start by creating a Provider
struct that conforms to the TimelineProvider
protocol. This provider is responsible for generating the timeline of our widget's content.
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 dayOffset in 0 ..< 7 {
let entryDate = Calendar.current.date(byAdding: .day, value: dayOffset, to: currentDate)!
let startOfDate = Calendar.current.startOfDay(for: entryDate)
let entry = DateEntry(date: startOfDate)
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
The getTimeline
function generates a timeline of seven entries, one for each day of the week, starting from the current date.
Creating the DateEntry
Next, we define a DateEntry
struct that conforms to the TimelineEntry
protocol. This struct represents the data for each widget update.
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 "🤖"
}
}
}
The emoji
computed property returns a different emoji based on the day of the week.
Designing the Widget View
The WidgetTestWidgetEntryView
struct defines the layout and appearance of our widget.
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 displays the emoji, weekday name, and day of the month on a gradient background.
Configuring the Widget
Finally, we create the WidgetTestWidget
struct to configure our widget.
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("Our Test Widget")
.description("This is Our Test Widget for date and emoji")
.contentMarginsDisabled()
.supportedFamilies([.systemSmall])
}
}
This configuration sets up the widget with a display name, description, and supported size (small).
Preview
To help with development, we've included a preview that shows how the widget looks for each day of the week:
#Preview(as: .systemSmall) {
WidgetTestWidget()
} timeline: {
DateEntry(date: Calendar.current.date(from: DateComponents(year: 2024, month: 9, day: 1))!) // Sunday
DateEntry(date: Calendar.current.date(from: DateComponents(year: 2024, month: 9, day: 2))!) // Monday
DateEntry(date: Calendar.current.date(from: DateComponents(year: 2024, month: 9, day: 3))!) // Tuesday
DateEntry(date: Calendar.current.date(from: DateComponents(year: 2024, month: 9, day: 4))!) // Wednesday
DateEntry(date: Calendar.current.date(from: DateComponents(year: 2024, month: 9, day: 5))!) // Thursday
DateEntry(date: Calendar.current.date(from: DateComponents(year: 2024, month: 9, day: 6))!) // Friday
DateEntry(date: Calendar.current.date(from: DateComponents(year: 2024, month: 9, day: 7))!) // Saturday
}
This preview allows you to see how your widget will look for each day of the week without having to wait for the actual days to pass.
And there you have it! A fun and informative widget that displays the current date with a matching emoji. Feel free to customize the colors, fonts, and emojis to suit your preferences.