I am getting calling my API and getting the exact response how my models are setup. Everything should have worked properly. But for some reason I am getting the following error.
typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "answer", intValue: nil), CodingKeys(stringValue: "quiz", intValue: nil), CodingKeys(stringValue: "category", intValue: nil)], debugDescription: "Expected to decode Dictionary<String, Any> but found a string/data instead.", underlyingError: nil))
I am getting the following response from server.
[{"_id":null,"max":1,"answer":{"_id":"612749f150e4691d7d8be86d","wordCount":19,"isDeleted":false,"type":"Real","text":"randome text for 43","quiz":{"_id":"61235bc6d0dd5339b7ff56d3","scheduled_at":"2021-08-31T06:39:30.134Z","timeUsed":96,"type":"Scheduled","isDeleted":false,"category":"61235bc5d0dd5339b7ff563b","name":"Delectus quia architecto voluptates at.","__v":0,"created_at":"2021-08-23T08:26:46.227Z","updated_at":"2021-08-23T08:26:46.227Z"},"user":{"_id":"61235bc4d0dd5339b7ff5600","isBlocked":true,"isDeleted":true,"education":"Clg_Graduate","maretialStatus":"Widowed","gender":"Female","city":"East Jordanmouth","userType":"Normal","password":"$2b$10$YPCcVpX4Osj0UQ5SsqAfAe5lubzCxKn0u.QkWbsg/8OHS.FJuEqNS","email":"[email protected]","lname":"Daniel","fname":"Marcelino","name":"Fae Ullrich","favAnimal":"Rabit","__v":0,"created_at":"2021-08-23T08:26:44.945Z","updated_at":"2021-08-23T08:26:44.945Z"},"created_at":"2021-08-26T07:59:45.439Z","updated_at":"2021-08-26T07:59:45.439Z","__v":0}}]
These are my models
class WinnerByMonth: Codable {
// let id: String
let max: Int
let answer: Answer
enum CodingKeys: String, CodingKey {
// case id = "_id"
case max, answer
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.max = try container.decodeIfPresent(Int.self, forKey: .max) ?? 0
self.answer = try container.decodeIfPresent(Answer.self, forKey: .answer) ?? Answer()
}
}
class Answer: Codable {
let id: String
let wordCount: Int
let isDeleted: Bool
let user: User
let quiz: Quiz
let text: String
let createdAt, updatedAt: String
enum CodingKeys: String, CodingKey {
case id = "_id"
case wordCount, isDeleted, user, quiz, text
case createdAt = "created_at"
case updatedAt = "updated_at"
}
init() {
self.id = ""
self.wordCount = 0
self.user = User()
self.isDeleted = false
self.quiz = Quiz()
self.text = ""
self.createdAt = ""
self.updatedAt = ""
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(String.self, forKey: .id) ?? ""
self.wordCount = try container.decodeIfPresent(Int.self, forKey: .wordCount) ?? 0
self.user = try container.decodeIfPresent(User.self, forKey: .user) ?? User()
self.isDeleted = try container.decodeIfPresent(Bool.self, forKey: .isDeleted) ?? false
self.quiz = try container.decodeIfPresent(Quiz.self, forKey: .quiz) ?? Quiz()
self.text = try container.decodeIfPresent(String.self, forKey: .text) ?? ""
self.createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) ?? ""
self.updatedAt = try container.decodeIfPresent(String.self, forKey: .updatedAt) ?? ""
}
}
class Quiz: Codable {
let scheduledAt: String
let timeUsed: Int
let type: String
let isDeleted: Bool
let id: String
let category: Category
let name: String
let createdAt, updatedAt: String
enum CodingKeys: String, CodingKey {
case scheduledAt = "scheduled_at"
case timeUsed, type, isDeleted
case id = "_id"
case category, name
case createdAt = "created_at"
case updatedAt = "updated_at"
}
init() {
self.scheduledAt = ""
self.timeUsed = 0
self.type = ""
self.isDeleted = false
self.id = ""
self.category = Category()
self.name = ""
self.createdAt = ""
self.updatedAt = ""
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.scheduledAt = try container.decodeIfPresent(String.self, forKey: .scheduledAt) ?? ""
self.timeUsed = try container.decodeIfPresent(Int.self, forKey: .timeUsed) ?? 0
self.type = try container.decodeIfPresent(String.self, forKey: .type) ?? ""
self.isDeleted = try container.decodeIfPresent(Bool.self, forKey: .isDeleted) ?? false
self.id = try container.decodeIfPresent(String.self, forKey: .id) ?? ""
self.category = try container.decodeIfPresent(Category.self, forKey: .category) ?? Category()
self.name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
self.createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) ?? ""
self.updatedAt = try container.decodeIfPresent(String.self, forKey: .updatedAt) ?? ""
}
}
class User: Codable {
var _id: String = ""
var fname: String = ""
var lname: String = ""
var profilePic: String = ""
var city: String = ""
var gender: String = ""
var ageGroup: String = ""
var martialStatus: String = ""
var favAnimal: String = ""
var education: String = ""
var isProfileCompleted: Bool = false
var charity: Charity = Charity()
enum CodingKeys: String, CodingKey {
case _id
case fname
case lname
case profilePic
case city
case gender
case ageGroup = "age"
case martialStatus = "maretialStatus"
case favAnimal
case education
case isProfileCompleted
}
init() {
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.fname = try container.decodeIfPresent(String.self, forKey: .fname) ?? ""
self.lname = try container.decodeIfPresent(String.self, forKey: .lname) ?? ""
self._id = try container.decodeIfPresent(String.self, forKey: ._id) ?? ""
self.city = try container.decodeIfPresent(String.self, forKey: .city) ?? ""
self.gender = try container.decodeIfPresent(String.self, forKey: .gender) ?? ""
self.ageGroup = try container.decodeIfPresent(String.self, forKey: .ageGroup) ?? ""
self.martialStatus = try container.decodeIfPresent(String.self, forKey: .martialStatus) ?? ""
self.profilePic = try container.decodeIfPresent(String.self, forKey: .profilePic) ?? ""
self.favAnimal = try container.decodeIfPresent(String.self, forKey: .favAnimal) ?? ""
self.education = try container.decodeIfPresent(String.self, forKey: .education) ?? ""
self.isProfileCompleted = try container.decodeIfPresent(Bool.self, forKey: .isProfileCompleted) ?? false
}
}
Here is how I am parsing
func execute<T: Codable>(endpoint: Endpoint, completion: ((T?, _ E: NetworkStatus.Code) -> Void)?) {
let session = RequestHandler.alamofireManager
session.request(endpoint.path, method: endpoint.method, parameters: endpoint.params,
encoding: endpoint.encoding, headers: nil,
interceptor: self).validate().response { (response) in
if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
print("Data Path: \(endpoint.path)")
print("Data: \(utf8Text)")
}
switch response.result {
case .success( _):
do {
let result = try JSONDecoder().decode(T.self, from: response.data!)
completion?(result, NetworkStatus.Code.success)
} catch {
print(error)
completion?(nil, NetworkStatus.Code.unParsable)
}
case .failure(let error):
if error.responseCode == 401 {
App.isLoggedIn = false
completion?(nil, NetworkStatus.Code.unauthorized)
} else {
completion?(nil, NetworkStatus.Code.notFound)
}
}
}
}
Here is how I am calling the function
static func getWinnersByMonth(year: Int, month: Int, completion: @escaping (_ result: [WinnerByMonth]?, _ error: Any?) -> Void) {
let handler = RequestHandler()
handler.execute(endpoint: LeaderBoardManger.GetWinnersByMonth(year: year, month: month), completion: completion)
}
The error clearly saying that the category
property of the quiz
of the first Answer
inside the json contains and string ("category":"61235bc5d0dd5339b7ff563b"
), but you decleared it as a Category
:
...
let category: Category // should be String
...
So the following ling is throwing that error:
...
self.category = try container.decodeIfPresent(Category.self, forKey: .category) ?? Category() // it exist, but it's not a Category. it is a String
...