防止处理 PublishSubject (RxSwift)

Posted

技术标签:

【中文标题】防止处理 PublishSubject (RxSwift)【英文标题】:Prevent disposal of PublishSubject (RxSwift) 【发布时间】:2018-05-29 12:22:36 【问题描述】:

我正在为结合 RxSwift 的 PublishSubject 的特定用例而苦苦挣扎。

为了简单起见,省略了不重要的细节。

有一个 MVVM 设置。在 VC 中,我有一个 UIButton,点击它应该调度网络调用。在 ViewModel 中,我有一个 buttonDidTapSubject: PublishSubject<Void>

class ViewModel 
  let disposeBag = DisposeBag()
  let buttonDidTapSubject = PublishSubject<Void>()
  let service: Service
  typealias Credentials = (String, String)
  var credentials: Observable<Credentials> 
   return Observable.just(("testEmail", "testPassword"))
  
  init(_ service: Service) 
   self.service = service
   buttonDidTapSubject
    .withLatestFrom(credentials)
    .flatMap(service.login) // login method has signature func login(_ creds: Credentials) -> Observable<User>
    .subscribe(onNext:  user in print("Logged in \(user)") ,
               onError:  error in print("Received error") )
    .disposed(by: disposeBag)
  


class ViewController: UIViewController 
  let viewModel: ViewModel
  let button = UIButton()
  init(_ viewModel: ViewModel)  
    self.viewModel = viewModel
  

在控制器的viewDidLoad 中我进行了绑定:

override func viewDidLoad() 
  button.rx.tap.asObservable()
    .subscribe(viewModel.buttonDidTapSubject)
    .disposed(by: disposeBag)

问题是,由于网络请求可能会失败并且从login(_:) 方法返回的Observable 会产生错误,因此ViewModel 中对buttonDidTapSubject 的整个订阅将被释放。按钮上的所有其他点击都不会触发登录 ViewModel 的序列。

有什么办法可以避免这种行为吗?

【问题讨论】:

看看github.com/RxSwiftCommunity/Action 绑定按钮时非常有用,并且有很好的功能(比如自动处理启用状态) 【参考方案1】:

您可以使用retry 来防止完成订阅。如果您只想在特定情况或错误中重试,也可以使用retryWhen 运算符

在视图模型中:

lazy var retrySubject: Observable<Void> = 
    return viewModel.buttonDidTapSubject
            .retryWhen  error in 
                if (error == .networkError) //check here your error
                    return .just(Void())
                 else 
                    return .never() // Do not retry
                
            
()

在视图控制器中我会以另一种方式完成:

override func viewDidLoad() 
  super.viewDidLoad()
  button.rx.tap.asObservable()
    .flatMap  [weak self] _ in
      return self?.viewModel.retrySubject
    
    .subscribe(onNext: 
        //do whatever
    )
    .disposed(by: disposeBag)
  

【讨论】:

【参考方案2】:

不确定是否仍然相关 - 使用 PublishRelay(虽然它是 RxCocoa)

【讨论】:

以上是关于防止处理 PublishSubject (RxSwift)的主要内容,如果未能解决你的问题,请参考以下文章

如何在按钮点击时观察 PublishSubject

RxSwift PublishSubject 单元测试

Debounce 使 PublishSubject 只发出一项

RxSwift - PublishSubject - 忽略错误并继续订阅(不要处置)

PublishSubject 与 Kotlin 协程(流/通道)

RxJava - 即使调用 onNext() 如何阻止 PublishSubject 发布