Monthly Archives: May 2015

Predictable Date Formatting

Note: see caveats at the bottom of this post. Some of my conclusions in the body of this post are wrong and motivated by a misunderstanding of NSDateFormatter’s documented behavior. I’m leaving the post here for reference because I still think it could help somebody trying to understand similar behavior in their own app, but don’t take the griping too seriously…

Apple’s NSDateFormatter class supports a method of converting a date to a string by use of a date format string. For example, the date format string I use in MarsEdit to supply dates in ISO8601 format to blog servers:


That “HH” is supposed to reflect the hour as a zero-padded number between 00 and 23. And it does, or at least it has, ever since I started using this formatting string in MarsEdit eight years ago.

Starting very recently, I think with 10.10.3 (Edit: nope, not new after all, see end of post), NSDateFormatter may return a string formatted for the user’s 12-hour clock preference, and with a troubling “am” or “pm” component embedded within. So instead of a bona fide standard ISO 8601 date for the above format, MarsEdit is now prone to receive something like this:

20150526T1:58:42 pmZ

Oops! And Ugh! The whole point of using NSDateFormatter’s dateFormat string, I thought, was to specifically generate strings that defy the user’s preferences, but that comply with a very specific set of rules. In fact, Apple encourages using date format strings in their documentation:

There are broadly speaking two situations in which you need to use custom formats:

	1. For fixed format strings, like Internet dates.
	2. For user-visible elements that don’t match any of the existing styles

Yes, internet dates! Thank you. Well, no thanks, I guess. The current documentation also goes on to offer some caveats, particularly with respect to iOS, where I guess users have been empowered to override the 12/24-hour clock setting for longer than they have on the Mac. And in general, they warn:

Although in principle a format string specifies a fixed format, by default NSDateFormatter still takes the user’s preferences (including the locale setting) into account.

The specific scenario where this crops up for me is if the user has set their Mac’s region to one that defaults to 24-hour time, but has then specifically chosen to uncheck the 24-hour time option:

Language Region

The behavior doesn’t occur, for example, if the user’s region defaults to 12-hour time as it does in the United States. It only occurs when a region’s defaults have been specifically overridden.

If you want predictable behavior from NSDateFormatter, you must set an explicit NSLocale on the formatter before requesting any string generation. I’m not sure it matters which locale you set, the key seems to be setting it to anything but the default to avoid this strange deference to the user’s default settings.

I’ll be fixing this by setting the locale on the NSDateFormatter to “en_US” because, being the very locale that my Mac is most often configured to use, I’ll be more likely to notice if the workaround stops working at some point in the future. I reported a bug (Radar 21105874) because it seems to be there should be a more straight-forward means of expressing to NSDateFormatter that you want to perform a very literal conversion, one that is guaranteed to not take into consideration any user-provided customizations of date and time formatting.

Hopefully this post will help other developers notice and repair the faulty handling of date strings in their apps, before too many of your customers run into the problem first!

Update: Many thanks to several people on Twitter noting that Apple specifically recommends using the “en_US_POSIX” locale for this purpose. I am still a bit annoyed that the behavior changed out from under me, but it sounds like setting the locale explicitly to this computer-y locale is the right solution for the long term.

Update 2: Well I made a few wrong assumptions before writing this post. After further testing I’ve confirmed the problematic “new” behavior is at least the case in 10.9.4 and possibly earlier as well. I’m now inclined to think this has been my bug all along, but I still think I’ll file a bug with Apple encouraging them to update the documentation to stress that setting a locale on the formatter is important.

Update 3: It turns out the documentation goes into some detail about the need to specify a locale, but I overlooked it because it was in a section about “parsing date strings” (not what I’m doing here). I filed Radar 21115452, requesting better documentation about the need to set a locale in the section pertinent to either parsing strings or generating them.

Right Storyboard, Wrong Platform

If, in haste, you inadvertently add a storyboard file to your Mac or iOS project from the wrong platform palette, you’ll end up with a storyboard that compiles and installs into the app bundle, but which products cryptic errors upon building and running. For example, a Mac storyboard lost in an iOS world:

*** Assertion failure in -[UIStoryboard initWithBundle:storyboardFileName:identifierToNibNameMap:designatedEntryPointIdentifier:], /SourceCache/UIKit_Sim/UIKit-3347.44.1/UIStoryboard.m:52

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: nibNameMap != nil'

The assertion above occurs when, internally to UIStoryboard, the Info.plist for your compiled “.storyboardc” file is consulted to determine its constituent “.nib” files. In the Mac case, the keys for storyboard Info.plist entries have an “NS” prefix, e.g. NSViewControllerIdentifiersToNibNames, whereas on iOS, it goes hunting for a UI-prefixed key: UIViewControllerIdentifiersToNibNames.

Granted, as soon as you proceeded to the next step, trying to populate the storyboard with UI elements that make sense for the platform, you would probably figure out your mistake. But if you’re just trying to get the ball rolling and end up immediately scratching your head over the failure, hopefully this blog post will have helped you figure out more quickly what was wrong.

I don’t think there’s any official way to change a created storyboard’s platform target. Best bet if you run into this is to delete the storyboard and recreate it from scratch, taking care to select the file template from the appropriate platform in Xcode’s “New File” panel.

Storyboard To Nib And Back

At some point along the way Xcode has consolidated the “Main Storyboard” and “Main Interface” fields pertaining to storyboard and nib files into a single “Main Interface” field that simply updates whichever of the pertinent Info.plist fields Xcode thinks you are working with.

The problem is that if you switch from storyboard to nib or back, then the value of the Info.plist entry is changed, but the key is not updated to reflect whether the new value is either a storyboard or a nib.

I’ve reported Radar 20954053 to Apple, requesting that Xcode should intuit from the file extension of the file named by “Main Interface” whether the Info.plist should advertise a storyboard or a nib.

In the mean time, if you switch from storyboard to nib or back, you need to manually update the Info.plist key to match: UIMainStoryboardFile if you’re using storyboards, or NSMainNibFile if you’re using nibs.

Auto Layout: Copy First, Then Edit

Here’s a possibly-too-obvious tip for folks who are adapting older iOS and Mac UI from traditional “springs and struts” to Auto Layout: always make a copy of the nib content you are working on before you start hacking away.

Because Auto Layout requires you to flip the switch for e.g. a whole window at once, and because the various adjustments to frames you make while adapting to Auto Layout may affect views in unwanted and unexpected ways, it’s extremely convenient to have at hand the UI layout as it was before surgery, so to speak.

Sure, because everybody in their right mind these days uses version control software, it’s easy enough to check out a previous iteration of a xib file and open it up for comparison, but it’s become such a certainty that I will find value in the reference, I usually just make a redundant copy in the xib itself while I’m working:

1. Select the window element in Xcode’s Interface Builder.
2. Copy and paste to make a new copy.
3. Adjust the original’s Auto Layout information until satisfied.
4. Delete the copy.

Having the reference copy is very handy as well for trying to isolate layout behaviors to just specific parts for the UI. For example, a deeply nested view seems to behave unexpectedly and you can’t get the layout right. Copy just the original from your “backup window”, and paste the view in isolation. Now see if you can get the layout right in terms of itself, without respect to the larger window. This can help you figure out the “aha!” constraint that you need to go back and add to the “real window.”