21

I want to load an online json file into my application, but I am running into this error:

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

I have looked on stackoverflow but other sollutions didn't help to solve mine.

My JSON:

{
  "copyright" : "NHL and the NHL Shield are registered trademarks of the National Hockey League. NHL and NHL team marks are the property of the NHL and its teams. © NHL 2022. All Rights Reserved.",
  "totalItems" : 0,
  "totalEvents" : 0,
  "totalGames" : 0,
  "totalMatches" : 0,
  "metaData" : {
    "timeStamp" : "20220813_172145"
  },
  "wait" : 10,
  "dates" : [ ]
}

My datamodel:

import Foundation

struct Initial: Codable {
    let copyright: String
    let totalItems: Int
    let totalEvents: Int
    let totalGames: Int
    let totalMatches: Int
    let wait: Int
    let dates: [Dates]
}

struct Dates: Codable {
    let date: String
    let totalItems: Int
    let totalEvents: Int
    let totalGames: Int
    let totalMatches: Int
    let games: [Game]
}

struct Game: Codable {
    let gamePk: Int
    let link: String
    let gameType: String
    let season: String
    let gameDate: String
    let status: Status
    let teams: Team
    let venue: Venue
    let content: Content
}

struct Status: Codable {
    let abstractGameState: String
    let codedGameState: Int
    let detailedState: String
    let statusCode: Int
    let startTimeTBD: Bool
}

struct Team: Codable {
    let away: Away
    let home: Home
}

struct Away: Codable {
    let leagueRecord: LeagueRecord
    let score: Int
    let team: TeamInfo
}

struct Home: Codable {
    let leagueRecord: LeagueRecord
    let score: Int
    let team: TeamInfo
}

struct LeagueRecord: Codable {
    let wins: Int
    let losses: Int
    let type: String
}

struct TeamInfo: Codable {
    let id: Int
    let name: String
    let link: String
}

struct Venue: Codable {
    let name: String
    let link: String
}

struct Content: Codable {
    let link: String
}

and here is my viewcontroller

import UIKit

class TodaysGamesTableViewController: UITableViewController {
    var todaysGamesURL: URL = URL(string: "https://statsapi.web.nhl.com/api/v1/schedule")!
    
    var gameData: [Dates] = []
    let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)

    override func viewDidLoad() {
        super.viewDidLoad()
        loadTodaysGames()
    }

    func loadTodaysGames(){
        print("load Games")
        
        view.addSubview(activityIndicator)
        activityIndicator.frame = view.bounds
        activityIndicator.startAnimating()
        
        let todaysGamesDatatask = URLSession.shared.dataTask(with: todaysGamesURL, completionHandler: dataLoaded)
        
        todaysGamesDatatask.resume()
    }
    
    func dataLoaded(data:Data?,response:URLResponse?,error:Error?){
        if let detailData = data{
            print("detaildata", detailData)
            let decoder = JSONDecoder()
            do {
                let jsondata = try decoder.decode([Dates].self, from: detailData)
                gameData = jsondata //Hier .instantie wil doen krijg ik ook een error
               
                DispatchQueue.main.async{
                    self.tableView.reloadData()
                }
            }catch let error{
                print(error)
            }
        }else{
            print(error!)
        }
    }
SmileBot
  • 19,393
  • 7
  • 65
  • 62
Brent Le Comte
  • 255
  • 1
  • 2
  • 7

3 Answers3

32

Please learn to understand the decoding error messages, they are very descriptive.

The error says you are going to decode an array but the actual object is a dictionary (the target struct).

First take a look at the beginning of the JSON

{
  "copyright" : "NHL and the NHL Shield are registered trademarks of the National Hockey League. NHL and NHL team marks are the property of the NHL and its teams. © NHL 2018. All Rights Reserved.",
  "totalItems" : 2,
  "totalEvents" : 0,
  "totalGames" : 2,
  "totalMatches" : 0,
  "wait" : 10,
  "dates" : [ {
    "date" : "2018-05-04",

It starts with a { which is a dictionary (an array is [) but you want to decode an array ([Dates]), that's the type mismatch the error message is referring to.


But this is only half the solution. After changing the line to try decoder.decode(Dates.self you will get another error that there is no value for key copyright.

Look again at the JSON and compare the keys with the struct members. The struct whose members match the JSON keys is Initial and you have to get the dates array to populate gameData.

let jsondata = try decoder.decode(Initial.self, from: detailData)
gameData = jsondata.dates
vadian
  • 274,689
  • 30
  • 353
  • 361
  • 2
    Thanks for helping me out! I ran into some keyNotFound errors now but I'll try to fix them on my own! I am relatively new to swift so any help is welcome! Thanks again! – Brent Le Comte May 05 '18 at 06:21
8

The JSON is represented by your Initial struct, not an array of Dates.

Change:

let jsondata = try decoder.decode([Dates].self, from: detailData)

to:

let jsondata = try decoder.decode(Initial.self, from: detailData)
rmaddy
  • 314,917
  • 42
  • 532
  • 579
2

Correct Answer is done previously from my two friends

but you have to do it better i will provide solution for you to make code more clean and will give you array of Dates

here is your model with codable

   import Foundation

struct Initial: Codable {
    let copyright: String
    let totalItems: Int
    let totalEvents: Int
    let totalGames: Int
    let totalMatches: Int
    let wait: Int
    let dates: [Dates]
}

struct Dates: Codable {
    let date: String
    let totalItems: Int
    let totalEvents: Int
    let totalGames: Int
    let totalMatches: Int
    let games: [Game]
}

struct Game: Codable {
    let gamePk: Int
    let link: String
    let gameType: String
    let season: String
    let gameDate: String
    let status: Status
    let teams: Team
    let venue: Venue
    let content: Content
}

struct Status: Codable {
    let abstractGameState: String
    let codedGameState: Int
    let detailedState: String
    let statusCode: Int
    let startTimeTBD: Bool
}

struct Team: Codable {
    let away: Away
    let home: Home
}

struct Away: Codable {
    let leagueRecord: LeagueRecord
    let score: Int
    let team: TeamInfo
}

struct Home: Codable {
    let leagueRecord: LeagueRecord
    let score: Int
    let team: TeamInfo
}

struct LeagueRecord: Codable {
    let wins: Int
    let losses: Int
    let type: String
}

struct TeamInfo: Codable {
    let id: Int
    let name: String
    let link: String
}

struct Venue: Codable {
    let name: String
    let link: String
}

struct Content: Codable {
    let link: String
}

// MARK: Convenience initializers

extension Initial {
    init(data: Data) throws {
        self = try JSONDecoder().decode(Initial.self, from: data)
    }

}

And Here is Controller Will make solution more easy

class ViewController: UIViewController {
    var todaysGamesURL: URL = URL(string: "https://statsapi.web.nhl.com/api/v1/schedule")!

    var gameData: Initial?
    let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)





    override func viewDidLoad() {
        super.viewDidLoad()

        self.loadTodaysGames()

    }

        func loadTodaysGames(){
    print("load Games")
    let todaysGamesDatatask = URLSession.shared.dataTask(with: todaysGamesURL, completionHandler: dataLoaded)
    todaysGamesDatatask.resume()
}
func dataLoaded(data:Data?,response:URLResponse?,error:Error?){
    if let detailData = data {
        if  let inital = try? Initial.init(data: detailData){
           print(inital.dates)
        }else{
             // print("Initial")
        }
    }else{
        print(error!)
    }
}

}
Abdelahad Darwish
  • 5,969
  • 1
  • 17
  • 35