Let's start with the question. You said you have a problem with parsing JSON. That means, very likely I wouldn't need to care about you view. I'd only need to care about the code that actually parse the JSON. So you can actually strip the entire view controller, search bar, and all the UI shenanigans. You parse the JSON file in downloadJSON (easy enough), so you can separate that function into a new console project and just call it. So your question can become much smaller (and more attractive to the readers):
import Foundation
struct GalleryData: Codable {
let data: ListingData
}
struct ListingData: Codable {
let children: [Child]
}
struct Child: Codable {
let data: ChildData
}
struct ChildData: Codable {
let subreddit, author, title: String
let thumbnail, url_overridden_by_dest: String
}
func downloadJSON() {
let baseURL = "https://www.reddit.com/r/"
let url = URL(string: "\(baseURL)cat/top.json")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
do {
let array = try JSONDecoder().decode([GalleryData].self, from: data!)
print("Got data:", array)
} catch {
print("Your TableView did not load data")
}
}.resume()
}
downloadJSON()
// Prevent the program from terminating before the URLSession finishes
dispatchMain()
Now then anyone who can create an empty console Xcode project and run the (33-line) code and understand the (same) question sans the distraction.
Note that we use dispatchMain at the end. So this program won't terminate by itself, you'll need to make sure to kill it (ctrl+c) if you run the program outside of the Xcode environment.
First, note that catch is for catching errors that may arise. Since you fail the decoding process, it'll likely throw something from there. It is usually very useful to read the error for debugging purposes. So if you replace
print("Your TableView did not load data")
with
print("Data loading fails with error:", error)
It'll now print:
Data loading fails with error: typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))
It's a little verbose, but also good since it's packing a lot of information.
The error seems to be type-mismatch on coding path [] (i.e. top-level) with a handy description:
Expected to decode Array<Any> but found a dictionary instead.
Ok, so, at the top-level, the decoder is trying to decode an array, but found JSON dictionary. If you look closely at the JSON file, that is indeed the case. The top-level object is not an array of GalleryData, but a single GalleryData containing multiple Child.
So instead you need to do:
JSONDecoder().decode(GalleryData.self, from: data!)
and you should now get the data.
I notice this key: url_overridden_by_dest 

. That's no good. It's soooo unSwifty. Good thing that JSON decoder (and encoder) has an option to handle snake-case for you. So you can instead do this:
struct ChildData: Codable {
let subreddit, author, title: String
let thumbnail: String
// Not snake case anymore!
let urlOverriddenByDest: String
}
//
// When decoding:
//
let decoder = JSONDecoder()
// Let the decoder know we're dealing with snake case
decoder.keyDecodingStrategy = .convertFromSnakeCase
// The rest is the same
let data = try decoder.decode(GalleryData.self, from: data!)
let array = data.data.children
print("Got data:", array)