![](/img/trans.png)
[英]How can have an instance or a return type of a generic type conforming to a protocol with associated value
[英]How can I return a generic type that implements another protocol
我有一個網絡模塊,它實現了一個標准接口來公開我的網絡客戶端
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)
}
}
我有另一個框架要添加到我的應用程序中,它公開了一個相同的界面。
我不希望我的其他模塊依賴於這個網絡模塊,而是希望它公開它所需的接口並讓我的網絡模塊依賴它。
所以下面的接口被我的另一個模塊暴露了
protocol AuthHTTPClientTask {
func cancel()
}
protocol AuthHTTPClient {
@discardableResult
func execute(_ request: URLRequest, _ completion: @escaping (Result<(response: HTTPURLResponse, data: Data), Error>) -> Void) -> AuthHTTPClientTask
}
如您所見,接口是精確的,但是我希望避免僅為該接口創建整個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
的錯誤,因為出於一致性目的需要約束,在您的示例中,編譯器認為它是 inheritance。
您可以創建一個通用的私有方法來處理分派請求並擴展該方法的返回類型以符合您的模塊類型。
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)
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.