In this episode we take the response from the top podcasts feed and decode the JSON into models using Codable.
Episode Links iTunes RSS Feed Generator 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)) { //... } }