Codable
Codable is a typealias conformed by Decodable and Encodable protocols
/// A type that can convert itself into and out of an external representation.
public typealias Codable = Decodable & Encodable
All of the Swift standard library types conform to Codable (Number Types, Bool, String, Optional, Array, Dictionary and Set<Codable>), as well as many Apple’s framework types such as Data, Date, URL, CGPoint, and CGRect. Meaning we can use this types in our Codable objects and they will be encoded and decoded automatically with no extra work.
Encodable
Encodable only requires encode(to encoder: Encoder) throws which takes Encoder object.
/// A type that can encode itself to an external representation.
public protocol Encodable {
/// Encodes this value into the given encoder.
///
/// If the value fails to encode anything, `encoder` will encode an empty
/// keyed container in its place.
///
/// This function throws an error if any values are invalid for the given
/// encoder's format.
///
/// - Parameter encoder: The encoder to write data to.
public func encode(to encoder: Encoder) throws
}
Decodable
Decodable only requires init(from decoder: Decoder) throws which takes Decoder object.
/// A type that can decode itself from an external representation.
public protocol Decodable {
/// Creates a new instance by decoding from the given decoder.
///
/// This initializer throws an error if reading from the decoder fails, or
/// if the data read is corrupted or otherwise invalid.
///
/// - Parameter decoder: The decoder to read data from.
public init(from decoder: Decoder) throws
}
Encode
As we mentioned earlier all of the Swift standard library types conform to Codable so we are able to use them in our Content type.
// Shortened representation
struct Content: Codable {
let id: Int
let title: String
let body: String
}
Adding Codable to the inheritance list for Content triggers an automatic conformance that satisfies all of the protocol requirements from Encodable and Decodable. This enables us to serialize them to and from JSON format even though our Content struct has no code that handles parsing. The same principle applies when using our custom Codable as properties of other objects.
struct Section: Codable {
let id: Int
let title: String
let contents: [Content]
}
We are able to do this as Array conforms to Codable as long as contains Codable types such as Content type.
Using types that don’t conform to Codable would break this implementation.
let content = Content(id: 1,
title: "Getting Started",
body: "Body of our content")
let section = Section(id: 1,
title: "Learn iOS 11 Design",
contents: [content])
let encoder = JSONEncoder()
do {
let jsonData = try encoder.encode(section)
let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: .allowFragments)
print(String(describing: jsonObject))
} catch {
// Encoding error.
print(error.localizedDescription)
}

Decode
It might happen that we only have one way data flow in our custom type as some objects are for example only read from a web service but never uploaded from our app.
In the DesignCode app the DataUpdate conforms only to Decodable as the updates JSON data always come from our web service network requests and is never uploaded from the app.
struct Update: Decodable {
let id: Int
let title: String
let text: String
let imageURL: URL
let date: Date // Parsed with timeIntervalSinceReferenceDate value
}
The same approach can be implemented if you have objects that need to be encoded but never decoded. Just conform to Encodable. Although there’s no extra functional benefit for declaring a type conform to only one of the protocols it will in fact define a clearer usability API for your types.
let updateJSONString = """
{
"id": 1,
"title": "First update",
"text": "This is the first update of our updates elements",
"imageURL": "https://designcode.io/images.png",
"date": 534988800
}
"""
do {
let decoder = JSONDecoder()
let data = updateJSONString.data(using: .utf8)!
let update = try decoder.decode(Update.self, from: data)
print(update)
} catch {
print(error.localizedDescription)
}
Take into account that Date class decodes its value by using the timeIntervalSinceReferenceDate value on Apple’s platforms rather than timeIntervalSince1970 mostly used in other platforms and systems.

CodingKeys
Codable types can declare a nested enum called CodingKeys that conform to the protocol with the same name. This CodingKeys cases represent the properties in our type while their stringValue is key used in the JSON property value. For integer-indexed collections CodingKeys have a intValue. This acts as the list of properties that must be included when decoding or encoding our types. If we omit any of our properties in the CodingKeys enum this property will not be encoded or decoded. To omit properties they must have a default initial value to not break their protocol conformance. CodingKeys are also useful if we have a different naming convention from the service providing the JSON data (some backend programming languages prefer snake_case naming rather than camelCase we use in Swift).
For our Update implementation we are setting a default title and not include it in the CodingKeys enum. Also, we are changing the key for our imageURL property to image_url
struct Update: Decodable {
let id: Int
let title = "New Update!"
let text: String
let imageURL: URL
let date: Date
public enum CodingKeys: String, CodingKey {
case id
case text
case imageURL = "image_url"
case date
}
}
let updateJSONString = """
{
"id": 1,
"text": "This is the first update of our updates elements",
"image_url": "https://designcode.io/images.png",
"date": 534988800
}
"""
do {
let decoder = JSONDecoder()
let data = updateJSONString.data(using: .utf8)!
let update = try decoder.decode(Update.self, from: data)
print(update)
} catch {
print(error.localizedDescription)
}

Manual Encoding and Decoding
So far we let the JSONEncoder and JSONDecoder classes do all the heavy work as we just simply conform to their protocols and get all the functionalities with no extra code added. But what happens when we want to do some extra customization or our JSON data is not structured the same was as our data type?
Both Encodable and Decodable protocols come with init implementations:
public protocol Encodable {
public func encode(to encoder: Encoder) throws
}
public protocol Decodable {
public init(from decoder: Decoder) throws
}
Which let us replace de default encoding/decoding functionality. We’ll do some customization on our Update type.
First of all we’ll change our JSON string to feed us the date in a Unix Timestamp format given that we plan to use this value on other non-Apple platforms. We’ll have to change de default encoding/decoding implementation to start our date with timeIntervalSince1970 rather than the default timeIntervalSinceReference. Also, we’ll nest a additionalInfo dictionary into our data which contains a postedBy property of type string which will represent the name of the team member that posted that update.
This is how the updated JSON will look like:
{
"id": 1,
"text": "This is the first update of our updates elements",
"image_url": "https://designcode.io/images.png",
"timestamp": 1513634273,
"additionalInfo" : {
"postedBy" : "Marcos Griselli"
}
}
We want to see this changes represented on both our Update struct and it’s CodingKey so this is how they’ll look
struct Update: Decodable {
let id: Int
let title = "New Update!"
let text: String
let imageURL: URL
let date: Date
let postedBy: String // User posting this update
public enum CodingKeys: String, CodingKey {
case id
case text
case imageURL = "image_url"
case timestamp
case additionalInfo
}
Take into account our _postedBy_property is inside the _additionalInfo_ nested dictionary. So it won’t be reachable if we add it to our CodingKeys enum. We’ll need to nestedContainer structure to support this kind of functionality and use it in our init methods.
enum AdditionalInfoKeys: String, CodingKey {
case postedBy
}
This will let us reference the nestedContainers from a container property from the Encoder or Decoder protocols in our init methods. Let’s create our custom init(from: Decoder) throws
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(Int.self, forKey: .id)
text = try values.decode(String.self, forKey: .text)
imageURL = try values.decode(URL.self, forKey: .imageURL)
let unixTimestamp = try values.decode(TimeInterval.self, forKey: .timestamp)
date = Date.init(timeIntervalSince1970: unixTimestamp)
let additionalInfo = try values.nestedContainer(keyedBy: AdditionalInfoKeys.self, forKey: .additionalInfo)
postedBy = try additionalInfo.decode(String.self, forKey: .postedBy)
}
In the snippet above we are accessing the values form the Decoder manually. try decoder.container(keyedBy: CodingKeys.self) gives us access to the values contained which are referenced by CodingKeys values. If we don’t need any additional setup or nested lookup we can directly try to decode the value for our CodingKeys option, we are doing this for id, text and imageURL. We changed the time handling and move from Apple’s timestamp to the default Unix Timestamp so now we’ll have to implement a different initializer for our date. Luckily Date comes with an initializer for Unix Timestamp timeIntervalSince1970 so we’re able to add support to this new format with out much refactor. Lastly we need to access our nested postedBy value. As mentioned before we’ll use nestedContainers which let us define the set of keys that will be contained inside it. In our case the a_additionalInfo_ dictionary is represented by the AditionalInfoKeys enum and we access it via the .additionalInfo case on our default CodingKeys
let additionalInfo = try values.nestedContainer(keyedBy: AdditionalInfoKeys.self, forKey: .additionalInfo)
Then we just keep using that container values with it’s custom Keys enum to access our nested data
postedBy = try additionalInfo.decode(String.self, forKey: .postedBy)
Implementing encode(to: Encoder) throws is the reversed step. We encode all of our type properties into a Encoder container using the CodingKeys values or the nested AdditionalInfoKeys for the properties that it holds. Our Update extension implementing Encodable would look like this:
extension Update: Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(text, forKey: .text)
try container.encode(imageURL, forKey: .imageURL)
let unixTimestamp = date.timeIntervalSince1970
try container.encode(unixTimestamp, forKey: .timestamp)
var additionalInfo = container.nestedContainer(keyedBy: AdditionalInfoKeys.self, forKey: .additionalInfo)
try additionalInfo.encode(postedBy, forKey: .postedBy)
}
}

Credit
Special thanks to Marcos Griselli for co-authoring this section.