Swift’s JSONDecoder’s ONE BIG MISS
Difficulty: Beginner | Easy | Normal | Challenging
This article has been developed using Xcode 12.4, and Swift 5.3.2
This article has a supporting video at: YouTube video
- You just need to be able to create a Swift project, but it might help you to read over the Date and Time Cheatsheet
You will almost certainly need to communicate with a backend through a published API. In fact, you might use an API like the Twitter API that is stable and well documented.
Now depending on why you are using an API, you might well have a date/time returned to you.
Here is a snippet of data from a cursor-based API
What should be used and when?
The brief alternatives
The Unix epoch (also Unit time, POSIX time or Unix timestamp) are the number of sections that have elapsed since midnight on January 1, 1970 (UTC/GMT). ISO 8601 is a calendar and clock format and represents a standardised way of presenting date and time (as well as time intervals).
The problem to be solved
Mobile devices often need to display the time in the local time of the user. For example I want to check into a hotel at 3pm, and this must be 3pm at the hotel- irrespective of which country the backend server is in.
Yet the backend still needs to store the time. So how is this done?
Since there is no format for dates in JSON, the output either needs to be a number or a string. This means the backend will present
ISO 8601 is human readable, and has a specified timezone and enable reasonable sorting and comparison by front end developers.
Epoch on the other hand enables smaller payloads and is very familar for those from an embedded systems background.
How do you know the end user’s timezone
You don’t need to know! The frontend will know the timezone and can display the information to the user according to their geographic location and preferences. If something (like a notification) needs to be sent that displays the user’s time the timezone identifier can be stored (but of course would need to be refreshed with the user’s location when they change location across timezones)
What format should be used to store a time in a database? Databases are in the business of returning queries quickly. Integers are easy to query, index and are space efficient we often see times stored as Unix Epoch Time.
Using a local timezone would mean you assume your server is always deployed in the same timezone, and with AWS meaning servers can be quickly switched between regions (and even if you don’t use AWS in the short term you might in the long term) making this an inadvised choice.
How should you print usertime to the screen of the user? How can you convert a time to a local time?
We can do this in iOS! In fact this is what the rest of this article is about!
Rather than creating a full backend API for this article, I’ve created two hard-coded JSON strings that can be decoded in a Swift Playground or MacOS command-line application.
The model will conform to codable, and it would be great if we could store the data directly as a date object, so we shall do exactly that with the following model:
Date type is independent of any time zone, so we can choose to make the data natively Swift by storing the date and making this change just once. This is independent from displaying the date on the screen.
I would usually split the data model into an APIdto where the timestamp coud be stored as an Int64, and a data object where it would be stored as a Data. However in this rather simple version storing the data as a Date will do!
Data from Unix Epoch
The data can be decoded from the followingusing Swift:
we do need to be careful of that snake case and this can be converted as we set up the decoder with
which would output the following:
Note that this isn’t how you might output the date to the user, but for the console output this would be fine.
Data from iso8601: The happy path
The data is stored in the following JSON string:
we can produce the following which, is perhaps not surprising to the reader:
and we have a console output of the following:
Data from iso8601: The likely path
Unfortunately we don’t always have such a simple ride when using
That wrinkle is known as “milliseconds”.
If we have a new JSON
String to decode (and it is perfectly viable that this might be returned from your backend):
If you decode this using the same technique as above:
we unfortunately never print out the result. The
print(usersMilliseconds) line is never executed.
Wait? What is going on?
Unfortunately the iso8601 format doesn’t include the fractional settings. Perhaps what we should do is panic.
Or we can add our own date formatter:
we can then add in the date decoder strategy so we can use this with
Codable which is, after all, what we intend to do here:
which can then be used:
which outputs the following to the console:
Unfortunately you’d need to only support from iOS11 onwards. Which you probably aren’t. Which is a shame.
We now get:
and what is better is this works from iOS9!
This article has gone some way to making those rather annoying dates a little bit easier to process.
This has been further covered in my YouTube video that explains some of this as well.
I hope this article has really helped you out! Oh, and the code is avaliable over at: https://github.com/stevencurtis/SwiftCoding/tree/master/HandleTimestampsFromYourBackend
If you’ve any questions, comments or suggestions please hit me up on Twitter