I'm implementing an API Client that will call my backend API, and return the appropriate object, or an error.
This is what I have so far:
public typealias JSON = [String: Any]
public typealias HTTPHeaders = [String: String]
public enum RequestMethod: String {
case get = "GET"
case post = "POST"
case put = "PUT"
case delete = "DELETE"
}
public class APIClient {
public func sendRequest(_ url: String,
method: RequestMethod,
headers: HTTPHeaders? = nil,
body: JSON? = nil,
completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) {
let url = URL(string: url)!
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = method.rawValue
if let headers = headers {
urlRequest.allHTTPHeaderFields = headers
urlRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
}
if let body = body {
urlRequest.httpBody = try? JSONSerialization.data(withJSONObject: body)
}
let session = URLSession(configuration: .default)
let task = session.dataTask(with: urlRequest) { data, response, error in
completionHandler(data, response, error)
}
task.resume()
}
}
Ok, so what I want to be able to do is something like this:
apiClient.sendRequest("http://example.com/users/1", ".get") { response in
switch response {
case .success(let user):
print("\(user.firstName)")
case .failure(let error):
print(error)
}
}
apiClient.sendRequest("http://example.com/courses", ".get") { response in
switch response {
case .success(let courses):
for course in courses {
print("\(course.description")
}
case .failure(let error):
print(error)
}
}
So, the apiClient.sendRequest() method has to decode the response json into the desired swift object, and return either that object or an error object.
I have these structs:
struct User: Codable {
var id: Int
var firstName: String
var lastName: String
var email: String
var picture: String
}
struct Course: Codable {
var id: Int
var name: String
var description: String
var price: Double
}
I have this Result enum defined as well:
public enum Result<Value> {
case success(Value)
case failure(Error)
}
Where I am stuck is, I am not sure how to tweak my completionHandler in sendRequest() so that I can use it with a User object or a Course object or any other custom object. I know I have to use generics somehow to make this happen, and I've used generics in C#, but I'm not quite comfortable yet in Swift 4, so any help is appreciated.
EDIT: Also, I'd like to know how sendRequest()'s response can be bubbled back up one level to the calling code in the ViewController, so that the ViewController has access to the success and failure results (in an async fashion).