Navigate back to the homepage

Long Story Short: Codable Protocols

Arvind Ravi
January 18th, 2018 · 2 min read

As developers, I’m sure we all have been in a place where we’ve had to deal with JSON in our lives. In my previous article, I detailed out how to deal with networking in Swift. Although the pattern discussed in the previous article still works excellently, we relied heavily on the JSONSerialization class to convert data back and forth to JSON.

With Swift 4, there are three fancy protocols that we could leverage to build a better model to deal with networking data:

  • Codable
  • Encodable
  • Decodable

Encodable Protocol - defines a type that can be encoded into another form/representation for instance, JSON.

Decodable Protocol - defines a type that can be decoded from another representation into a native Swift type.

Codable Protocol - simply is a wrapper over Encodable and Decodable Protocols, and represents a type that can both be encoded and decoded.

The Fundamentals

In this section, we will discuss the concepts behind the Codable Protocols, using the example from the previous article:

1[
2 {
3 "title": "Pulp Fiction",
4 "actors": [
5 { "name": "Samuel L Jackson" },
6 { "name": "John Travolta" }
7 ]
8 },
9 {
10 "title": "Ocean's Eleven",
11 "actors": [
12 { "name": "George Clooney" },
13 { "name": "Brad Pitt" },
14 { "name": "Matt Damon" }
15 ]
16 },
17 {
18 "title": "Goodfellas",
19 "actors": [
20 { "name": "Robert De Niro" },
21 { "name": "Ray Liotta" }
22 ]
23 },
24 {
25 "title": "I Am Sam",
26 "actors": [
27 { "name": "Sean Penn" }
28 ]
29 }
30]

Looking at the data, we realise that we could have two types to model this:

  • Movie
  • Actor

Swift 4 makes it easier to make types codable. As long as the type declares its properties using types that are already codable, by simply adding Codable to the inheritance list on the definition it triggers the conformance to Codable automatically.

This means the type now can be encoded/decoded using a JSONEncoder or a PropertyListEncoder to encode and decode by itself, even though we didn’t have to implement anything for it.

The types that are natively codable include:

  • String
  • Int
  • Double
  • Date
  • Data
  • URL

And as long as the elements they contain are Codable, the following types can also be considered as Codable:

  • Array
  • Dictionary
  • Optional

Code:

1struct Actor: Codable {
2 let name: String
3}
4
5struct Movie: Codable {
6 let title: String
7 var actors: [Actor]
8}

In the above example, we’ve declared both our types as Codable and the automatic conformance is triggered without having to implement anything more because we simply are trying to encode/decode types that are already Codable.

Encoding

Now that our types are Codable, we can now get to the exciting part, trying to encode our type into say, JSON.

To encode the Movie type into JSON. We use an instance of JSONEncoder:

1// Movie to be encoded
2let movie = Movie(title: "Goodfellas")
3
4// Encoder
5let encoder = JSONEncoder()
6
7// Encoded Data
8let encodedMovie = try encoder.encode(movie)

Since the properties already conform to Codable, we were able to use the encoder directly to encode the movie.

Decoding

Now that encodedMovie contains the encoded data, typically as Data, we can see how we could possible decode this into native movie type:

1// Data to be decoded
2let encodedMovie: Data?
3
4// Decoder
5let decoder = JSONDecoder()
6
7let movie = try decoder.decode(Movie.self, from: encodedMovie)
8
9// `movie` is a native swift object now
10// movie.title is valid

Coding Keys

Codable types, can optionally specify an enum called CodingKeys that conforms to CodingKey protocol. When this particular enum is present, the cases specified are the list of properties that are included when the type is encoded or decoded.

1struct Movie: Encodable {
2 enum CodingKeys: CodingKey {
3 case title, actors
4 }
5}

Doin’ it manually

Things aren’t always nice. They’re not always easy. You may often find yourself in a place where your Swift type has a different structure than the JSON you have to deal with. In such cases the way to go is:

  • Let Swift know about the structure of your encoded form
  • Implement an init on the type reflecting the structure of the JSON or the encoded form

Code:

Assuming we have a property called meta on Movie which in JSON is nested like:

1[
2 {
3 "title": "Goodfellas",
4 "actors": [ { "name": "Robert De Niro" }, { "name": "Ray Liotta" } ]
5 "meta": {
6 "rating": 8.0
7 }
8 }
9]

Because our JSON data contains a second level of nested information for the rating property, the type’s Codable protocol use two enumerations that each list the coding keys on a particular level.

We can extend the Movie type to reflect this structure by using two enumerations to list the Coding Keys used on each level.

1struct Movie: Codable {
2 let title: String
3 var actors: [Actor]
4 let rating: Double
5
6 enum CodingKeys: String, CodingKey {
7 case title
8 case actors
9 case additionalInfo
10 }
11
12 enum AdditionalInfoKeys: String, CodingKey {
13 case rating
14 }
15}

To be able to support Decodable for our data with a nested structure, we can extend the Movie to conform to Decodable by implementing its initialiser:

1extension Movie: Decodable {
2 init(from decoder: Decoder) throws {
3 let values = try decoder.container(keyedBy: CodingKeys.self)
4 title = try values.decode(String.self, forKey: .title)
5 actors = try values.decode([Actor].self, forKey: .actors)
6
7 let additionalInfo = try values.nestedContainer(keyedBy: AdditionalInfoKeys.self, forKey: .additionalInfo)
8 rating = additionalInfo.decode(Double.self, forKey: .rating)
9 }
10}

That should take care of decoding nested data from JSON.

Codable Protocols are very handly, and make dealing with an API a little easier, so the next time you’re looking to work with an API, give Codable Protocols a shot.

Feel free to leave any feedback, or questions behind!

More articles from Swiftla

Networking with Swift

Introduction We all know why dependencies are bad, and writing software dependant on other software is not necessarily good. It probably…

November 8th, 2017 · 5 min read

ARKit: A Noob's Guide - Part IV

Part IV: Lighting with SceneKit 💡 In the last few parts of the series, we’ve discussed how to setup an AR Session, detecting planes, and…

October 31st, 2017 · 4 min read
© 2017–2021 Swiftla
Link to $https://twitter.com/arvindravi_Link to $https://github.com/arvindraviLink to $https://www.linkedin.com/in/arvindravizxc/