The problem
The usual error that a JSONDecoder
gives isn't very useful on its own. If you've seen The data couldn't be read because it is missing
in the Xcode console while using an API, I'm gonna show you a way to get more rich data from the errors it throws.
Let's say you have a model that represents some data the API returns:
struct: Person: Codable {
let name: String
let age: Int
let city: String
}
And you're using a JSONDecoder
to parse some JSON into models, like so:
// build our demo JSON into Data for this demo. this will usually come from a server!
let data = """
[
{
"name": "Cameron",
"age": 19,
"city": "Los Angeles"
}
]
""".data(using: .utf8)!
// create a decoder, then parse the data returned
let decoder = try JSONDecoder()
decoder.decode([Person].self, from : data)
// and all is well...
A spanner in the works
Let's say, unbeknownst to you, the backend stops returning the city
property in the JSON. Parsing data into this model will fail with a very generic and unhelpful error like this:
The data couldn't be read because it is missing.
How the frak would you find out what went wrong!
A solution
You can put your decoding in a do-try block whilst catching very specific errors, like this:
do {
let people = try decoder.decode([Person].self, from: data)
} catch let DecodingError.keyNotFound(key, context) {
print("keyNotFound: '\(key.stringValue)' not found for \(context.codingPath).")
} catch let DecodingError.dataCorrupted(context) {
print("dataCorrupted: data corrupted. \(context.debugDescription)")
} catch let DecodingError.typeMismatch(type, context) {
print("typeMismatch: type mismatch of \(type) in \(context.debugDescription)")
} catch let DecodingError.valueNotFound(type, context) {
print("valueNotFound: value not found for \(type). \(context.debugDescription)")
} catch {
print(error.localizedDescription)
}
You'll now get errors that tell you when a type didn't match, a key or value way missing in the JSON, or when the data is corrupted in some way (malformed JSON), looking like this:
keyNotFound: 'city' not found for [_JSONKey(stringValue: "Index 0", intValue: 0)].
This error is saying that the item at index 0 in the array has no key named city
.
Armed with this you could now investigate the raw JSON using HTTP proxying (I like to use ProxyMan for this), and/or talk to the back-end team, see if they've made some changes and are aware of the breakage in the contract between client and server.
💡 Remember to delete the print statement before releasing the app!
Top comments (0)