
This video is only available to subscribers. Start a subscription today to get access to this and 484 other videos.
Creating an API Client
This episode is part of a series: Making a Podcast App From Scratch.
Episode Links
In this episode, we will create an API Client to fetch the top rated podcasts from iTunes. We will use this as suggestions on the search screen.
RSS Feed Generator
Apple provides RSS Feed Generator for top podcasts, where we can set the genre, result limit, format and options for allowing explicit content. This generates a Feed URL we can use to fetch the list.
Creating our API Client
We’ll start by creating a class: TopPodcastsAPI
.
class TopPodcastsAPI {
private let session: URLSession
private let baseURL = URL(string: "https://rss.itunes.apple.com/api/v1/us/podcasts/")!
init(session: URLSession = .shared) {
self.session = session
}
}
To pull the feed data we are initializing with URLSession
and baseURL
. Note that URLSession
is initialized as shared.
Creating Function to Fetch Podcasts
Start by defining the function fetchTopPodcasts
. Let’s define the limit
as 50 and allowExplicit
as false. It’s always good to have these parameters as configurable.
func fetchTopPodcasts(limit: Int = 50, allowExplicit: Bool = false, completion: @escaping (Result<Data, APIError>) -> Void) {
let explicit = allowExplicit ? "explicit" : "non-explicit"
let path = "top-podcasts/all/\(limit)/\(explicit).json"
let url = baseURL.appendingPathComponent(path)
let request = URLRequest(url: url)
perform(request: request, completion: completion)
}
Define the completion handler as @escaping
so the the provided block can capture the scope and return after the calling function is executed. Note that the completionHandler
is void. Swift 5's Result
type takes the Data
(success type) which represents the response obtained from the feed and Error
(failure type).
Create an enum to represent errors
Create an enum APIError
outside the class. It defines multiple types of HTTP errors.
enum APIError : Error {
case networkingError(Error)
case serverError // HTTP 5xx
case requestError(Int, String) // HTTP 4xx
case invalidResponse
}
We'll use these errors to represent the multiple possible failure paths for a network request.
Create a simple URLRequest by appending path and baseURL.
Creating a reusable method to perform network requests
This calls the session.dataTask method which will retrieve feed / data from URL request object and calls the completion handler.
let task = session.dataTask(with: request) { data, response, error in
if let error = error {
DispatchQueue.main.async {
completion(.failure(.networkingError(error)))
}
return
}
}
Note that completionHandler
is called on the main queue. We need to parse the response in JSON format which takes time, so we will do this on a background queue first, then jump over to the main queue to call the completionHandler.
Make sure to handle the other errors requestError
and serverError
using http.statusCode.
guard let http = response as? HTTPURLResponse, let data = data else {
DispatchQueue.main.async {
completion(.failure(.invalidResponse))
}
return
}
Verify HTTP status code of 200 and call the completion block with the data.
switch httpResponse.status {
case 200:
DispatchQueue.main.async {
completion(.success(data))
}
Don't forget to start the request by executing resume on the task:
task.resume()