I have networking module that implements a standard interface for exposing my networking client
protocol HTTPClientTask {
func cancel()
}
protocol HTTPClient {
@discardableResult
func execute(_ request: URLRequest, _ completion: @escaping (Result<(response: HTTPURLResponse, data: Data), Error>) -> Void) -> HTTPClientTask
}
This is implemented something like
final class URLSessionHTTPClient: HTTPClient {
private let session: URLSession
private struct RequestError: Error { }
private struct URLSessionTaskWrapper: HTTPClientTask {
let wrapped: URLSessionTask
func cancel() {
wrapped.cancel()
}
}
init(session: URLSession = .shared) {
self.session = session
}
func execute(_ request: URLRequest, _ completion: @escaping (Result<(response: HTTPURLResponse, data: Data), Error>) -> Void) -> HTTPClientTask {
let task = session.dataTask(with: request) { data, response, error in
completion(Result {
if let error = error {
throw error
} else if let data = data, let response = response as? HTTPURLResponse {
return (response, data)
} else {
throw RequestError()
}
})
}
task.resume()
return URLSessionTaskWrapper(wrapped: task)
}
}
An example to run in a playground would be
let requestURL = URL(string: "https://jsonplaceholder.typicode.com/todos/1")!
let httpClient = URLSessionHTTPClient()
httpClient.execute(.init(url: requestURL)) { result in
if let code = try? result.get().response.statusCode {
print(code)
}
}
I have another framework I'd like to add to my app that exposes an interface that is identical.
I don't want my other module to have a dependancy on this networking module, instead I'd prefer it to expose the interface it requires and have my networking module be dependant on it.
So the following interface is exposed by my other module
protocol AuthHTTPClientTask {
func cancel()
}
protocol AuthHTTPClient {
@discardableResult
func execute(_ request: URLRequest, _ completion: @escaping (Result<(response: HTTPURLResponse, data: Data), Error>) -> Void) -> AuthHTTPClientTask
}
As you can see the interfaces are exact, however I was hoping to avoid creating an entire URLSessionHTTPClient just for that interface - URLSessionAuthHTTPClient or something as the behaviour etc is the same.
Is it possible to create some sort of type that URLSessionHTTPClient implements that allows it to return AuthHTTPClientTask OR HTTPClientTask?
I thought I could do something like
extension HTTPClientTask: AuthHTTPClientTask { }
final class URLSessionHTTPClient: HTTPClient, AuthHTTPClient {
private let session: URLSession
private struct RequestError: Error {
.........
But this produces Extension of protocol 'HTTPClientTask' cannot have an inheritance clause and Type 'URLSessionHTTPClient' does not conform to protocol 'AuthHTTPClient'