How to use the new Text timer formats?

I'm trying to get a countdown timer to work, and the way I currently do it in my watchOS 10 app is a complicated load of nonsense to get two Strings that look like "1w 1d" and "12:34:56", i.e. a String that shows components like year, week and day, and another String showing hours, minutes and seconds.

The new Text formats seen here https://developer.apple.com/wwdc24/10144?time=645 look useful, but I can't get them to return the values I need.

If I use this:

let dateA = Date.now
let dateB = Date.advanced(by: /* value for 8 days, 12 hours, 34 minutes and 56 seconds */)

Text(dateA, format: .offset(to: dateB, allowedFields: [.year, .week, .day], maxFieldCount: 3))

I expect to see "1 week 1 day", but it always comes back as "8 days". I guess it's giving me the most concise result, but I don't want that. I'm not sure anyone would want that.

Imagine you have an event coming up in 3 days 6 hours, do you want to see "in 78 hours" or do you want "in 3 days and 6 hours"? Why must we make the user calculate the days and hours in their head when we have the ability to give them the right information?

While I'm on this, why does the resulting String have to have "in " at the beginning? I don't want that; it's not my use case, but it's forced on me.

I've raised this a hundred times with Apple. I just want to see a String that shows the difference between two dates in a format of my choosing, i.e. "1w 1d", but they never give me what I need, so I have to write extremely complex - and fragile - code that figures this stuff out, and I still can't get that to work properly.

Why can't we just have something like:

Text(from: dateA, to: dateB, format: "%yy %ww %dd") // for "1 year 2 weeks 3 days", show parts with a value > 0

Text(from: dateA, to: dateB, format: "%0yy %0ww %0dd") // for "0 years 2 weeks 3 days", show all parts regardless of value

Text(from: dateA, to: dateB, format: "%y %w %d") // for "1y 2w 3d", show parts with a value > 0

Text(from: dateA, to: dateB, format: "%0y %0w %0d") // for "0y 2w 3d", show all parts regardless of value
Answered by DTS Engineer in 796533022

@darkpaw The example below should return the format you expect "1 week 1 day":

var futureDate: Date {
        Calendar.current.date(byAdding: .day, value: 8, to: .now) ?? .now
    }

Text(.now, format: .offset(to: futureDate, allowedFields: [.year, .week, .day], maxFieldCount: 3))
Accepted Answer

@darkpaw The example below should return the format you expect "1 week 1 day":

var futureDate: Date {
        Calendar.current.date(byAdding: .day, value: 8, to: .now) ?? .now
    }

Text(.now, format: .offset(to: futureDate, allowedFields: [.year, .week, .day], maxFieldCount: 3))

Hi, DTS Engineer,

That works, thank you, but it still needs improvement.

This Text can only be one line in my View, and it's currently showing "21 weeks, 5 da...". I need it to say "21w 5d".

AFAICS the string is computed in the Text then displayed. Is there a way of intercepting this string and formatting it as I need?

Without a simple way of formatting the output this will probably involve a lot of stupid string replacements to replace " week" and " weeks" with "w", and then I have to handle it for other languages, too. And I have to remove that comma.

As I mentioned above, we really need to be able to format this sort of string. There doesn't seem to be any reason why we are forced to use this one particular format.

I've raised FB14499568.

How to use the new Text timer formats?
 
 
Q