Group Group Group Group Group Group Group Group Group

Am I using Rx and MVVM correctly here?

I’ve got a UIPageViewController with 3 pages. I’m using RxSwift and Cocoa to change the content of each page depending on which page view controller is currently shown.

I have the following property in my UIPageViewController:

var presentationIndex = BehaviorSubject<Int>(value: 0)

Then in viewDidLoad, for each page view controller I do

controllers.forEach { viewController in
  let welcomeView = viewController.view.map { $0 as! WelcomeRootView }
  welcomeView?.viewModel.subscribe(to: presentationIndex)
}

I emit the index in the following delegate method:

extension PageViewController: UIPageViewControllerDelegate {
  func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
    guard let index = controllers.firstIndex(where: { $0 === pendingViewControllers[0] }) else { return }
    presentationIndex.onNext(index)
  }
}

My View Model looks like this:

let image = BehaviorSubject<UIImage>(value: PageState.images[0])
let heading = BehaviorSubject<String>(value: PageState.headings[0])
let subheading = BehaviorSubject<String>(value: PageState.subheadings[0])
let isButtonHidden = BehaviorRelay<Bool>(value: true)

func subscribe(to observable: Observable<Int>) {
    observable
      .share()
      .distinctUntilChanged()
      .subscribe(onNext: { index in
        self.image.onNext(PageState.images[index])
        self.heading.onNext(PageState.headings[index])
        self.subheading.onNext(PageState.subheadings[index])
        
        if index == 2 {
          self.isButtonHidden.accept(false)
        } else {
          self.isButtonHidden.accept(true)
        }
      })
      .disposed(by: disposeBag)
  }

And finally the view:

func bindViewsToViewModel() {
    bindImageView()
    bindHeadingLabel()
    bindSubheadingLabel()
    bindButton()
  }
  
  func bindImageView() {
    viewModel.image
      .bind(to: onboardingImageView.rx.image)
      .disposed(by: disposeBag)
  }
  
  func bindHeadingLabel() {
    viewModel.heading
      .map { $0 }
      .bind(to: headingLabel.rx.text)
      .disposed(by: disposeBag)
  }
  
  func bindSubheadingLabel() {
    viewModel.subheading
      .map { $0 }
      .bind(to: subheadingLabel.rx.text)
      .disposed(by: disposeBag)
  }
  
  func bindButton() {
    viewModel.isButtonHidden
      .bind { self.actionButton.isHidden = $0 }
      .disposed(by: disposeBag)
  }
if index == 2 {
    self.isButtonHidden.accept(false)
} else {
    self.isButtonHidden.accept(true)
}

<=>

self.isButtonHidden.accept(index != 2)