Using Codable to Deserialize JSON

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 ?? "")
}
})



