Expected to decode Array<Any> but found a dictionary instead

Hello everyone! I've been scouring the internet and yes I have read the many threads on here with this exact scenario and all of the wonderful solutions. However none have worked for my particular application. Here is the API I'm using. API

I've taken the array brackets off of var mealList = [TopMeal]in the viewController about 100 times like most of the internet suggests. I've rearranged the structs several times. I've watched no telling how many YouTube videos and I've texted two people I know.... no solution yet. Just know reposting a duplicate topic is my last resort, I just want to know how to fix it so I can move on. This project is due tomorrow and it's not looking hot. Any help is very appreciated.

Here are my structs:

//Struct for top level meals
struct TopMeal: Codable {
    var topMeal: Meals
}

//struct for base meal details
struct Meals: Codable {
    var strMeal: String?
    var strMealThumb: String?
    var idMeal: String?

}``` 

**Last but not least**

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

var mealList = [TopMeal]()

@IBOutlet weak var tableView: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()
    
    getApiData {
        print("data loaded")
        self.tableView.reloadData()
    }
    tableView.delegate = self
    tableView.dataSource = self
    }

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return mealList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
    cell.textLabel?.text = "\(mealList[indexPath.row])"
    return cell
}

// MealApi.shared.getApiData(onCompletion: anonFunction)
func getApiData(completed: @escaping () -> ()) {
let url = URL(string: "https://www.themealdb.com/api/json/v1/1/filter.php?c=Dessert")

    URLSession.shared.dataTask(with: url!) { (data, response, error) in
        
        if error == nil {
            do {
                self.mealList = try JSONDecoder().decode([TopMeal].self, from: data!)
               
                DispatchQueue.main.async {
                    completed()
                }
            }
            catch {
                print(error)
            }
        }
    }.resume()
    
}

}```

The problem is that your JSON data is a dictionary at the top level, and it contains the desired array under that dictionary's meals key:

{"meals":[{"strMeal":"Apam balik","strMealThumb":"https:\/\/www.themealdb.com\/images\/media\/meals\/adxcbq1619787919.jpg","idMeal":"53049"},
^dict    ^array

You're going to need to decode the data as (for example) type [String: [TopMeal]].self to create a dictionary matching the actual JSON data, then get your array of top meals out of that dictionary.

This change needs to happen in the in my struct as well?
var topMeal = [String: [TopMeal]] ?

I changed the decoder to what you have listed and I get this error now
(I changed the struct names to help me and others better understand which I'm trying to access.
Cannot assign value of type '[String : [dessertDictionary]]' to type '[Desserts]'

I appreciate you responding, I feel like I'm close.

You have a choice whether to decode a JSON dictionary as a Swift dictionary or a custom Swift struct. At the top level, your JSON is a dictionary with (apparently) a single key/value pair, so you can choose to create a 3rd custom struct:

struct MealsData: Codable { 
    var meals: [TopMeal]
}

and decode it like this:

   let mealsData = try JSONDecoder().decode([MealsData].self, from: data)
    mealsList = mealsData.meals

Or, if you just use a Swift dictionary:

    let mealsDict = try JSONDecoder().decode([String: [TopMeals].self, from: data)
    mealsList = mealsDict["meals"]!

The two approaches are equivalent in this scenario. Once you have mealsList, you can proceed as you'd previously planned.

The two approaches are equivalent in this scenario.

They are not. If the key is missing, the first one will throw a nice error, while the second one will crash the app.

1 Like

It's good to point out that difference, but the equivalence I was referring to is in the decoding of the JSON, not the disposition of the result. It'd be equally valid to note that the two approaches are not equivalent because one produces a dictionary and the other produces a custom struct. :slight_smile: