在我的视图控制器中,我有一个属性items
,我订阅并呈现了我的视图。
对于这个视图控制器,我有一个视图模型,其中我有加载方法,如:
@Published private(set) var items: [Item] = []
func load(params: [String: Any] = [:]) {
self.isLoading = true
self.subscription = WebRepo().index(params: params).sink(receiveCompletion: { [weak self] (completion) in
switch completion {
case .failure(let error):
print("Error is: (error.localizedDescription)")
default: break
}
self?.isLoading = false
}, receiveValue: { [weak self] (response) in
self?.items = response.data
})
}
我的WebRepo
看起来像:
final class WebRepo {
func index(params: [String: Any]) -> AnyPublisher<MyResponse<[Item]>, Error> {
let url = URL(...)
return AF.request(url, method: .get, parameters: params)
.publishDecodable(type: MyResponse<[Item]>.self)
.tryCompactMap { (response) -> MyResponse<[Item]>? in
if let error = response.error { throw error }
return response.value
}
.eraseToAnyPublisher()
}
}
我的用户可以多次加载,正如您所看到的,每次调用load
方法时,它都会订阅,我认为不应该是这样的。
我试图为我的视图模型介绍属性:
private var indexResponse: AnyPublisher<MyResponse<[Item]>, Error>?
//And my load becomes
func load(params: [String: Any] = [:]) {
self.isLoading = true
self.indexResponse = WebRepo().index(params: params)
}
但在这种情况下,我不能进行初始绑定,因为初始值是nil
,因此它不会订阅。
另一个问题是关于处理加载错误,我是否需要在视图模型中具有error
的属性,或者有没有方法可以重新引发$items
的错误?
您可以在init
中设置单个订阅,PassthroughSubject
作为触发器,.send
在load
:中设置
private var cancellables: Set<AnyCancellable> = []
private let loadTrigger = PassthroughSubject<[String: Any], Never>()
init(...) {
loadTrigger
//.debounce(...) // possibly debounce load requests
.flatMap { params in
WebRepo().index(params: params)
}
.sink(
receiveCompletion: {/* ... */},
receiveValue: {/* ... */})
.store(in: &cancellables)
}
func load(params: [String: Any] = [:]) {
loadTrigger.send(params)
}
我不能告诉你如何处理这个错误,因为它是非常具体的,可能是主观的,例如,一个错误应该终止订阅吗?你打算处理可能出现的多个错误吗?
如果您希望永远不会失败,但仍然将错误作为副作用处理,则可以返回Result
,它将捕获值或错误。这样,管道可能具有Never
的故障类型,因此它永远不会因错误而终止。然后打开.sink
:中的结果
loadTrigger
.flatMap { params -> AnyPublisher<Result<MyResponse<[Item]>, Error>, Never> in
WebRepo().index(params: params)
.map { .success($0) }
.catch { Just(.failure($0)) }
.eraseToAnyPublisher()
}
.sink { [weak self] result in
switch result {
case success(let response):
self?.items = response.data
case failure(let err):
// do something with the error
}
}