1

I am using swift 4 xcode 9.2, I got the below error when using JSONDecoder.

typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array but found a dictionary instead.", underlyingError: nil))

class Hits: Codable { 
    let hits: [Hit] 

    init(hits: [Hit]) { 
       self.hits = hits 
    } 
}

class Hit: Codable { 
    let recipe: String 
    let uri: String 
    let label: String 
    let image: String 
    let source: String 
    let url: String 
    let shareAs: String 
    let yield: String 

    init(recipe: String, uri: String, label: String, image: String, source: String, url: String, shareAs: String, yield: String) {
        self.recipe = recipe 
        self.uri = uri 
        self.label = label 
        self.image = image 
        self.source = source 
        self.url = url 
        self.shareAs = shareAs 
        self.yield = yield
    }
}

func downloadJSON() { 
    guard let downloadURL = url else {return}   
    URLSession.shared.dataTask(with: downloadURL) { (data, urlResponse, error) in 
       guard let data = data, error == nil, urlResponse != nil else { print("Something is wrong"); return } 
       print("download completed") 
       do { 
          let decoder = JSONDecoder() 
          let foods = try decoder.decode([Hits].self, from: data)  
          print(foods) 
       } catch { 
          print(error) 
       } 
   }.resume()
}

Here is the JSON: https://api.edamam.com/search?q=chicken&app_id=110d8671&app_key=3f01522208d512f592625dfcd163ff5c&from=0&to=10

lpy
  • 25
  • 5
  • show us your code so we can help you. i assume you have taken the wrong type (array) instead of (dictionary) when decoding... – Chris Dec 07 '19 at 13:53
  • class Hits: Codable{ let hits: [Hit] init(hits: [Hit]){ self.hits = hits } } – lpy Dec 07 '19 at 13:58
  • class Hit: Codable{ let recipe: String let uri: String let label: String let image: String let source: String let url: String let shareAs: String let yield: String init(recipe: String, uri: String, label: String, image: String, source: String, url: String, shareAs: String, yield: String){ self.recipe = recipe self.uri = uri self.label = label self.image = image self.source = source self.url = url self.shareAs = shareAs self.yield = yield – lpy Dec 07 '19 at 13:58
  • Here is the function of downloadJSON() func downloadJSON(){ guard let downloadURL = url else {return} URLSession.shared.dataTask(with: downloadURL) { (data, urlResponse, error) in guard let data = data, error == nil, urlResponse != nil else{ print("Something is wrong") return } print("download completed") do{ let decoder = JSONDecoder() let foods = try decoder.decode([Hits].self, from: data) print(foods) } catch{ print(error) } }.resume() – lpy Dec 07 '19 at 14:00
  • you can edit your question -> there you can mark your code and then tap on the "parenthesis" -> then your code will be formatted nicely and we will understand it better ;) – Chris Dec 07 '19 at 14:02
  • the problem is this line: try decoder.decode([Hits].self, from: data) print(foods) } -> you expect an array : [Hits] -> but it is an dictionary (your json) – Chris Dec 07 '19 at 14:04

2 Answers2

2

The error clears states that you are trying to decode an array but the actual type is a dictionary (single object).

Replace

let foods = try decoder.decode([Hits].self, from: data)

with

let foods = try decoder.decode(Hits.self, from: data)

And your classes (actually structs are sufficient) are

struct Recipe : Decodable {
    let uri : URL
    let label : String
    let image : URL
    let source : String
    let url : URL
    let shareAs : URL
    let yield : Double
    let calories, totalWeight, totalTime : Double
}

struct Hits: Decodable {
    let hits: [Hit]
}

struct Hit: Decodable {
    let recipe: Recipe
}
vadian
  • 274,689
  • 30
  • 353
  • 361
  • After that I got the error below. typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [TestTableView.Hits.(CodingKeys in F57C5D8E25B3781A1A1D556B4F4F396A).hits, Foundation.(_JSONKey in _12768CA107A31EF2DCE034FD75B541C9)(stringValue: "Index 0", intValue: Optional(0)), TestTableView.Hit.(CodingKeys in _F57C5D8E25B3781A1A1D556B4F4F396A).recipe], debugDescription: "Expected to decode String but found a dictionary instead.", underlyingError: nil)) – lpy Dec 07 '19 at 16:41
  • The error says that the value for `recipe` is a dictionary, in terms of Codable another struct. – vadian Dec 07 '19 at 18:19
  • You have to declare another struct or class like `Hit` – vadian Dec 08 '19 at 09:08
  • I have created another class like this. class Hits: Codable { let hits: [Hit] init(hits: [Hit]) { self.hits = hits } } – lpy Dec 08 '19 at 09:20
  • The class must be named `Recipe` and must contain properties which match the JSON keys and types. By the way all initializers are not needed if you adopt Codable. – vadian Dec 08 '19 at 09:26
  • Finally, I can get the JSON result. However, I found that all the values of the JSON result is nil. How can I do? – lpy Dec 08 '19 at 09:47
  • It's hard to answer without knowing the JSON – vadian Dec 08 '19 at 09:49
  • Here is the result: Hit(bookmarked: nil, bought: nil, recipe: Optional(TestTableView.Recipe(calories: nil, cautions: nil, dietLabels: nil, digest: nil, healthLabels: nil, image: nil, ingredientLines: nil, ingredients: nil, label: nil, shareAs: nil, source: nil, totalTime: nil, totalWeight: nil, uri: nil, url: nil, yield: nil))) – lpy Dec 08 '19 at 10:00
  • Once again, it's impossible to answer without knowing the JSON. Basically declare properties non-optional to get errors. And please [edit](https://stackoverflow.com/posts/59226649/edit) your question to add information. – vadian Dec 08 '19 at 10:04
0
hits = try decoder.decode(Hits.self from: data)
Chris
  • 7,579
  • 3
  • 18
  • 38
  • Do I need to define hits = dict["hits"] in the swift.file? – lpy Dec 07 '19 at 14:16
  • 1
    While this code may answer the question, providing additional context regarding *why* and/or *how* this code answers the question improves its long-term value. – Tân Dec 08 '19 at 07:38