I get Reddit post comments from Reddit API (for one post from specific subreddit) in JSON, then parse JSON via Structs. When I try to output decoded comments I get an error:
Error decoding Json comments - typeMismatch(Swift.Dictionary<Swift.String, Any>,
Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))
Maybe I missed something in my structs or mismatched types in Repository getComments
method. Please advise.
enum RequestURL {
case top(sub: String, limit: Int)
case postAt(sub: String, id: String)
var url: String {
switch self {
case .top(let sub, let limit):
return "https://www.reddit.com/r/\(sub)/top.json?limit=\(limit)"
case .postAt(let sub, let id):
return "https://www.reddit.com/r/\(sub)/comments/\(id).json"
}
}
}
class HTTPRequester {
init() {}
func getData (url: RequestURL, completion: @escaping(Data?) -> Void) {
guard let url = URL(string: url.url) else {
print("Error: Request URL is nil!")
completion(nil)
return
}
URLSession.shared.dataTask(with: url) {data,_,error in
guard let jsonData = data else {
print(error ?? "Error")
completion(nil)
return
}
completion(jsonData)
}.resume()
}
}
class Service {
init() {}
func decodeJSONComments(url: RequestURL, completion: (@escaping (_ data: CommentListing?) -> Void)) {
HTTPRequester().getData(url: url) { jsonData in
do {
let postsResponse = try JSONDecoder().decode(CommentListing.self, from: jsonData!)
print(postsResponse)
completion(postsResponse)
} catch {
print("Error decoding Json comments - \(error)")
completion(nil)
}
}
}
}
class Repository {
init() {}
func getComments(sub: String, postId: String, completion: (@escaping ([RedditComment]) -> Void)) {
Service().decodeJSONComments(url: RequestURL.postAt(sub: sub, id: postId)) { (comments: CommentListing?) in
var commentsList = [CommentData]()
commentsList = (comments?.data.children) ?? []
let mappedComs = commentsList.map { (comment) -> RedditComment in
return RedditComment(
id: comment.data.id,
author: comment.data.author,
score: comment.data.score,
body: comment.data.body)
}
completion(mappedComs)
}
}
}
class UseCase {
func createComments(sub: String, postId: String, completion: (@escaping (_ data: [RedditComment]) -> Void)) {
Repository().getComments(sub: sub, postId: postId) { (comments: [RedditComment]) in
completion(comments)
}
}
}
UseCase().createComments(sub: "ios", postId: "4s4adt") { comments in
print(comments)
}
JSON structure
You mentioned that you have the following error:
typeMismatch(Swift.Dictionary<String, Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))
Let's deconstruct that:
Here are two helpful things you can read from that:
First, the debugDescription
: Expected to decode Dictionary<String, Any> but found an array instead.
This means that you are trying to decode a dictionary but the JSON contains an array. Note, that most normal types that you mark as Codable will encode to a dictionary.
Second, the codingPath
, which is in your case an empty array ([]
), meaning this issue is right at the root type that you are trying to decode.
Now let's have a look at the Postman response that you posted. Right in line 1, you can see that the outermost container (line 1) is an array.
But when you are decoding, you are decoding a CommentListing
, which uses a keyed container (dictionary).
So to fix this, you have to decode an array of CommentListing
s.
let postsResponse = try JSONDecoder().decode([CommentListing].self, from: jsonData!)