我是一位经验丰富的 RxSwift 用户,并且在 RxSwift 中拥有良好的 MVVM 结构。我是结合的新手,但看在上帝的份上,我不知道如何在结合中做类似的事情。最大的阻碍是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)
}
}
动作类来自此框架:https://github.com/RxSwiftCommunity/Action
我不知道如何在合并中做类似的事情,我已经阅读了一些教程,但这对我来说没有意义。看起来你需要一千个变量才能通过 viewModel 将 1 个值传递到你的 view/viewController 中。
我正在寻找一段与上面的 RxSwift 代码完全相同的代码,但结合了一些解释。
我注意到我没有回答关于
bind(to:)
在合并中是什么样子的明确问题。这是实现:
extension Publisher {
func bind<S>(to value: S) -> Cancellable where S: Subject, S.Output == Output, S.Failure == Never {
sink { completion in
if case let .failure(error) = completion {
assert(false, "Binding error \(error)")
}
value.send(completion: .finished)
} receiveValue: { output in
value.send(output)
}
}
}
首先,让我们简化您的视图模型:
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
)
}
}
当然,很大程度上取决于您想要如何处理错误。上面的内容吞掉了它们,但是您可能想将它们公开以获得另一个输出。
所有这些都变成了一般教程,而不是回答问题。