How to make action in viewModel of ViewController when in UIView button was tapped in RxSwift?

Viewed 492

I have a view like custom popup, in it I have a button.

I observe tap on it with the help of let tap = button.rx.tap

When this button had been tapped, l need to make action in my viewModel - for example, print "tap" and push action into relay, but l can't understand, how to bind whem together.

I try to make it in my viewModel:

let buttonTapped = view.tap
let tapRelay = PublishRelay<Void>

func setTap() {
buttonTapped
.bind { [weak self] _ in {
print("tap")
self?.tapRelay.accept(())
.disposed(by: bag)

So my goal is to just make sure that taps on button is going on, and l don't understand how to do it right.

1 Answers

First, let's clear up a misconception. The view model is not where you "make action." The view model is where you want logic. There should not be any relays, subjects or observers in your view model. It should be Observables in, Observables out.

Second, when defining an Observable chain, much like when you are defining a formula in a cell of an Excel spreadsheet, the final result, the thing you want to ultimately do, is the most important part but that is the one thing you haven't described. I doubt that all you want to do is print something.

Third, a Subscription, just like a use-case, consists of a Cause and Effect, with Logic transforming the cause into an effect. The Logic is what goes in the view model, an here the only logic you have described is that "tap" should be the output every time the button is tapped.

So as it stands, the entire use-case you have described is "when the user taps the button, the system should print 'tap'." With only that to go on, we have a Cause (the button tap), the Logic (the word "tap" once per tap) and the Effect (print something.) Which would be modeled like this:

// in the view controller or wherever you are keeping effects...
handle(tap: view.tap)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

// in the logic portion. This is the "view model"
func handle(tap: Observable<Void>) -> Observable<String> { 
    tap.map { "tap" }
}

I know the code above looks crazy simple. "Is that all there is to Rx?" you might be thinking. Rx code is much simpler than anything you have ever written before. Your use-cases will be obvious and once you get the hang of it, they will even be easier.

I understand that your real use-case is much more complex than "print tap" but since you are viewing the use-case, and asking the question, from a procedural mindset, you didn't even think to include all the parts of the use-case. I suggest you either ask a new question or update this question with the real use-case. Also, you will likely benefit from joining the RxSwift slack channel where you can ask lots of little questions that are too small for Stack Overflow.

Related