iosswiftmvvmrx-swiftsvprogresshud

How to show/hide the progressHUD, with MVVM and RxSwift in swift


I'm beginning with MVVM in order to well separate logic code from the view. But I have some concern about where to put the progressHUD related code when tapping a button that makes a request.

Before, I used to do that:

//Before 
@IBAction func startRequestTapped() {
   SVProgressHUD.show()
    self.apiClient.requestObservable().subscribe(onError: { (error) in
        SVProgressHUD.hide()                    
    }, onCompleted: { 
        SVProgressHUD.hide()                                        
    })
}

But when I use mvvm, I do like that:

//In the viewModel
public var validateButtonDidTap = PublishSubject<Void>()
init() {
    validateButtonDidTap.flatMap { (_)
        return self.apiClient.requestObservable()
    }
}


   // In the viewController
viewDidLoad() {
    let tap = self.validateButton.rx.tap
    tap.bindTo(self.viewModel.validateButtonDidTap)
}

And amongst that, I don't know where to put the the ProgressHUD hide or show.


Solution

  • Mark answer is right, but I am going to guide you step by step.

    Let's supose you're going to try signing in.

    1. Copy ActivityIndicator.swift file in your project.

    2. In the viewModel:

      //MARK: - Properties
      
      /// The http client
      private let apiClient: YourApiClient
      
      /// Clousure when button is tapped
      var didTappedButton: () -> Void = {}
      
      /// The user 
      var user: Observable<User>
      
      /// Is signing process in progress
      let signingIn: Observable<Bool> = ActivityIndicator().asObservable()
      
      //MARK: - Initialization
      
      init(client: YourApiClient) {
          self.client = client
      
          self.didTappedButton = { [weak self] in
              self.user = self.apiClient
                              .yourSignInRequest()
                              .trackActivity(self.signingIn)
                              .observeOn(MainScheduler.instance)
          }
      }
      
    3. Create an extension of SVProgressHUD: (I don't know SVProgressHUD library, but it would be something like this. Please fix it if needed)

      extension Reactive where Base: SVProgressHUD {
      
          /// Bindable sink for `show()`, `hide()` methods.
          public static var isAnimating: UIBindingObserver<Base, Bool> {
              return UIBindingObserver(UIElement: self.base) { progressHUD, isVisible in
                  if isVisible {
                      progressHUD.show() // or other show methods
                  } else {
                      progressHUD.dismiss() // or other hide methods
                  }
              }
          }
      
      }
      
    4. In your viewController:

      @IBAction func startRequestTapped() {
          viewModel.didTappedButton()
      }
      
      override func viewDidLoad() {
      
          // ...
      
          viewModel.signingIn
                   .bindTo(SVProgressHUD.rx.isAnimating)
                   .addDisposableTo(disposeBag)
      }