Skip to main content

Command Palette

Search for a command to run...

Using Codable to Deserialize JSON

Updated
•3 min read
Using Codable to Deserialize JSON
C

Passionate about: Defensive Programming • Effective Engineering Documentation • Emotion-Centered Design and Microcopy

Codable is a powerful Swift feature that has already been around for quite some time, and is super useful for building network requests.

As a refresher, a while back I wrote a post on making API calls and handling the JSON responses returned from those calls. In that post, I wrote about handling JSON responses using Apple’s JSONSerialization class, which lets you convert JSON to Foundation objects in Swift. JSONSerialization is fairly straightforward to use; it allows you to access JSON keys using bracket notation subscripts. The downside of this is that if your JSON data is deeply nested, you’ll be writing many layers of bracket notation subscripts and validity checks each time. And with deeply nested JSON, it’s easy to get visually lost as you switch between reading the JSON and writing the corresponding bracket notation subscript.

An alternative to using JSONSerialization is to use Apple’s Codable type alias. Codable, available in Swift 4, lets you convert JSON to a struct or enum so it’s easier to visualize the data. Codable is a type alias, meaning it’s a variable name you can define to concisely represent an object such as a struct. This is convenient if you’ll be passing the said object around and want to ensure that the passed objects conform exactly to the object you’ve created (i.e. matching properties).

I’ll show a simple example using this Zoo Animal API.

First, you need to create a Codable struct that maps to your network call’s expected response. (If you don’t know the expected response, check your API’s documentation to see if it shows a sample response. Or, you can use a tool like Postman to quickly test the network call and see its response.)

struct AnimalProfile: Codable {
    let name: String
    let latin_name: String
    let animal_type: String
    let active_time: String
    let length_min: String
    let length_max: String
    let weight_min: String
    let weight_max: String
    let lifespan: String
    let habitat: String
    let diet: String
    let geo_range: String
    let image_link: String
    let id: Int
}

Important note: make sure that your Codable’s property names exactly match the fields in the network call’s expected response, otherwise the decoding won’t work.

Next, we’ll write the method for making the network request.

func fetchProfile(completion: @escaping (_ didSucceed: Bool, _ profile: AnimalProfile?, _ error: String?) -> Void) {
    let url = URL(string: "https://zoo-animal-api.herokuapp.com/animals/rand")
    let request = URLRequest(url: url!.absoluteURL)
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        if let error = error {
            completion(false, nil, error.localizedDescription)
            return
        }

        guard let animalData = data else {
            completion(false, nil, error?.localizedDescription)
            return
        }

        var animalProfile: AnimalProfile?
        do {
            animalProfile = try JSONDecoder().decode(AnimalProfile.self, from: animalData)
        } catch {
            completion(false, nil, error.localizedDescription)
            return
        }

        // All checks passed; success!
        completion(true, animalProfile, nil)
    }
    task.resume()
}

And finally, we’ll call the method we just wrote above:

fetchProfile(completion: { didSucceed, profile, error in
    if didSucceed {
        guard let animalProfile = profile else {
            return
        }
        print("The \(animalProfile.name), found in \(animalProfile.geo_range), eats a diet of \(animalProfile.diet.lowercased()).")
    } else {
       print(error ?? "")
    }
})

More from this blog

Y

Yours Swiftly šŸ“±āœØ

10 posts

iOS Developer based in the Bay Area ā˜€ļø