Updating time text label each minute in WidgetKit

Solution 1:

A possible solution is to use the time date style:

/// A style displaying only the time component for a date.
///
///     Text(event.startDate, style: .time)
///
/// Example output:
///     11:23PM
public static let time: Text.DateStyle

  1. You need a simple Entry with a Date property:
struct SimpleEntry: TimelineEntry {
    let date: Date
}
  1. Create an Entry every minute until the next midnight:
struct SimpleProvider: TimelineProvider {
    ...

    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
        var entries = [SimpleEntry]()
        let currentDate = Date()
        let midnight = Calendar.current.startOfDay(for: currentDate)
        let nextMidnight = Calendar.current.date(byAdding: .day, value: 1, to: midnight)!

        for offset in 0 ..< 60 * 24 {
            let entryDate = Calendar.current.date(byAdding: .minute, value: offset, to: midnight)!
            entries.append(SimpleEntry(date: entryDate))
        }

        let timeline = Timeline(entries: entries, policy: .after(nextMidnight))
        completion(timeline)
    }
}
  1. Display the date using the time style:
struct SimpleWidgetEntryView: View {
    var entry: SimpleProvider.Entry

    var body: some View {
        Text(entry.date, style: .time)
    }
}

If you want to customise the date format you can use your own DateFormatter:

struct SimpleWidgetEntryView: View {
    var entry: SimpleProvider.Entry
    
    static let dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.dateFormat = "HH:mm"
        return formatter
    }()

    var body: some View {
        Text("\(entry.date, formatter: Self.dateFormatter)")
    }
}

Here is a GitHub repository with different Widget examples including the Clock Widget.

Solution 2:

Thanks very much for @pawello222's answer, but the problem with this solution is that too many stuff (24 * 60) are saved in the Timeline.

Once the widget's view contains a lot of elements (like 5 or more Text(...) in my case), then the widget will completely stop rendering on the ios real device, and xcode will report this:

2020-12-03 11:22:46.094442+0800 PixelClockWidgetExtension[660:53387] 
[default] -[EXSwiftUI_Subsystem beginUsing:withBundle:] unexpectedly called multiple times.

I think one possible solution is to divide the daily time into several small pieces and save them in the Timeline multiple times.

My Environment:

  • ios 14.2
  • Xcode 12.2
  • iPhone 12 pro