At Wayfair, we’re keen to research, innovate, and invest in new technologies whenever the opportunity arises. There are obvious advantages to this approach for our iOS teams in particular: It allows us to keep up to date with current developments and be prepared for potential API updates. This is particularly important for the Apple ecosystem, where technology changes every year bringing about newer and more reliable APIs for developers and users alike.
We recently started exploring one such technology on our iOS platform, the Codable protocol. This Type Alias is the result of the hard work and dedication coming out of the Swift open source community.
So, what is Codable?
Codable, introduced in Swift 4, provides a standardized approach to encode and decode custom types. It can be adopted by custom types by conforming to Codable protocol. What does this mean for developers? We no longer have to rely on third party SwiftyJSON variant libraries to perform JSON serializing and deserializing operations.
Swift standard library types (String, Int etc.), Foundation types (Data, URL etc.), and Built-in types (Array, Dictionary etc.) all conform to Codable protocol, meaning custom types using these as a base can also accommodate to the protocol. Codable includes built-in support for handling URLs, Date, and Float types so that developers don't have to worry about handling special cases specifically for them.
Updates to our codebase
We currently use Unbox as our standard framework for encoding and decoding data that comes in and goes out through network requests. However, we also want to move towards writing cleaner code by transitioning to Codable types.
In support of this plan, we undertook a rewrite of our existing models (written to decode JSON using Unbox) to a new version which uses Codable. Below are some of the changes and updates that were required in order to make our architecture compatible with Codable.
- Update the current network service to handle Codable types in addition to existing Unbox types
- Update the custom type of our existing model updating protocol conformance from Unbox to Codable
- Codable provides a generic way to handle both Array and Single custom types conforming to Codable, so maintenance of two separate interfaces isn’t required
- Write unit tests for Codable types
- Perform regression testing on the refactored code
Examples
Let’s look at some examples to understand how Codable works.
Suppose we are receiving the following JSON which needs to be decoded in our Wayfair app:
{
"first_name": "abc",
"last_name": "xyz",
"user_age": 20
}
The above is typical JSON, nothing fancy about it.
We will first start by creating a model for this object, let’s call it a Person
. This will be represented by a structure named Person
.
As pointed out by
this answer, in Swift it is always preferable to use struct
, which is a value type, over a class which is a reference type. This decision can help
avoid unnecessary complexity and inconsistencies resulting from referenced values being changed over time.
struct Person {
let firstName: String
let lastName: String
let userAge: Int
}
As you can see, we have used appropriate data types for struct Person
to match the incoming JSON. However, we’ve got a little problem: How does the decoder know which key in the incoming JSON belongs to which field in the Person
object?
This is where the magic of the CodingKeys
enum comes in. Using CodingKeys
, we can tell the Codable protocol which keys in JSON get mapped to which values of the custom type. We will modify our struct
to include the CodingKeys
enum.
struct Person {
let firstName: String
let lastName: String
let userAge: Intenum CodingKeys: String, CodingKey {
case firstName = "first_name"
case lastName = "last_name"
case userAge = "user_age"
}
}
In the above code, we created mappings from JSON keys to structure properties. E.g. the value of first_name
gets mapped to firstName
and so on. Now we can simply use an instance method decode
of the JSONDecoder
object, passing an expected type and JSON Data
object to receive the decoded Person
object as a result.
For the sake of this example, we will use local JSON manually converted to a Data
object as if it has returned from the remote API.
let data = """
{"first_name": "abc", "last_name": “xyz”, “age”: 20}
""".data(using: .utf8)!
do {
let decodedData = try JSONDecoder().decode(Person.self, from: data) {
// Prints: Person(firstName: "abc", lastName: "xyz", age: 20)
print(decodedData)
} catch let error {
print(error.localizedDescription)
}
Similarly, you can use the JSONEncoder
class's encode
method to encode custom type into JSON. Please take a look at the following example:
let personObject = Person(firstName: "abc", lastName: "xyz", age: 20)
do {
let encodedDictionary = try JSONEncoder().encode(personObject)
// prints {"first_name":"abc","last_name":"xyz","age":20}
print(String(data: encodedDictionary,encoding: .utf8)!)
} catch let error {
print(error.localizedDescription)
}
Et voilà! This is all you will need to perform basic encoding/decoding using the Codable protocol.
Benefits
For now, we’re currently using Codable for only one model as a start and have a network layer ready to handle both Unbox and Codable types. There are several benefits we’re looking forward to from future usage of Codable:
- Foundation-provided framework: More reliable and recommended by Apple
- Future-proof way of handling JSON encoding and decoding, as it’ll be supported by the Swift open source community for some time
- Reduces overhead and boilerplate code
- Reliable error handling with few possible error scenarios
Our future plans
As our team moves forward, Codable is going to be a future standard in the way we work and will replace what we used to do using third party libraries. However, there are some challenges on this path. We have several models in our application which still use Unbox to perform JSON serialization/deserialization. This will certainly take a significant amount of time to convert all models to Codable. As with most of our processes, the change will happen incrementally and may possibly enforce the Codable standard for newly added models in the app. Being in the large team, we’ll aim to set up a workgroup to standardize best practices when using Codable, which will maintain consistency across our different and distributed iOS teams.
To summarize, if you are already using Swift 4 to write your apps and adding new models, Codable is worth exploring. A brief session could also be beneficial to have users acquainted with the new framework. It should be noted that your exact plan of action depends on your own particular use case, team size, the complexity of your code base, and overall team structure.
Have you had some exposure to Codable? Are you using it in your app? How do you rate it? Please let us know your thoughts by commenting below.