Parsing strings with Objective-C and NSDateFormatter, a gotcha
I was recently working on an odd bug in one of my client's iPhone apps; the app in question fetches data from a Rails-based REST API and stores the records in a local Core Data store.
Unfortunately, some users were reporting that some records were simply not appearing. Try as I might, I was unable to reproduce this issue locally.
With some help of one of the users who was having this issue, I was eventually able to track down the core of the problem; the records were fetched from Core Data using a predicate that matched records with a
createdAt attribute greater than a particular date however, the records in the database all had nil
Having located the root cause, the next step was to determine why those dates were nil and the problem lied in the way the dates were being parsed.
The dates returned by the Rails API were in the format '2010-11-28T20:30:49Z' and were in UTC. The method that parsed this string and tried to convert it to an NSDate was implemented as a category on NSDate that looked like this:
The code first checks that the string is not nil, then replaces the 'Z' at the end of the timestamp with a zero UTC offset string that we can match using
NSDateFormatter. Finally, it calls this method:
This is fairly straightforward; it initialises a new
NSDateFormatter, sets the format of the string we are trying to parse and then parses the date.
Ensuring consistent NSDateFormatter behaviour
It's hard to see what could be going wrong with this, especially as I couldn't reproduce the issue. I had already confirmed that the customer with the issue was running the latest version of iOS (as was I) and we were both using the same locale ("en_GB" in this case).
The solution was simple but not obvious; to ensure that the
NSDateFormatter behaviour is consistent across locales and date/time settings, you should explicitly set the appropriate locale of the date formatter which in this case, was "enUSPOSIX". The new method looked like this:
Apple explains this in even more detail in this tech note - if you ever work with dates and timestamp strings from web APIs, I highly recommend you read it.
I spent several hours banging my head against a wall on this one, so hopefully this post will prove useful to somebody else in the future.