swift - 如何将此方法作为函数参数传递,而不是注入整个类
问题描述
我创建了一个包含我的网络代码的项目。我有另一个项目,其中包含一些与用户配置文件相关的服务和视图控制器。
我的网络层有以下接口
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
推荐阅读
- html - react.js中的表格内表格
- angularjs - 在使用 ui-router 的单页应用程序中的模板 html 文件中指定 js 文件路径时出错
- css - 圆圈图案的背景
- python - 需要解释为什么我的代码会产生 ValueError
- python - 使用 browser.select_form() 的机械汤困难
- arrays - 将内存中的 []byte 转换为 [32]byte 而不复制数据
- go - 无法理解电子书中的并发示例
- angular - Angular 6 - 输入上的 *ngIf 条件是 add attr readonly
- java - 无法通过 Ajax SpringMVC 从表单中发布对象
- c++ - AES 加密/解密接收所有明文