结合RxSwift的bind(to:)的等价物

问题描述 投票:0回答:1

我是一位经验丰富的 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 代码完全相同的代码,但结合了一些解释。

swift mvvm rx-swift combine
1个回答
2
投票

我注意到我没有回答关于

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
        )
    }
}

当然,很大程度上取决于您想要如何处理错误。上面的内容吞掉了它们,但是您可能想将它们公开以获得另一个输出

所有这些都变成了一般教程,而不是回答问题。

© www.soinside.com 2019 - 2024. All rights reserved.