Search code examples
swiftreactive-programmingrx-swiftrx-cocoa

RxSwift: Observe viewDidLoad from view model without Subjects


I have a dependency problem with my UIViewController and my view model. Basically I want to listen the viewDidLoad event inside my view model. At the moment I have a Class A which instantiates view model and UIViewController with parameter the viewModel, so:

let viewModel = ViewModel()
let viewController = UIViewController(viewModel)

and I've created a RxCocoa extension for the viewDidLoad:

var viewDidLoad: Observable<Void> {
    return self.sentMessage(#selector(Base.viewDidLoad)).map { _ in Void() }
}

now I'm stuck to bind this rx.viewDidLoad to an observable inside my view model. I am able to do it with Subjects but I want a reactive approach using just Observable. I know that I could inject rx.viewDidLoad as constructor parameter of the view model but in this way I'd break my architecture and I don't want to allow the UIViewController to instantiate the view model internally but I want to keep it as a injected dependency.

Any suggestions? Thanks

Solution

Thank to @tomahh I've used this solution:

My view controller:

override func configure(viewModel: ViewModel) {
    viewModel.bindViewDidLoad(rx.viewDidLoad)
}

My view model:

func bindViewDidLoad(_ viewControllerDidLoad: Observable<Void>) {
    //Create observers which depend on viewControllerDidLoad
}

Solution

  • Because ViewController already knows about view model, it could set a property on ViewModel at initialisation time

    class ViewController: UIViewController {
      init(_ viewModel: ViewModel) {
        viewModel.viewDidLoad = self.rx.viewDidLoad
      }
    }
    

    And then, observables in ViewModel could be defined as computed property deriving viewDidLoad

    struct ViewModel {
      var viewDidLoad: Observable<Void> = .never()
    
      var something: Observable<String> {
         return viewDidLoad.map { "Huhu, something is guuut" }
      }
    }