';

Subscribe to The Swift Post

Enter your email address to subscribe to The Swift Post and receive notifications of new posts by email.

Swift 4’s Codable

It is almost certain that at some point in your app development journey, you needed to (or you will soon :]) serialize some object or value and parse JSON response to your model. If I am right, then this might worth your time.
In Swift, we used to use NSCoding protocol to serialize our objects. To conform to NSCoding we very often ended up making our classes inherit from NSObject and then after some cha cha and samba and some freakishly long amount of time, we finally manage to implement the expected init(coder:) and encode(with:) methods. But what about structs right? Well, most people found the solution in introducing third party frameworks to their projects. Hopefully, the fight for serialization and mapping has come to an end.

What now?

Swift 4 introduces a protocol called Codable which is a composition of two other protocols: Encodable & Decodable. Codable allows us to serialize and deserialize classes, structs and enums with almost no effort. Most types including String, Int, Array, Dictionary are conforming to this protocol. So when we create a new type containing properties which already conform to Codable, we do not need to write one more line of code. The Person type below conforms to Codable just like that:

Serializing Person to JSON:

Voilà, person value converted to data just like that.

Now let’s deserialize:

What’s in it for me?

There are two awesome things that I found Codable useful for:

  1. Mapping
  2. Transformations
  3. Archival & Unarchival

Mapping

Say you receive your responses as a JSON string (even better if it comes as encoded data). It would be delicious to initialize our types with that JSON string, right? Let’s write it down:

Thanks to powerful protocol extensions, once again, we need not do anything further with our types (multiline strings with triple quotation marks below, are also introduced in Swift 4).

A little bit of refactoring to Person:

Adding optionality to Mappable initializer is needed to avoid possible failures due to missing fields in the response. And when we make a property optional (friends in this case), Codable will still work if the optional field is not included in the data.
If you were using some third party framework like ObjectMapper for this, great news, you no longer need to. Not only it already offers the same functionality for mapping, it also saves you from finding the values with the keys by subscript. It infers the keys from the property name. Swift 4 also provides the CodingKey protocol to grant you the freedom of naming your properties as you like:

I have changed name to alias and friends to comrades because I can.

Transformations

Apart from modifying the naming of response parameters with CodingKey, we can also make structural changes with the response. One case would be using your own Coordinate type instead of latitude and longitude values received from the service call. Or I could wish to store the date string in various formats of Date type. To achieve that, we are going to need to implement the init(from:) method Decodable of protocol ourselves.

Let’s reimplement the Person type:

And the usage:

We used CodingKeys enum to be able to parse lat and long from the response and a DateFormatter to parse birth property as Date instead of String (I will come back to this in a moment). Notice how the name is nil since the jsonString does not contain name key and value. For our optional types, we have to use decodeIfPresent method instead of decode method. The down side is, if we were to perform transformation only on one property of a type with say ten properties, we will have to implement the assignments for all others in the usual manner too since the default extension cannot be used anymore.

The better way to transform Date would be to update our Mappableextension like the following:

This way, instead of creating a Date type from the String by ourselves, we tell the JSONDecoder how to parse String to Date. So this:

Is now just one line for every Date assignment:

And if the only transformation we need is about Date type, we no longer need to implement init(from decoder: Decoder) since JSONDecoder will be able to handle it with the default implementation.

Archival & Unarchival

NSKeyedArchiver & NSKeyedUnarchiver also supports Codable. Here is how we archive a value:
NSKeyedArchiver.archiveRootObject(person, toFile: "file")
Along with your classes that conform to NSCoding, now all your types that conform to Codable can be handled by NSKeyedArchiver.
Unarchival, here you go:
guard let data = NSKeyedUnarchiver.unarchiveObject(withFile: "file") as? Data else {return}
If you chose to encode your value with JSONEncoder and then archive it, you can decode it with JSONDecoder after unarchival.
This feature allows us to persistently store our Codable types.

Final Notes

As you know, Swift 4 is still being developed. For instance, you cannot conform to Codable in an extension (this will be fixed until first release of course). Apart from that, I have not faced with any other problem. If you decide to move forward to Swift 4 though, you can certainly use all these great features. Cheers!

Alp Avanoğlu