Search code examples
iosjsonswiftjsondecoder

JSON Decoding Error for 2 Links Returning Similar Structures


I have the following piece of code available here that decodes a JSON response from the Kitsu API (seriously just copy and paste in a playground environment and you should be good to go).

I am running into some decoding error that makes the code in the try statement fail and I have no idea why.

I have two links that return the same JSON body (different results but same structure) except one fails and one doesn't.

// "https://kitsu.io/api/edge/anime?sort=popularityRank" <-- works
// "https://kitsu.io/api/edge/anime?sort=-startDate" <-- does not work

To help debug the one that fails I print out data with the following statement:

print(String(data: data!, encoding: String.Encoding.utf8) as Any) // "as Any" to suppress warnings

And with this I am able to see the data object contains everything I need, so I'm ruling out a bad response (text was too large to copy so here's a screenshot, you'll get the picture):

enter image description here

If I had to guess, the issue is in the parsing, but the way I'm parsing it works for the first link. What exactly can I do to debug this? I've compared the json side by side and like I said, the structure between the responses is the same, only the content differs.


Solution

  • If you read the error closely, you'd find that it's quite descriptive. So, don't hide the error, like you did with print("error, wtf"), and instead log/print it:

    do {
       let animeData = try JSONDecoder().decode(AnimeData.self, from: data!)
    } catch {
       print(error)
    }
    

    And the error would be: valueNotFound(Swift.KeyedDecodingContainer<__lldb_expr_163.CoverImage.CodingKeys>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "data", intValue: nil), _JSONKey(stringValue: "Index 4", intValue: 4), CodingKeys(stringValue: "attributes", intValue: nil), CodingKeys(stringValue: "coverImage", intValue: nil)], debugDescription: "Cannot get keyed decoding container -- found null value instead.", underlyingError: nil))

    I find it easier to read the error backwards, i.e. bottom up.

    Right away, you see that the issue is "found null value instead" - i.e. your model didn't have an optional for something that was null.

    Where? CodingKeys(stringValue: "coverImage", intValue: nil)] - so, coverImage is null.

    Where is that? CodingKeys(stringValue: "attributes", intValue: nil) - under attributes, which you probably knew.

    But which one, since it's a property of an array element? [CodingKeys(stringValue: "data", intValue: nil), _JSONKey(stringValue: "Index 4", intValue: 4) - an element of data index 4, i.e. data[4], which is the fifth element.

    Long story short, at least one (but actually two) of the Anime objects' attribute property has coverImage: null in the "broken" response. To solve, make this property optional:

    class Attributes: Codable {
       // other properties
       let coverImage: CoverImage?
    }