Search code examples
swiftjsondecoderspoonacular

Error: "Expected to decode Dictionary<String, Any> but found an array instead." — but I haven't defined a dictionary?


I'm working on a creative project, and I'm trying to decode content from an API database using Swift's JSONDecoder() function. I've built my structs, a getData() function, and I've set up a do-try-catch for the JSONDecoder() function. I'm having difficulty understanding what I'm doing to get the error I'm getting.

Here are my structs:

struct Response: Codable {
    let foundRecipes: [Recipe]
    let foundIngredients: [Ingredient]
}

struct Recipe: Codable {
    let id: Int
    let title: String
    let image: String
    let imageType: String
    let usedIngredientCount: Int
    let missedIngredientCount: Int
    let missedIngredients: [Ingredient]
    let usedIngredients: [Ingredient]
    let unusedIngredients: [Ingredient]
    let likes: Int
}

struct Ingredient: Codable {
    let id: Int
    let amount: Int
    let unit: String
    let unitLong: String
    let unitShort: String
    let aisle: String
    let name: String
    let original: String
    let originalString: String
    let origianalName: String
    let metaInformation: [String]
    let meta: [String]
    let image: String
}

Here's my getData() function:

    func getData(from url: String) {
    URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { data, response, error in
        guard let data = data, error == nil else {
            print("something went wrong.")
            return
        }
        var result: Response?
        do {
            result = try JSONDecoder().decode(Response.self, from: data)
        }
        catch {
            print("")
            print(String(describing: error)) // Right here is where the error hits.
        }
        
        guard let json = result else {
            return
        }
        print(json.foundRecipes)
    }).resume()
}

Here's a link to the API's documentation. The URL I'm calling in getData() links to the same structure of search as shown in their example: https://spoonacular.com/food-api/docs#Search-Recipes-by-Ingredients — and here's a screenshot of the url results for the exact search I'm working on: https://i.sstatic.net/MVtES.jpg

And finally, here's the full error that I'm catching:

typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))

My understanding of this error is that it's saying I told the JSONDecoder() to look for a Dictionary of <String, Any>, but it's at the link and only seeing an array. I'm confused, because I don't know where it thinks I'm providing a dictionary. Where am I screwing up? Not looking for specific code changes, just some guidance on what I'm missing.

Thanks in advance :)


Solution

  • As you can see in your image of the API data and in the API documentation you linked to, the API is returning an array (in the documentation, for example, you can see that it is surrounded by [...]). In fact, it looks like the API returns an array of Recipe.

    So, you can change your decoding call to this:

    var result: [Recipe]?
    do {
       result = try JSONDecoder().decode([Recipe].self, from: data)
       print(result)
    } catch {
      print(error)
    }
    

    Perhaps your idea for Response came from somewhere else, but the keys foundRecipes or foundIngredients don't show up in this particular API call.


    Also, thanks to @workingdog's for a useful comment about changing amount to a Double instead of an Int in your model.