如何将此方法作为函数参数传递,而不是注入整个类



我创建了一个包含我的网络代码的项目。我还有另一个项目,其中包含一些与用户配置文件相关的服务和视图控制器。

我的网络层具有以下接口

public protocol HTTPClientTask {
func cancel()
}
public typealias HTTPClientResult = Result<(response: HTTPURLResponse, data: Data), Error>
public protocol HTTPClient {
/// The completion handler can be invoked in any thread.
/// Clients are responsible for dispatching to the appropriate thread, if needed.
@discardableResult
func execute(_ request: URLRequest, completion: @escaping (HTTPClientResult) -> Void) -> HTTPClientTask
}

我的混凝土类符合HTTPClient,目前我正在注入这种依赖注入。

但是,这需要我的配置文件模块知道我的网络模块。这会在我想删除的那些模块之间创建耦合。

由于HTTPClient只有一个返回任务的方法,我想我也许可以更新我的配置文件模块以接受函数签名而不是客户端。

这样,只有我的应用程序中的合成根才能知道我认为可以接受的两个模块。

我无法完全理解的是如何将HTTPClient接口表示为一个简单的函数。

我相信是:

typealias ClientResult = Result<(response: HTTPURLResponse, data: Data), Error>
typealias ClientMethod = (URLRequest, @escaping (ClientResult) -> Void) -> HTTPClientTask

但是我也不能代表HTTPClientTask属性。

这应该是一个对象,其上有一个返回 void 的方法。

本质上,我想将execute方法作为参数传递,但无法使用当前协议,无法确定如何将其表示为类型。

可以在下面找到如何实现此接口的示例:

public final class URLSessionHTTPClient: HTTPClient {
private let session: URLSession
private struct UnexpectedValuesRepresentationError: Error { }
private struct URLSessionTaskWrapper: HTTPClientTask {
let wrapped: URLSessionTask
func cancel() {
wrapped.cancel()
}
}
public init(session: URLSession = .shared) {
self.session = session
}
public func execute(_ request: URLRequest, completion: @escaping (HTTPClientResult) -> 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 UnexpectedValuesRepresentationError()
}
})
}
task.resume()
return URLSessionTaskWrapper(wrapped: task)
}
}

我在下面添加了一个游乐场,希望能更清楚。

import UIKit
public protocol HTTPClientTask {
func cancel()
}
public typealias HTTPClientResult = Result<(response: HTTPURLResponse, data: Data), Error>
public protocol HTTPClient {
/// The completion handler can be invoked in any thread.
/// Clients are responsible for dispatching to the appropriate thread, if needed.
@discardableResult
func execute(_ request: URLRequest, completion: @escaping (HTTPClientResult) -> Void) -> HTTPClientTask
}

public final class URLSessionHTTPClient: HTTPClient {
private let session: URLSession
private struct UnexpectedValuesRepresentationError: Error { }
private struct URLSessionTaskWrapper: HTTPClientTask {
let wrapped: URLSessionTask
func cancel() {
wrapped.cancel()
}
}
public init(session: URLSession = .shared) {
self.session = session
}
public func execute(_ request: URLRequest, completion: @escaping (HTTPClientResult) -> 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 UnexpectedValuesRepresentationError()
}
})
}
task.resume()
return URLSessionTaskWrapper(wrapped: task)
}
}
let client = URLSessionHTTPClient(session: .init(configuration: .ephemeral))
typealias ClientResult = Result<(response: HTTPURLResponse, data: Data), Error>
typealias ClientMethod = (URLRequest, @escaping (ClientResult) -> Void) -> HTTPClientTask // I want to remove the knowledge of `HTTPClientTask` from the typealias
class Loader {
private let load: ClientMethod
init(load: @escaping ClientMethod) {
self.load = load
}
func get() {
let url = URL(string: "https://google.com")!
load(.init(url: url)) { result in
print(result)
}
}
}

let loader = Loader(load: client.execute(_:completion:))
loader.get()

我相信URLSessionTaskWrapper如果不使用HTTPClientTask等抽象接口,可能很难表示这一点。

您也许可以返回URLSessionTask本身。

public protocol HTTPClient {
@discardableResult
func execute(_ request: URLRequest, completion: @escaping (HTTPClientResult) -> Void) -> URLSessionTask
}
...
typealias ClientMethod = (URLRequest, @escaping (ClientResult) -> Void) -> URLSessionTask 

最新更新