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

Hi, I have this problem for quite a while and still didn’t solved it. I tried numerous things.

the error i get is

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

this is my product struct

import Foundation
import SwiftyJSON



struct ProductDetail: Codable {
    var code: String?
    var product: [Product?]
}
struct Product: Codable {
    var _id: String?
    var additives_tags: [String]? // [JSON?]
    var allergens_tags: [String]?// [JSON?]
    var brand_owner: String?
    var countries_tags: [String]?  // [JSON?]
    var image_url: String?
    var image_front_small_url: String?
    var image_front_thumb_url: String?
    var image_front_url: String?
    var image_ingredients_small_url: String?
    var image_ingredients_thumb_url: String?
    var image_ingredients_url: String?
    
}

and this is the fetch function

func fetchProduct(completionHandler: @escaping ([ProductDetail]) -> Void){
        let url = URL(string: "https://world.openfoodfacts.org/api/v0/product/87222241)")!
        
        URLSession.shared.dataTask(with: url) { (data,
        response, error) in
            guard let data = data else { return}
            
            do {
                let productData = try JSONDecoder().decode([ProductDetail].self, from: data)
                print(productData)
                completionHandler(productData)
            }catch {
                let error = error
                print(error)
            }
            
        }.resume()
        
    }

the json looks like this

code": "87222241",
    "product": {
        "_id": "87222241",
        "_keywords": [
            "aa",
            "drink"
        ],
        "added_countries_tags": [],
        "additives_debug_tags": [],
        "additives_old_tags": [],
        "additives_original_tags": [],
        "additives_prev_original_tags": [],
        "additives_tags": [],
        "allergens": "",
        "allergens_from_ingredients": "",
        "allergens_from_user": "(fr) ",
        "allergens_hierarchy": [],
        "allergens_tags": [],
        "amino_acids_prev_tags": [],
        "amino_acids_tags": [],
        "brands": "AA drink ",
        "brands_tags": [
            "aa-drink"
        ],
        "categories_debug_tags": [],
        "categories_hierarchy": [],
        "categories_prev_hierarchy": [],
        "categories_prev_tags": [],
        "categories_properties": {},
        "categories_properties_tags": [
            "all-products",
            "categories-unknown",
            "agribalyse-food-code-unknown",
            "agribalyse-proxy-food-code-unknown",
            "ciqual-food-code-unknown",
            "agribalyse-unknown"
        ],

thanks for helping me out

Assuming that he only thing missing from your example json was the opening and closing curly bracket:
Your reply is a json object, not an array, in the type of product detail
change your code to

let productData = try JSONDecoder().decode(ProductDetail.self, from: data)

thanks for helping, I’ve changed the code like you said but still get the same error.
if you want to have a look at the json file you can have a look at https://world.openfoodfacts.org/api/v0/product/87222241

Because there are other issues in your struct representation of the JSON you are getting from that API.

Just from first glance:

var product: [Product?]

product is not an array in the JSON, so it shouldn’t be an array in your struct.

let url = 
    URL(string: "https://world.openfoodfacts.org/api/v0/product/87222241)")!

You have an extraneous ) at the end of your URL string.

Looking into it some more:

var brand_owner: String?

There is no key brand_owner in the JSON you are consuming.

Looking at the returned response, you probably don’t need all those Optionals either.

You need to better translate the structure of the JSON to your Swift struct.

1 Like

Here is a working example you can use to build off of. Obviously, I didn’t bother with all of the keys in the JSON, but hopefully you can extrapolate from this example to fit your needs.

struct ProductDetail: Codable {
    let code: String
    let product: Product
}

struct Product: Identifiable, Codable {
    let id: String
    let productName: String
    let additivesTags: [String]
    let allergensTags: [String]
    //let brandOwner: String
    let countriesTags: [String]
    let imageUrl: URL
    let imageFrontUrl: URL
    let imageFrontSmallUrl: URL
    let imageFrontThumbUrl: URL
    let imageIngredientsUrl: URL
    let imageIngredientsSmallUrl: URL
    let imageIngredientsThumbUrl: URL
}

class FoodAPI: ObservableObject {
    func fetchProduct(completionHandler: @escaping (ProductDetail) -> Void){
        let url = URL(string: "https://world.openfoodfacts.org/api/v0/product/87222241")!
        
        URLSession.shared.dataTask(with: url) { (data,
                                                 response, error) in
            guard let data = data else { return}
            
            do {
                let decoder = JSONDecoder()
                decoder.keyDecodingStrategy = .convertFromSnakeCase
                let productData = try decoder.decode(ProductDetail.self, from: data)
                print(productData)
                completionHandler(productData)
            }catch {
                let error = error
                print(error)
            }
            
        }.resume()
    }
}

struct FoodAPIView: View {
    @StateObject var foodAPI = FoodAPI()
    
    @State private var product: Product? = nil
    
    var body: some View {
        VStack {
            if let product = product {
                Text(product.productName)
                    .font(.title)
                Text(product.id)
            } else {
                Text("Nothing loaded yet")
                    .font(.title)
            }
        }
        .onAppear {
            foodAPI.fetchProduct { productDetail in
                product = productDetail.product
            }
        }
    }
}
2 Likes