簡體   English   中英

如何返回實現另一個協議的泛型類型

[英]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實現的某種類型,允許它返回AuthHTTPClientTaskHTTPClientTask

我以為我可以做類似的事情

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM