我有一个网络模块,它实现了一个标准接口(interface)来公开我的网络客户端
protocol HTTPClientTask {
func cancel()
}
protocol HTTPClient {
@discardableResult
func execute(_ request: URLRequest, _ completion: @escaping (Result<(response: HTTPURLResponse, data: Data), Error>) -> Void) -> HTTPClientTask
}
这是实现的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)
}
}
在操场上运行的一个例子是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)
}
}
我有另一个框架要添加到我的应用程序中,它公开了一个相同的界面。我不希望我的其他模块依赖于这个网络模块,而是希望它公开它所需的接口(interface)并让我的网络模块依赖它。
所以下面的接口(interface)被我的另一个模块暴露了
protocol AuthHTTPClientTask {
func cancel()
}
protocol AuthHTTPClient {
@discardableResult
func execute(_ request: URLRequest, _ completion: @escaping (Result<(response: HTTPURLResponse, data: Data), Error>) -> Void) -> AuthHTTPClientTask
}
如您所见,接口(interface)是精确的,但我希望避免创建整个 URLSessionHTTPClient
只针对那个界面 - URLSessionAuthHTTPClient
或行为等是相同的。是否可以创建某种类型
URLSessionHTTPClient
允许它返回 AuthHTTPClientTask
的实现或 HTTPClientTask
?我以为我可以做类似的事情
extension HTTPClientTask: AuthHTTPClientTask { }
final class URLSessionHTTPClient: HTTPClient, AuthHTTPClient {
private let session: URLSession
private struct RequestError: Error {
.........
但这会产生 Extension of protocol 'HTTPClientTask' cannot have an inheritance clause
和 Type 'URLSessionHTTPClient' does not conform to protocol 'AuthHTTPClient'
最佳答案
您收到有关 inheritance clause
的错误由于出于一致性目的需要约束,因此在您的示例中,编译器认为它是继承。
您可以创建一个通用的私有(private)方法来处理分派(dispatch)请求并扩展该方法的返回类型以符合您的模块类型。
protocol HTTPClientTask {
func cancel()
}
protocol HTTPClient {
@discardableResult
func execute(_ request: URLRequest, _ completion: @escaping (Result<(response: HTTPURLResponse, data: Data), Error>) -> Void) -> HTTPClientTask
}
protocol AuthHTTPClientTask {
func cancel()
}
protocol AuthHTTPClient {
@discardableResult
func execute(_ request: URLRequest, _ completion: @escaping (Result<(response: HTTPURLResponse, data: Data), Error>) -> Void) -> AuthHTTPClientTask
}
private struct URLSessionTaskWrapper {
let wrapped: URLSessionTask
func cancel() {
wrapped.cancel()
}
}
extension URLSessionTaskWrapper: HTTPClientTask { }
extension URLSessionTaskWrapper: AuthHTTPClientTask { }
final class URLSessionHTTPClient {
private let session: URLSession
private struct RequestError: Error { }
init(session: URLSession = .shared) {
self.session = session
}
private func dispatch(_ request: URLRequest, _ completion: @escaping (Result<(response: HTTPURLResponse, data: Data), Error>) -> Void) -> URLSessionTaskWrapper {
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)
}
}
extension URLSessionHTTPClient: HTTPClient {
func execute(_ request: URLRequest, _ completion: @escaping (Result<(response: HTTPURLResponse, data: Data), Error>) -> Void) -> HTTPClientTask {
return dispatch(request, completion)
}
}
extension URLSessionHTTPClient: AuthHTTPClient {
func execute(_ request: URLRequest, _ completion: @escaping (Result<(response: HTTPURLResponse, data: Data), Error>) -> Void) -> AuthHTTPClientTask {
return dispatch(request, completion)
}
}
let requestURL = URL(string: "https://jsonplaceholder.typicode.com/todos/1")!
let httpClient: HTTPClient = URLSessionHTTPClient()
httpClient.execute(.init(url: requestURL)) { result in
if let code = try? result.get().response.statusCode {
print("HTTP", code)
}
}
let authzHTTPClient: AuthHTTPClient = URLSessionHTTPClient()
authzHTTPClient.execute(.init(url: requestURL)) { result in
if let code = try? result.get().response.statusCode {
print("Authz", code)
}
}
关于ios - 如何返回实现另一个协议(protocol)的泛型类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62703484/