Search code examples
jsonswiftswift4codable

Safe JSON Decoding. Still getting Key not found: No value associated with key'


IMGUR image search returns json:

    {"data": [{
                "title": "Family :)",
               "images": [{
                    "title": null,
                    "description": null,
                    "nsfw": null,
                    "link": "https:\/\/i.imgur.com\/oe5BrCu.jpg",
            }]
        } ]
    }

Model Object is configured:

struct ImgurResponse: Codable {
    let data: [ImageData]
}

struct ImageData: Codable {
    let title: String
    let images: [Image]
}

struct Image: Codable {
    let title, description, nsfw: String
    let link: String
    
    enum CodingKeys: String, CodingKey {
        case title
        case description
        case nsfw
        case link
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        title = try container.decodeIfPresent(String.self, forKey: .title) ?? ""
        description = try container.decodeIfPresent(String.self, forKey: .description) ?? ""
        nsfw = try container.decodeIfPresent(String.self, forKey: .nsfw) ?? ""
        link = try container.decodeIfPresent(String.self, forKey: .link) ?? ""
    }

Calling JSONDecoder():

let response = try JSONDecoder().decode(ImgurResponse.self, from: data)          

When putting a brake point on the last line 'link = ' I land on it about 48 times. As i understand, we are able to create Image object 48 times.

Yet, im getting an error and 'response' isn't created:

Key 'CodingKeys(stringValue: "images", intValue: nil)' not found: No value associated with key CodingKeys(stringValue: "images", intValue: nil) ("images").
codingPath: [CodingKeys(stringValue: "data", intValue: nil), _JSONKey(stringValue: "Index 49", intValue: 49)]

What other 'safe' decoding practices should I implement?


Solution

  • The error means that one of the JSON objects that you map to ImageData is missing a key "images", yet ImageData expects it to be there, since it's not optional.

    To fix, you need to make the property images optional:

    struct ImageData: Codable {
       let title: String
       let images: [Image]? // <- here
    }
    

    Typically, an optional array is hard to work with, so you might want to consider creating an empty array when the key is missing:

    struct ImageData {
       let title: String
       let images: [Image]
    }
    
    extension ImageData: Codable {
       init(from decoder: Decoder) throws {
          let container = try decoder.container(keyedBy: CodingKeys.self)
            
          title = try container.decode(String.self, forKey: .title)
          images = try container.decodeIfPresent([Image].self, forKey: .images) ?? []
       }
    }