I have a watchOS complication that shows information about what's happening either on the current day or on some future day. As with any complication, I have a CLKComplicationDataSource
set up with various methods including getCurrentTimelineEntryForComplication:withHandler:
and getTimelineEntriesForComplication:afterDate:limit:withHandler:
. When these methods are called, I create a CLKComplicationTimelineEntry
using a CLKComplicationTemplate
subclass that's appropriate for the complication type.
The "Modular Large" complication type (CLKComplicationFamilyModularLarge
) is the one that's giving me the most trouble. With this type I want the heading to say "Today" or "Tomorrow" when it's appropriate. Below that will be some text about what's happening that day. Currently I'm using CLKRelativeDateTextProvider
for this:
template.headerTextProvider = [CLKRelativeDateTextProvider textProviderWithDate:date style:CLKRelativeDateStyleNatural units:NSCalendarUnitDay];
This doesn't quite do what I want though—it shows something like "0DAYS" or "1DAY". That's acceptable for a smaller complication, but with Modular Large it looks especially awkward. You don't even get a space after the number, no matter how much extra room there is.
I thought I could fix this by just using an NSDateFormatter
to generate the string, then using a CLKSimpleTextProvider
to display it. Seemed simple enough:
NSDateFormatter *formatter = [NSDateFormatter new];
formatter.doesRelativeDateFormatting = YES;
formatter.dateStyle = NSDateFormatterShortStyle;
formatter.timeStyle = NSDateFormatterNoStyle;
NSString *string = [formatter stringFromDate:date];
But there's a problem: a watchOS complication needs to be able to supply data for dates in the future. As far as I can tell, NSDateFormatter
has no way to tell it what date you want the output to be relative to—it's always relative to the current date. So if the date for the complication data is tomorrow, and getTimelineEntriesForComplication:afterDate:limit:withHandler:
is called, then all the timeline entries I generate will say "Tomorrow". That will be right for the current day, but when midnight hits, it'll be wrong. It will still say "Tomorrow" until all the complication data is generated again.
I've only been able to think of a couple solutions to this problem, and they both have drawbacks:
I could calculate how many days away the date is and just give CLKSimpleTextProvider
the string "Today" or "Tomorrow" when it's appropriate. My main concern here is that I use NSDateFormatter
elsewhere in my app, and I'd like to be consistent with that. With various languages to account for that's not trivial. Just to give one simple example, French uses "après-demain", meaning "the day after tomorrow", which has no real English equivalent.
I could give NSDateFormatter
the wrong date, forcing it to give me the output I want. Let's say the complication date is June 14th, and it's currently June 13th. When my app is creating a timeline entry for the current day, it calls my date formatter using with the date June 14th ("Tomorrow"). But when it needs to create timeline data past midnight, it calls the date formatter with the date June 13th ("Today"). This seems like a terrible idea that's bound to break in some situation like a leap year or time change or something. I already don't like using anything other than CLKRelativeDateTextProvider
, but I really don't like this.
That said, I'm not sure what else I could do. I have already filed a Radar (27267550: CLKRelativeDateTextProvider should offer more control) asking for more date formatting options. It's a year old and shows no sign of being addressed in watchOS 4.
What's the best way to solve this?
If you go the route of calculating how many days away it is, you could use a localized .stringsdict and get a localized string based on the number. For English it could be:
<key>zero</key>
<string>Today</string>
<key>one</key>
<string>Tomorrow</string>
...
<key>other</key>
<string>%d days</string>
And then for other languages you can add specific rules, like French's "après-demain" for two.
Feels like a pretty unsatisfying option since you're now taking on the responsibility of managing every language and each possible way of describing future days, but it could work.