DEV Community

Cover image for Catching useful errors when parsing JSON fails in Swift
acodeguy
acodeguy

Posted on

Catching useful errors when parsing JSON fails in Swift

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
}
Enter fullscreen mode Exit fullscreen mode

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...
Enter fullscreen mode Exit fullscreen mode

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)
    }
Enter fullscreen mode Exit fullscreen mode

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)].
Enter fullscreen mode Exit fullscreen mode

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)