Search code examples
iosjsonswift

Decoding JSON error "but found a dictionary instead"


I'm making a history of today iOS app using the API http://history.muffinlabs.com/date/4/19

Here is the data struct of me:

struct HistoryAPIResponse: Codable  {    
    let data: [Events]
}

struct Events: Codable {
    let year: String
    let text: String
    let html: String
}

And here's how I fetch the data from it:

import Foundation
import UIKit

final class HistoryAPICaller {
    static let shared = HistoryAPICaller()
    
    struct Constants {
        static let topHeadLinesURL = URL(string:
            "http://history.muffinlabs.com/date/4/19"
            )
        
        static let searchUrlString =
        
        "http://history.muffinlabs.com/date/4/19"
        
        
    }
    
    private init() {}
        
        public func getTopStories(completion: @escaping (Result<[Events], Error>) -> Void) {
            
            guard let html = Constants.topHeadLinesURL else {
                return
            }
            
            let task = URLSession.shared.dataTask(with: html) { data, _, error in
                
                if let error = error {
                    completion(.failure(error))
                }
                
                else if let data = data {
                    do {
                        _ = try JSONDecoder().decode(HistoryAPIResponse.self, from: data)           
                    }
                    catch {
                        completion(.failure(error))
                    }
            }
        }
            task.resume()
    }

    
    
    public func search(with query: String, completion: @escaping (Result<[Events], Error>) -> Void) {
        guard !query.trimmingCharacters(in: .whitespaces).isEmpty else {
            return
        }
        
        
        let urltring = Constants.searchUrlString + query
        guard let html = URL(string:urltring) else {
            return
        }
        
        let task = URLSession.shared.dataTask(with: html) { data, _, error in
            
            if let error = error {
                completion ( .failure(error))    
            }
            
            
            else if let data = data {
                do {
                    _ = try JSONDecoder().decode(HistoryAPIResponse.self, from: data)    
                }
                catch {
                    completion (.failure(error))   
                }
            }
        }
        task.resume()
    }
    
}

And in come one of the error message says:

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

I kind of understand the nature of the problem, that data is a dictionary, but I don't know how to solve it considering this is the structure of the API, that everything stores in the [data].

Any idea how to solve this issue? Appreciate the help in advance.


Solution

  • You need

    let res = try JSONDecoder().decode(Root.self, from: data)           
    
    struct Root: Codable  {    
        let data: HistoryAPIResponse
    }
    
    struct HistoryAPIResponse: Codable  {    
        let events: [Events] 
        private enum CodingKeys : String, CodingKey {
            case events = "Events" 
        }
    }
    
    struct Events: Codable {
        let year,text,html: String 
    }