ノート的なやつ

学んだことの備忘録です。ご指摘等お待ちしております。

RxSwift(MVVM)でのインディケーター表示状態の管理

自分は普段こうやっています的な紹介と悩み

インディケーターの状態管理について、自分はいつも以下のような形で管理しています。

enum LoadingStatus {
  case none
  case loading
  case dismiss
}

class XViewModel {
  
  private let disposeBag = DisposeBag()
  let loadingStatus: BehaviorRelay<LoadingStatus> = BehaviorRelay(value: .none)
  let isSuccess: PublishRelay<Bool> = PublishRelay()
  
  func fetchData() {
    AnySession
      .subscribe(onNext: { [weak self] _ in
        guard let self = self else { return }
        self.loadingStatus.accept(.dismiss)
      }, onError: { [weak self] _ in 
        self.loadingStatus.accept(.dismiss)
      })
      .disposed(by: disposeBag)
  }
}

class XViewController: UIViewController {
  
  let viewModel = XViewModel()
  private let disposeBag = DisposeBag()
  
  override func viewDidLoad() {
    super.viewDidLoad()
    setupObserver()
    
    viewModel.fetchData()
  }
  
  func setupObserver() {
    viewModel.loadingStatus
      .subscribe(onNext: { status in
        switch status {
        case .none:
          break
        case .loading:
          // インディケーターを出す処理
        case .dismiss:
          // インディケーターを消す処理
        }
      })
      .disposed(by: disposeBag)
  }
}

これ自体はHUDの表示非表示の状態管理が割と綺麗な形でできていると思います(個人的には)。 ただ、これだとエラーの時に数秒だけ表示させるエラー表示のインディケーター(HUD?)の管理が同時にできないので悩んでいます。 LoadingStatusを

enum LoadingStatus {
  case none
  case loading
  case error(message: String)
  case dismiss
}

みたいな形にすることも検討したのですが、運用する時に

// ViewModel
func fetchData() {
    AnySession
      .subscribe(onNext: { [weak self] _ in
        guard let self = self else { return }
        self.loadingStatus.accept(.dismiss)
      }, onError: { [weak self] _ in 
        self.loadingStatus.accept(.error(message: "エラーが発生しました"))
      })
      .disposed(by: disposeBag)
  }
  
// ViewController
func setupObserver() {
    viewModel.loadingStatus
      .subscribe(onNext: { [weak self] status in
        guard let self = self else { return }
        switch status {
        case .none:
          break
        case .loading:
          // インディケーターを出す処理
        case .error(message):
          // エラーのインディケーターを出す処理
          // 何らかの時間を待つ処理 ←ここが綺麗じゃない
          self.viewModel.loadingStatus.accept(.dismiss) // ←ここが綺麗じゃない
        case .dismiss:
          // インディケーターを消す処理
        }
      })
      .disposed(by: disposeBag)
}

のような形になってしまい、あまり綺麗な形で運用できていないので、何かアドバイス等あれば教えてください。 本当は↓こうしたい

AnySession
      .subscribe(onNext: { [weak self] _ in
        guard let self = self else { return }
        self.loadingStatus.accept(.dismiss)
      }, onError: { [weak self] _ in 
        self.loadingStatus.accept(.error(message: "エラーが発生しました"))
      }) // onErrorの場合だけ数秒待って self.loadingStatus.accept(.dismiss)をしたい
      .disposed(by: disposeBag)