Search code examples
iosswiftcodablensurlrequest

Expected Array but found Dictionary in Swift


I am new to Codable in Swift, and am trying to get some data from an API (https://sportspagefeeds.com/documentation). I don't need to get all of the data from the API, just the field's specified in the model. I am getting this error when trying to run my code: typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))

Weirdly enough when I put the API response in a string then encode it I can successfully decode it with the same exact code. Anyone have any insight here? I am getting a 200 code from the response

code:

let request = NSMutableURLRequest(url: NSURL(string: "https://sportspage-feeds.p.rapidapi.com/games?league=NBA")! as URL,
                                                cachePolicy: .useProtocolCachePolicy,
                                            timeoutInterval: 10.0)
        request.httpMethod = "GET"
        request.allHTTPHeaderFields = headers
        
        let session = URLSession.shared
        session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
            
            if let data = data {
                do {
                    let dec = JSONDecoder()
                    dec.keyDecodingStrategy = .convertFromSnakeCase
                    let games = try dec.decode([Game].self, from: data)
                    print(games)
                    
                } catch {
                    print(error)
                    
                }
   
            }
        }).resume()

API return structure (from the link):

[
    {
        "gameId":"tv7mnr2-g7c4fe5-p95kxge-ptyhr72" ,
        "details":{
            "league":"NFL" ,
            "season":2018 ,
            "seasonType":"regular" ,
            "conferenceGame":true ,
            "divisionGame":true
        } ,
        "schedule":{
            "date":"2018-12-30T18:00:00.000Z" ,
            "tbaTime":false
        } ,
        "teams":{
            "away":{
                "team":"Miami Dolphins" ,
                "location":"Miami" ,
                "mascot":"Dolphins" ,
                "abbreviation":"MIA" ,
                "conference":"AFC" ,
                "division":"East"
            } ,
            "home":{
                "team":"Buffalo Bills" ,
                "location":"Buffalo" ,
                "mascot":"Bills" ,
                "abbreviation":"BUF" ,
                "conference":"AFC" ,
                "division":"East"
            }
        } ,
        "venue":{
            "name":"New Era Field" ,
            "city":"Orchard Park" ,
            "state":"NY" ,
            "neutralSite":false
        } ,
        "scoreboard":{
            "score":{
                "away":17 ,
                "home":42 ,
                "awayPeriods": [0 ,14 ,3 ,0] ,
                "homePeriods": [14 ,0 ,14 ,14]
            } ,
            "currentPeriod":4 ,
            "periodTimeRemaining":"0:00"
        } ,

[ etc...... ]

]

Here are my models:

public struct Game: Codable {
    public var gameId: String?
    public var details: Details?
    public var teams: Teams?
    public var scoreboard: Scoreboard?
}

public struct Details: Codable {
    public var league: String?
    
}

public struct Teams: Codable {
    public var home: Team?
    public var away: Team?
}

public struct Team: Codable {
    public var team: String?
    public var abbreviation: String?
}

public struct Scoreboard: Codable {
    public var score: Score?
    public var currentPeriod: Int?
    public var periodTimeRemaining: String?
}

public struct Score: Codable {
    public var away: Int?
    public var home: Int?
}

Solution

  • let see the response from api:

        "status": 200,
        "time": "2021-03-26T01:42:14.010Z",
        "games": 5,
        "skip": 0,
        "results": [....]
    

    The response is Dictionary, but you encode it to Array of [Game] the why you receive this error. You need create more model like this

    public struct GameResponse: Codable {
        var status: Int
        var time: String
        var games: Int
        var skip: Int
        var results: [Game]
    
    }
    

    And decode reponse to GameResponseModel:

        let games = try dec.decode(GameResponse.self, from: data)
    
    

    And change your game id to Int, because it Int not String

    public struct Game: Codable {
        public var gameId: Int?
        public var details: Details?
        public var teams: Teams?
        public var scoreboard: Scoreboard?
    }