I'm calling an API endpoint getChatMessages and decode the response as follows:
func getChatMessages(conversation: Int, pagesize: Int = 10, page: Int = 1, completion: @escaping(Result<ConversationDetails, APIError>) -> Void) {
{...some networking - configure request and start dataTask}
do {
let dateFormatter: DateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)
decoder.keyDecodingStrategy = .custom { codingKeys in
let lastKey = codingKeys.last!
if lastKey.intValue != nil || codingKeys.count != 2 { return lastKey }
if lastKey.stringValue == "count" || lastKey.stringValue == "conversation_participants" { return lastKey }
return AnyCodingKey(stringValue: "element")!
}
let result = try decoder.decode(ResponseMultipleElements<ConversationDetails>.self, from: data!)
completion(.success(result.detailresponse.element))
}catch {
print("Error is: ", error)
completion(.failure(.decodingError))
}
}
dataTask.resume()
}
This is ResponseMultipleElements structure
struct ResponseMultipleElements<T1: Decodable>: Decodable {
let statuscode: Int
let response_type: Int
let errormessage: String?
let detailresponse: Element<T1>
}
struct Element<T2: Decodable>: Decodable {
let count: Int
let element: T2
let conversation_participants: [ConversationParticipants]?
}
struct AnyCodingKey: CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int? { return nil }
init?(intValue: Int) {
return nil
}
}
And finally, the structure of a ConversationDetails object, as returned by my getChatMessages function
class ConversationDetails: Codable {
var messages: [ChatMessage]?
var conversation_participants: [ConversationParticipants]?
}
Here's how the response from the getChatMessages API call looks like:
PROBLEM: I'm getting typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "detailresponse", intValue: nil), CodingKeys(stringValue: "element", intValue: nil)], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))
Went over everything numerous times now but can't find the error. I'm not sure for which element it finds an array instead of the expected <String, Any> so don't know what to fix. Hoping someone can help.
Your keyDecodingStrategy
tells the decoder to treat all the string keys that have exactly one parent, that is not count
or conversation_participants
, as "element". There is only one such key in your JSON that satisfy this condition - messages
, under detailresponse
.
The messages
key would be decoded by:
let element: T2
where T2
is:
class ConversationDetails: Codable {
var messages: [ChatMessage]?
var conversation_participants: [ConversationParticipants]?
}
But messages
is actually an array in the JSON!
What I suggest you do is to decode a ResponseMultipleElements<[ChatMessage]?>
. This way, T2
is actually the expected array type.
You should add an initialiser to ConversationDetails
to allow you to create a ConversationDetails
from an array of chat messages and participants,
init(messages: [ChatMessage]?, conversation_participants: [ConversationParticipants]?) {
self.messages = messages
self.conversation_participants = conversation_participants
}
and use it in getChatMessages
:
let result = try decoder.decode(ResponseMultipleElements<[ChatMessage]?>.self, from: data!)
let convoDetails = ConversationDetails(
messages: result.detailresponse.element,
conversation_participants: result.detailresponse.conversation_participants
)
completion(.success(convoDetails))