JSON serialization using Codable Swift

We will use the sample json mentioned below to explain

Playground file is attached to test the below mentioned examples.

let urlString = "https://jsonplaceholder.typicode.com/todos/1"

{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}

Codable

What’s encoding and decoding in simple terms in the context.

  Decoding : We need to convert JSON response to the objects defined in our projectt

  Encoding : We need to convert our custom objects into JSON string to  post to a service.

JSON serialization before swift 4 :

 A verbose process , length depending on the size of the json structure.

let json = try? JSONSerialization.jsonObject(with: data, options: [])
if let data = json as? [String: Any] {
//This expands to every property in the struct
if let userId  = data[“userId”] as? Int {bookCatalog.userId = userId }
}

Starting Swift 4:

JSON  serialization encoding and decoding can be achieved with mere adopting to Codable protocol.

The Codable protocol is a composition of two protocols, Decodable and Encodable. 

Once can define the struct in the following ways depending on your use case

if you intend to decode and encode for your use case

  Struct BookCatalog :  Codable {…} 

You can also simply use if your use case is only to decode the response and use the data in your UI.

Struct BookCatalog :  Decodable {…} 

Decodable and Encodable protocols are adopted by the components that encode/decode various data formats, like JSON.

For our discussion we have 

The JSONDecoder and JSONEncoder classes use those the Decoder and Encoder protocols to provide the functionality to decode/encode JSON. 

Example to encode /decode using codable

struct BookCatalog: Codable {
    let userId: Int
    let id: Int
    let title: String
    let completed: Bool
    let test:String?
}

func fetchData() {
    if let url = URL(string: urlString) {
        let dataTask = URLSession.shared.dataTask(with: url) { data, response, error in
            if let jsonData = data {
                    let decoder = JSONDecoder()
                do {
		    //Below 1 line does the magic to convert json to required Objects when you are adopting to codable protocol
                    let catalog = try decoder.decode(BookCatalog.self, from: jsonData)
                    print(catalog.userId)
                    print(catalog.id)
                    print(catalog.title)
                    print(catalog.completed)
                    
                    let encoder = JSONEncoder()
                    do {
                        let encodedJson = try encoder.encode(catalog)
		    //Below 1 line does the magic to convert Object to JSON string when you are adopting to codable protocol
                        if let jsonString = String(data: encodedJson, encoding: .utf8) {
                            print(jsonString)
                        }
                    } catch {
                            print("Encode failed")
                        }
                } catch {
                    print("Decode failed")
                }
            }
        }
        dataTask.resume()
    }
}

Coding keys 

When do you need coding keys ??

If you want a custom name for your properties than what’s in json response .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.

Below is the example to define your structure with coding keys.

struct BookCatalogKeys: Codable {
    let userIdentifier: Int
    let index: Int
    let title: String
    let completed: Bool
    
    enum CodingKeys: String, CodingKey {
        case userIdentifier = "userId"
        case index = "id"
        case title
        case completed
    }
}

Serialization code remains the same even though we change the property names using coding keys.

func fetchDataKeys() {
    if let url = URL(string: urlString) {
        let dataTask = URLSession.shared.dataTask(with: url) { data, response, error in
            if let jsonData = data {
                    let decoder = JSONDecoder()
                do {
                    let catalog = try decoder.decode(BookCatalogKeys.self, from: jsonData)
                    print(catalog.userIdentifier)
                    print(catalog.index)
                    print(catalog.title)
                    print(catalog.completed)
                    
                    let encoder = JSONEncoder()
                    do {
                        let encodedJson = try encoder.encode(catalog)
                        if let jsonString = String(data: encodedJson, encoding: .utf8) {
                            print(jsonString)
                        }
                    } catch {
                            print("Encode failed")
                        }
                } catch {
                    print("Decode failed")
                }
            }
        }
        dataTask.resume()
    }
}

What’s if a key value pair in nil

If your json response doesn’t always send all the key value pairs, you can define your property as optional to avoid decode failures. Example code below

struct BookCatalog: Codable {
    let userId: Int
    let id: Int
    let title: String
    let completed: Bool //
    let test:String? // you can make this non optional to see the decoder fail as this “test” key value pair is not available in the response.
}

What’s if we don’t need all the values from the json

you can always define a smaller structure as needed. The below code would work just fine.

struct BookCatalog: Codable {
    let userId: Int
    let id: Int
}

Leave a comment