'"Expected to decode Dictionary<String, Any> but found an array instead." Cannot seem to fix

I am creating an app which uses an external API to fetch JSON data. I keep getting the error above and I cannot figure out how to fix it. As you can see, I have tried making all the struct values optional and it still fails.

Here is my JSON data

[
    {
        "user_id": 433354,
        "property_id": 334528,
        "room_type_id": 399936,
        "periods": [
            {
                "start": "0001-01-01",
                "end": "0001-01-01",
                "available": 1,
                "closed_period": null,
                "bookings": [],
                "channel_calendars": []
            }
        ]
    }
]

Here is the model (I checked all the data types with the API documentation)

import Foundation

struct closed_period: Codable {
    var id: Int?
}

struct bookings: Codable {
    var id: Int?
    var status: String?
}

struct channel_calendars: Codable {
   var id: Int?
}

struct periods: Codable {
    var start: String?
    var end: String?
    var available: Int?
    var closed_period: closed_period?
    var bookings: [bookings]?
    var channel_calendars: [channel_calendars]?
}

struct Info: Codable {
    var user_id: Int?
    var property_id: Int?
    var room_type_id: Int?
    var periods: [periods]?
}

struct APIResults: Codable {
    var decodedResults: [Info]?
}

And this is the code that attempts to decode the JSON

let headers: HTTPHeaders = [
          "Accept": "application/json",
          "X-ApiKey": "{API Key}"
        ]
        
        AF.request("{API URL}", headers: headers).responseJSON { response in
            if let data = response.data {
                let json = String(data: data, encoding: .utf8)
                print("Response: \(json!)")
                let jsonData = json?.data(using: .utf8)!
                let jsonDecoded = try! JSONDecoder().decode(APIResults.self, from: jsonData!)
                print(jsonDecoded)
            }
        }

Finally, this is the console error I receive

2022-03-02 20:48:15.271752+0000 JSONDecoding[5621:1368607] 
JSONDecoding/ContentView.swift:29: Fatal error: 'try!' expression
unexpectedly raised an error: Swift.DecodingError.typeMismatch(
Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(
codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any>
but found an array instead.", underlyingError: nil))

Any help is greatly appreciated as I can't figure out the issue and this app is a project that I need to submit! Thanks everyone!



Solution 1:[1]

@Joakim answer's is right, APIResults should be deleted and you should use directly your Info struct:

See working example: https://swiftfiddle.com/kzopdsydabd7rdvyue4azstqbm

import Foundation

var json: [String : Any] = [
     "user_id": 433354,
     "property_id": 334528,
     "room_type_id": 399936,
     "periods": [ [
        "start": "0001-01-01",
        "end": "0001-01-01",
        "available": 1,
        "closed_period": nil,
        "bookings": [],
        "channel_calendars": []
        ]
    ]
]

struct closed_period: Codable {
    var id: Int?
}

struct bookings: Codable {
    var id: Int?
    var status: String?
}

struct channel_calendars: Codable {
   var id: Int?
}

struct periods: Codable {
    var start: String?
    var end: String?
    var available: Int?
    var closed_period: closed_period?
    var bookings: [bookings]?
    var channel_calendars: [channel_calendars]?
}

struct Info: Codable {
    var user_id: Int?
    var property_id: Int?
    var room_type_id: Int?
    var periods: [periods]?
}

struct APIResults: Codable {
    var decodedResults: [Info]?
}

extension Dictionary {
    
    /// Convert Dictionary to JSON string
    /// - Throws: exception if dictionary cannot be converted to JSON data or when data cannot be converted to UTF8 string
    /// - Returns: JSON string
    func toJson() throws -> String {
        let data = try JSONSerialization.data(withJSONObject: self)
        if let string = String(data: data, encoding: .utf8) {
            return string
        }
        throw NSError(domain: "Dictionary", code: 1, userInfo: ["message": "Data cannot be converted to .utf8 string"])
    }
}

let jsonString = try json.toJson()
let jsonData = try JSONSerialization.data(withJSONObject: json)

do {
    let results: Info = try JSONDecoder().decode(Info.self, from: jsonData)
    print(results)
} catch {
    print(error)
}

Your example use an array of Info object so yes, again use [Info].self in the decode method (in mine it decode only one Info object).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1