[英]Combine equivalent of RxSwift's bind(to:)
我是一位經驗豐富的 RxSwift 用戶,並且在 RxSwift 中有一個運行良好的 MVVM 結構。 我是 Combine 的新手,但看在上帝的份上,我想不出如何在 Combine 中做類似的事情。 最大的阻礙是 Combine 中的bind(to:)
等價物。 我不知道如何將 1 個變量的結果鏈接到另一個變量。
這是我在 RxSwift 中會做的事情:
protocol UseCase {
func execute(id: Int) -> Single<CustomClass>
}
class DefaultUseCase: UseCase {
func execute(id: Int) -> Single<CustomClass> {
// Do network call and return in Single format
}
}
class ViewModel {
struct Input {
let load = PublishRelay<Void>()
}
struct Output {
let isButtonEnabled: Driver<Bool>
}
let disposeBag = DisposeBag()
let input = Input()
let output: Output
init(id: Int, useCase: UseCase = DefaultUseCase()) {
let isButtonEnabled = BehaviorRelay<Bool>(value: false)
let action = Action<Void, CustomClass> { id in
return useCase.execute(id: id)
}
self.output = Output(isButtonEnabled: isButtonEnabled.asDriver())
input
.load
.bind(to: useCase.inputs)
.disposed(by: disposeBag)
action
.elements
.map { // map CustomClass to Bool }
.bind(to: isButtonEnabled)
.disposed(by: disposeBag)
}
}
動作 class 來自這個框架: https://github.com/RxSwiftCommunity/Action
我不知道如何在 Combine 中做類似的事情,我已經閱讀了一些教程,但這對我來說沒有意義。 看起來您需要感覺像是一千個變量才能使用 viewModel 將 1 個值傳遞給您的視圖/viewController。
我正在尋找一段與上面的 RxSwift 代碼完全相同的代碼,但結合了一些解釋。
首先,讓我們簡化您的視圖模型:
class ViewModel {
struct Input {
let load = PublishRelay<Void>()
}
struct Output {
let isButtonEnabled: Driver<Bool>
}
let input = Input()
let output: Output
init(id: Int, useCase: UseCase = DefaultUseCase()) {
let isButtonEnabled = input.load
.flatMapLatest { [useCase] in
useCase.execute(id: id)
.map { _ in /* map CustomClass to Bool */ true }
.catchAndReturn(false)
}
.asDriver(onErrorRecover: { _ in fatalError() })
self.output = Output(isButtonEnabled: isButtonEnabled)
}
}
我不是您的Input
結構的粉絲,但我正在使用它...
一旦你這樣做,很容易看到如何翻譯它:
class ViewModelʹ {
struct Input {
let load = PassthroughSubject<Void, Never>()
}
struct Output {
let isButtonEnabled: AnyPublisher<Bool, Never>
}
let input = Input()
let output: Output
init(id: Int, useCase: UseCase) {
let isButtonEnabled = input.load
.map { [useCase] in
useCase.execute(id: id)
.map { _ in /* map CustomClass to Bool */ true }
.catch { _ in Just(false) }
}
.switchToLatest()
.eraseToAnyPublisher()
self.output = Output(isButtonEnabled: isButtonEnabled)
}
}
更新回應評論
以下是您將如何使用多個輸出的用例響應(在 iOS 13 中編譯):
class ViewModelʹ {
struct Input {
let load = PassthroughSubject<Void, Never>()
}
struct Output {
let isButtonEnabled: AnyPublisher<Bool, Never>
let somethingElse: AnyPublisher<String, Never>
}
let input = Input()
let output: Output
init(id: Int, useCase: UseCase) {
let result = input.load
.map { [useCase] in
useCase.execute(id: id)
.catch { _ in Empty() }
}
.switchToLatest()
.share()
let isButtonEnabled = result
.map { _ in /* map CustomClass to Bool */ true }
.eraseToAnyPublisher()
let somethingElse = result
.map { _ in /* map CustomClass to String */ "" }
.eraseToAnyPublisher()
self.output = Output(
isButtonEnabled: isButtonEnabled,
somethingElse: somethingElse
)
}
}
當然,很大程度上取決於您希望如何處理錯誤。 上面的內容吞下了它們,但您可能希望將它們暴露給另一個輸出。
所有這些都變成了一般教程,而不是回答問題。
extension Publisher {
func bind<T: Subject>(to subject: T) -> AnyCancellable
where T.Output == Self.Output,
T.Failure == Self.Failure {
self.subscribe(subject)
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.