Expected to Decode Array<Any> But Found a Dictionary Instead

debugDescription: Expected to decode Array Any but found a dictionary instead. , underlyingError: nil)

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

Expected to decode Array Any but found a dictionary instead

First of all the JSON does not contain any array. It's very very easy to read JSON. There are only 2 (two!) collection types, array [] and dictionary {}. As you can see there are no square brackets at all in the JSON string.

Any (sub)dictionary {} has to be decoded to its own type, so it's supposed to be

struct Root : Decodable {
private enum CodingKeys : String, CodingKey { case raw = "RAW" }
let raw : RAW
}

struct RAW : Decodable {
private enum CodingKeys : String, CodingKey { case eth = "ETH" }
let eth : ETH
}

struct ETH : Decodable {
private enum CodingKeys : String, CodingKey { case usd = "USD" }
let usd : USD
}

struct USD : Decodable {

private enum CodingKeys : String, CodingKey {
case type = "TYPE"
case market = "MARKET"
case price = "PRICE"
case percentChange24h = "CHANGEPCT24HOUR"
}
let type : String
let market : String
let price : Double
let percentChange24h : Double
}

To decode the JSON and and print percentChange24h you have to write

 let result = try JSONDecoder().decode(Root.self, from: data)
print("percentChange24h", result.raw.eth.usd.percentChange24h)

Decoding JSON Error: Expected to decode Array Any but found a dictionary instead. , underlyingError: nil

my observations. Your error is probably not to do with Combine.

you are trying to decode "[FilmModel].self", but the response is only for one film, FilmModel.self.

Also I would make most/all var in your FilmModel etc... optional, add "?".
It works well in my test.

EDIT:

This is the code I use to test my answer. Works well for me:

import Foundation
import SwiftUI
import Combine


@main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}

struct ContentView: View {
@StateObject var movies = FilmViewModel()

var body: some View {
VStack (spacing: 50) {
Text("movie test")
ForEach(movies.films, id: \.id) { movie in
Text(movie.title ?? "no title").foregroundColor(.red)
}
}
}
}


class FilmDataService {
@Published var films: [FilmModel] = []

var filmSubscription: AnyCancellable?

init() {
getFilms()
}

private func getFilms() {
guard let url = URL(string: "https://api.themoviedb.org/3/movie/550?api_key=1f632307cea6ce33f288f9a232b9803b") else { return }

filmSubscription = URLSession.shared.dataTaskPublisher(for: url)
.subscribe(on: DispatchQueue.global(qos: .default))
.tryMap { (output) -> Data in
guard let response = output.response as? HTTPURLResponse,
response.statusCode >= 200 && response.statusCode < 300 else {
throw URLError(.badServerResponse)
}
return output.data
}
.decode(type: FilmModel.self, decoder: JSONDecoder()) // <--- here
.receive(on: DispatchQueue.main)
.sink { (completion) in
switch completion {
case .finished:
break
case .failure(let error):
print(error)
}
} receiveValue: { [weak self] (returnedFilms) in
self?.films.append(returnedFilms) // <--- here
self?.filmSubscription?.cancel()
}
}

}

class FilmViewModel: ObservableObject {
@Published var tabBarImageNames = ["house", "rectangle.stack", "clock.arrow.circlepath", "square.and.arrow.down"]
@Published var films: [FilmModel] = []

private let dataService = FilmDataService()
private var cancellables = Set<AnyCancellable>()

init() {
addSubscribers()
}

func addSubscribers() {
dataService.$films
.sink { [weak self] (returnedFilms) in
self?.films = returnedFilms
}
.store(in: &cancellables)
}

}

struct FilmModel: Identifiable, Codable {
let adult: Bool?
let backdropPath: String?
let budget: Int?
let genres: [Genre]?
let homepage: String?
let id: Int
let imdbID, originalLanguage, originalTitle, overview: String?
let popularity: Double?
let posterPath: String?
let productionCompanies: [ProductionCompany]?
let productionCountries: [ProductionCountry]?
let releaseDate: String?
let revenue, runtime: Int?
let spokenLanguages: [SpokenLanguage]?
let status, tagline, title: String?
let video: Bool?
let voteAverage: Double?
let voteCount: Int?

enum CodingKeys: String, CodingKey {
case adult
case backdropPath = "backdrop_path"
case budget
case genres
case homepage
case id
case imdbID = "imbd_id"
case originalLanguage = "original_language"
case originalTitle = "original_title"
case overview
case popularity
case posterPath = "poster_path"
case productionCompanies = "production_companies"
case productionCountries = "production_countries"
case releaseDate = "release_date"
case revenue
case runtime
case spokenLanguages = "spoken_languages"
case status, tagline, title
case video
case voteAverage = "vote_average"
case voteCount = "vote_count"
}
}

struct Genre: Identifiable, Codable {
let id: Int
let name: String?
}

struct ProductionCompany: Codable {
let id: Int
let logoPath: String?
let name, originCountry: String?
}

struct ProductionCountry: Codable {
let iso3166_1, name: String?
}

struct SpokenLanguage: Codable {
let englishName, iso639_1, name: String?
}

Expected to decode Dictionary String, Any but found an array instead. , underlyingError: nil; GitHub repo showing app; SwiftUI

for a minimalist working example code, try this:

struct Repository: Decodable, Identifiable {
let id: Int
let name, fullName: String
let owner: Owner

enum CodingKeys: String, CodingKey {
case id, name, owner
case fullName = "full_name" // <-- here
}
}

struct Owner : Decodable, Identifiable {
let id: Int
let reposUrl : String

enum CodingKeys: String, CodingKey, CaseIterable {
case id
case reposUrl = "repos_url" // <-- here
}
}

class NetworkingManager: ObservableObject{
@Published var owner = [Owner]()

init() {
loadData()
}

func loadData() {
guard let url = URL(string: "https://api.github.com/users/jacobtoye/repos") else { return }
URLSession.shared.dataTask(with: url) {(data, _, _) in
guard let data = data else { return }
DispatchQueue.main.async { // <-- here
do {
let repos = try JSONDecoder().decode([Repository].self, from: data) // <-- here
repos.forEach{ self.owner.append($0.owner) }
} catch {
print("error: \(error)")
}
}
}.resume()
}
}

struct ContentView: View {
@StateObject var netManager = NetworkingManager()

var body: some View {
List {
ForEach(netManager.owner) { item in
Text(item.reposUrl)
}
}
}
}

This should give you a list of "https://api.github.com/users/jacobtoye/repos" because that is what the data consist of.

EDIT-1: to list all repos

class NetworkingManager: ObservableObject{
@Published var repos = [Repository]() // <-- here repos

init() {
loadData()
}

func loadData() {
guard let url = URL(string: "https://api.github.com/users/jacobtoye/repos") else { return }
URLSession.shared.dataTask(with: url) {(data, _, _) in
guard let data = data else { return }
DispatchQueue.main.async { // <-- here
do {
self.repos = try JSONDecoder().decode([Repository].self, from: data) // <-- here
} catch {
print("error: \(error)")
}
}
}.resume()
}
}

struct ContentView: View {
@StateObject var netManager = NetworkingManager()

var body: some View {
List {
ForEach(netManager.repos) { repo in
VStack {
Text(repo.fullName).foregroundColor(.blue)
Text(repo.owner.reposUrl)
}
}
}
}
}

Expected to decode Array Any but found a dictionary instead. error when parsing nested JSON SwiftUI

You should actually use Campaigns:

var campaignsResult: Campaigns = load("campaigns.json")

And later on access the actual campaign entries using campaignsResult.campaigns.


You currently load into a [Campaign] and therefore you state you want an array at the top level which does not match the actual json data which in turn causes the error stating a mismatch between array and dictionary.



Related Topics



Leave a reply



Submit