About Codable in Swift… And the common mistakes to avoid….

Often while fetching data, its format needs to be changed like in networking calls we receive data in different format like JSON, which has to be changed to different format to use it.

So most of the time we receive data in JSON format so to convert it in usable format there are many ways, like many cool libraries are available to do the work. But Swift 4 has introduced Codable protocol for easy JSON parsing. Codable is a type alias of Encodable and Decodable protocol.

The advantage which I found of using Codable than other libraries:

  • No extra decode and encode code.
  • Native support
  • Easy to understand and maintain

Lets directly dive into example and convert JSON into a model object:

JSON Sample 1:

{
 “name” : “John”,
 “lastname” : “Shark”,
 “hobby” : “Reading”,
 “DOB” : “05/03/1990”,
 “job” : “Developer”
 }

Lets first create a model class for this:

struct MyModel : Codable {
 var name : String
 var lastname : String
 var hobby : String
 var DOB : String
 var job : String
}

Now, let’s parse it:

do {
 let json = try JSONSerialization.data(withJSONObject: jsonData,   options: .prettyPrinted)
 let reqJSONStr = String(data: json as! Data, encoding: .utf8)
 let data = reqJSONStr?.data(using: .utf8)
 let decoder = JSONDecoder()
 do {
    let user = try decoder.decode(MyModel.self, from: data)
 } catch {
    print(error.localizedDescription)
 }
 } catch {
 print(“Error while deserialization of jsonData”)
 }

decoder.decode() converts JSON to model object. Added catch blocks for exception handling.

JSON Sample 2:

Added an object “Friend” in the JSON

{
 “name” : “John”,
 “lastname” : “Shark”,
 “hobby” : “Reading”,
 “DOB” : “05/03/1990”,
 “job” : “Developer”,
 “Friend” : {
 “name” : “Michael”,
 “place” : “London”,
 “job” : “Tester”
 }
}

Let’s add a model “Friend” and also modify the “MyModel” class as below

struct MyModel : Codable {
 var name : String
 var lastname : String
 var hobby : String
 var DOB : String
 var job : String
 var Friend : Friend
}
struct Friend : Codable {
 var name : String
 var place : String
 var job : String
}

Parsing code will remain the same

Now the mistake which I always make is the variable name should be exactly the same as the JSON object i.e if I change the Model class as below

struct MyModel : Codable {

 var name : String
 var lastname : String
 var hobby : String
 var DOB : String
 var job : String
 var friend : Friend 
}

Then the above code won’t work, as it is case sensitive and ‘friend’ is different from keyword ‘Friend’ in JSON, if you want to keep the variable name different then you can use the CodingKeys as below

struct MyModel : Codable {
 var name : String
 var lastname : String
 var hobby : String
 var DOB : String
 var job : String
 var friend : [Friend]

enum CodingKeys : String, CodingKey {
 case name = “name”
 case lastname = “lastname”
 case hobby = “hobby”
 case DOB = “DOB”
 case job = “job”
 case friend = “Friend”
 }
}

Now the above JSON will be parsed without error.

JSON Sample 3:

{
 “name” : “John”,
 “lastname” : 12345,
 “hobby” : “Reading”,
 “DOB” : “05/03/1990”,
 “job” : “Developer”,
 “Friend” : [
   {
    “name” : “Michael”,
    “place” : “London”,
    “job” : “Tester”
   }
  ]
}

For above JSON the code will give exception as “The data couldn’t be read because it isn’t in the correct format”.
This is the error given when the binding is not proper and it is not able to parse.
To check in which line the parsing is breaking we can make use the decode method, let’s overwrite the decode method as below :

struct MyModel : Codable {

var name : String?
var lastname : String?
var hobby : String?
var DOB : String?
var job : String?
var friend : [Friend] = []

   enum CodingKeys : String, CodingKey {
      case name = “name”
      case lastname = “lastname”
      case hobby = “hobby”
      case DOB = “DOB”
      case job = “job”
      case friend = “Friend”
}

init(from decoder: Decoder) throws {

let container = try decoder.container(keyedBy: CodingKeys.self)
if let name = try container.decode(String?.self, forKey: .name) {
    self.name = name
}
if let lastName = try container.decode(String?.self, forKey: .lastname) {
   self.lastname = lastName
}
if let hobby = try container.decode(String?.self, forKey: .hobby){
   self.hobby = hobby
}
if let DOB = try container.decode(String?.self, forKey: .DOB){
   self.DOB = DOB
}
if let job = try container.decode(String?.self, forKey: .job){
   self.job = job
}
if let Friend = try container.decode([Friend]?.self, forKey: .friend) {
   self.friend = Friend
}
}
}

Parsing ‘lastName’ will break as the value in JSON is as Int value and we have declared the datatype as String.

DataType plays a major role in Codable protocol. If the datatype of an element declared differ from the json, then it will throw an exception.

So we will just change the datatype of ‘lastname’ as ‘int’, then it will work without any error.

HaPpY CoDiNg…..

Leave a comment