Codable in Swift 4

Codable in Swift 4

The Codable protocol was introduced in the release of Swift 4 so, now Swift 4 or later version has built-in support for working with JSON using the Codable protocol. Most commonly The JSON is using to send and receive data from web service.

Swift will allow you to convert freely between that data and JSON using only a few lines of code.

Codable makes it easy to parse JSON into structs and you can use it throughout your code, now there is no need for messing around with dictionaries. Swift 4.1 makes some changes to the Codable extension that make it even more powerful.

Codable Protocol :

A type that can convert itself into and out of an external representation. It is used by the type can be both encoded as well as decoded.

typealias Codable = Decodable & Encodable

It includes the methods declared in both Encodable as well as Decodable.The Codable is actually a type alias that combines two protocols Encodable and Decodable into one.

Encodable Protocol :

A type that can encode itself to an external representation. It is used by the types that can be encoded. It contains a single method.

encode(to:)  —>  Encodes this value into the given encoder.

Decodable Protocol :

A type that can decode itself from an external representation. It is used by the types that can be decoded. It also contains a single method,

init(from:)  —>  Creates a new instance by decoding from the given decoder.

Advantages:

-These protocols support Class, Struct, and Enums and Custom types.

– Using the Codable, we can model JSONObject or PropertyList file into equivalent Struct or Classes by writing very few lines of code. We don’t have to write the constructor for the properties in the objects. It’s all handled by Codable. We just need to extend our model to conform to the Codable, Decodable or Encodable protocol.

– By conforming to either of those protocols when declaring a type, the compiler will attempt to automatically synthesize the code required to encode or decode an instance of that type, which will work as long as we are only using stored properties that themselves are encodable or decodable.

– Parsing actual JSON become one-liner using JSONDecoder.

-With the introduction of CodingKey protocol, you can omit the unnecessary redundant code used in encoding and decoding the object.

-Mismatch between the strong data types of Swift and lose data types of JSON has been internally handled by Swift compiler. We can now handle Swift Data types like Date, URL, Float etc.

-Complex JSON can be modeled easily using Nesting Structs for readability.

Decoding JSON With Codable:

Here’s the JSON,

let jsonString = """

{

"first_name": "John",

"last_name": "Doe",

"country": "San José"

}

"""

Convert JSON into a Data object.

let jsonData = jsonString.data(using: .utf8)!

This is a necessary intermediate step. Instead of representing the JSON as a string, we now store the JSON as a native Data object.

If you look closely, you’ll see that the above code uses force unwrapping to work with the optional return value from data(using:). Let’s unwrap that optional more elegantly.

if let jsonData = jsonString.data(using: .utf8) {

// Use jsonData

} else {

// Respond to error

}

With the above code, we can respond to errors when data(using:) returns nil. You could for instance show an error message, or silently let the task fail and save diagnostic information in a log.

Next, we’re creating a new JSONDecoder object. Like this:

let decoder = JSONDecoder()

We then use that decoder to decode the JSON data.

let user = try! decoder.decode(User.self, from: jsonData)

However, the decode(_:from a : ) function can throw errors. The above code crashes whenever that happens. Again, we want to respond to any errors that might happen.

do {

let user = try decoder.decode(User.self, from: jsonData)

print(user.last_name)

} catch {

print(error.localizedDescription)

}

And the entire code block now looks like the following.

if let jsonData = jsonString.data(using: .utf8)

{

let decoder = JSONDecoder()

do {

let user = try decoder.decode(User.self, from: jsonData)

print(user.last_name)

} catch {

print(error.localizedDescription)

}

}

What if our JSON properties, like first_name, are different than the Swift struct properties? That’s where CodingKeys comes in.

Every class or struct that conforms to Codable can declare a special nested enumeration called CodingKeys. You use it to define properties that should be encoded and decoded, including their names.

Let’s take a look at an example. In the User struct below, we’ve changed the property names from snake case to camel case.

struct User : Codable

{

var firstName:String

var lastName:String

var country:String

enum CodingKeys: String, CodingKey {

case firstName = "first_name"

case lastName = "last_name"

case country

}

}

When you use the above struct with the examples in the previous sections, you’ll see that you can use a User object with the new property names.

print(user.firstName)

// Output: John

print(user.country)

// Output: San José

The CodingKeys enumeration is self-explanatory. It simply maps cases to properties and uses the string values to select the right property names in the JSON data.

Encoding Objects As JSON With Codable :

var user = User()

user.firstName = "Jane"

user.lastName = "Appleseed"

user.country = "Washington, D.C."

let jsonData = try! JSONEncoder().encode(user)

let jsonString = String(data: jsonData, encoding: .utf8)!

print(jsonString)

And the output is,

{"country":"Washington, D.C.", "first_name":"Jane", "last_name":"Appleseed"}

First, you create a User object and assign some values to its properties.Then, you use encode(_:) to encode the user object to a JSON Data object.

Finally, you convert the Data object to String and print it out. If you look closely, you’ll see that encoding follows the same steps as decoding, except in reverse.

let encoder = JSONEncoder()

encoder.outputFormatting = .prettyPrinted

do {

let jsonData = try encoder.encode(user)

if let jsonString = String(data: jsonData, encoding: .utf8) {

print(jsonString)

}

} catch {

print(error.localizedDescription)

}

JSON Output,

{

"country" : "Washington, D.C.",

"first_name" : "Jane",

"last_name" : "Appleseed"

}

First, you create a User object and assign some values to its properties. Then, you use encode(_:) to encode the user object to a JSON Data object.

Finally, you convert the Data object to String and print it out. If you look closely, you’ll see that encoding follows the same steps as decoding, except in reverse.

let encoder = JSONEncoder()

encoder.outputFormatting = .prettyPrinted

do {

let jsonData = try encoder.encode(user)

if let jsonString = String(data: jsonData, encoding: .utf8) {

print(jsonString)

}

} catch {

print(error.localizedDescription)

}

JSON Output,

{

"country" : "Washington, D.C.",

"first_name" : "Jane",

"last_name" : "Appleseed"

}

In the above example, we’re also using the output formatting property of the encoder to pretty-print the JSON data. This adds spaces, tabs, and newlines to make the JSON string easier to read.