Timers in widgets, how to calculate correct time

I have a Home Screen widget that contains a timer counting down to a specific date. In this image you can see the date calculations:

  • eventDate: 25th Dec 2023 at 09:00:00
  • entryDate(Date.now): This is just showing you the date in the first SimpleEntry for the widget.
  • getTimeRemaining(entryDate): This shows the number of seconds from the entryDate to the eventDate, figures out how many days there are ("43 days"), and how many hours:mins:secs to the event time, so "10:48:52".
  • Then there's a second entry, entryDate2, that's one hour later, and the values are appropriately calculated.

When I create the timeline entries, I add them for:

  • Now (1 entry)
  • Now plus one hour to a week away (one entry per hour = 167 entries)
  • Event date (1 entry)
  • Event date plus one hour to a week later (one entry per hour = 167 entries)

Each SimpleEntry entry contains a dictionary with the relevant timer details, and that's what the widget uses to determine what to display in the timer.

SwiftUI lacks any useful formatting for a timer. Even the developer docs state: "Example output: 36:59:01". Who wants to see a timer with 36 hours on it? I want it to say "1 day 12:59:01", so I munge the numbers about and grab the separate parts, converting 36:59:01 into "1 day" and "12:59:01". You can see that in the image above.

When the entry date of the timeline is reached and the widget is redrawn, it uses the entry containing the dictionary saying it should display "43 days" and the countdown timer should be 10:48:52, then an hour later the dictionary says it's 43 days 9:48:52, etc.

The issue is that the widgets, even though they're supposed to have entries at each hour, will always end up displaying something like "29:17:09". The timeline has an entry at every hour, so it should be using the right values. I've checked, and the right values are in the dictionary, so why does the timer keep getting out of sync?

I could cut out a massive amount of my widget code if only Text.init(date, style: .timer) would allow some proper formatting.

Replies

You are 100% right that the timer text field desperately needs formatting controls. I could delete a massive amount of code if it would just stop including the seconds field.

As to your problem, keep in mind that since you have no control over when the timeline generation code runs, it is almost certainly not happening right on a minute mark. Before you start generating your timeline entries take “now” and remove the seconds part of the time. the easiest way to do this is break the date into components and then rebuild it with the seconds hard-coded to zero. Start with that date and continue on. That will make all of the timeline entries at exactly the minute mark

Yeah, @jcgoforth, I see what you're saying, but even if I remove the seconds the timers get hours out of sync, i.e. "29:17" (29 hours 17 mins). With a timeline entry every hour - even if were a few minutes out in refreshing it - it should calculate the right time, and never be more than 24 hours.

I raised a request to Apple to have proper formatting in timers, and make them more suitable for widgets, but it's fallen on deaf ears.