
This video is only available to subscribers. Start a subscription today to get access to this and 484 other videos.
Parsing JSON into Models
This episode is part of a series: Making a Podcast App From Scratch.
Episode Links
Modeling the API Response
We’ll start by defining the response JSON structure as Decodable
structs.
extension TopPodcastsAPI {
struct Response: Decodable {
let feed: Feed
}
struct Feed: Decodable {
let results: [PodcastResult]
}
}
Note that Response
is Decodable
. This will allow us to decode the JSON that comes back from the server. Every type used in a Decodable
must implement Decodable
.
Create struct PodcastResult
with properties matching to JSON names obtained from the feed.
struct PodcastResult : Decodable {
let id: String
let artistName: String
let name: String
let artworkUrl100: String
let genres: [Genre]
}
struct Genre: Decodable {
let name: String
let genreId: String
}
Extracting a function to parse the response
Here we create a generic function that takes any Decodable
type T
and handles the parsing logic for us. Note that we are returning a closure this method (a function returning a function). Doing this allows us to compose the result of this function with the perform()
function.
private func parseDecodable<T : Decodable>(
completion: @escaping (Result<T, APIError>) -> Void
) -> (Result<Data, APIError>)
Change the completionHandler
to handle success and failure. Note the changes in completionHandler
, it takes Result<Data, APIError>
and processes callback on the main queue.
switch result {
case .success(let data):
do {
let jsonDecoder = JSONDecoder()
let object = try jsonDecoder.decode(T.self, from: data)
DispatchQueue.main.async {
completion(.success(object))
}
}
catch let decodingError as DecodingError {
DispatchQueue.main.async {
completion(.failure(.decodingError(decodingError)))
}
}
catch {
fatalError("Unhandled error raised.")
}
case .failure(let error):
DispatchQueue.main.async {
completion(.failure(error))
}
}
In case of success, use JSONDecoder
to decode the data. In case of failure, catch DecodingError
and any unhandled error.
Fetching Parsed JSON Response
To fetch the response, switch Result<Data, APIError>
to Result<Response, APIError>
. We call parseDecodable
through the completionBlock
of func perform
, this reduces nesting of closures.
func fetchTopPodcasts(limit: Int = 50, allowExplicit: Bool = false, completion: @escaping (Result<Response, APIError>) -> Void ) {
//... fetching parsed response
perform(request: request, completion: parseDecodable(completion: completion)) {
//...
}
}