I want to make one-way binding from UIViewController to VewModel, in ReactiveObjC I've used RACChannel. What is the equivalent of a later one or what is the best way to do that?
Asked
Active
Viewed 42 times
0
-
Need more information. What is the problem you are trying to solve? Give more details please. – Daniel T. Mar 24 '22 at 10:28
-
In reading the ReactiveObjC documentation, I see that a RACChannel sets up a two-way binding so the fact that you say you want a one-way binding is a bit confusing. – Daniel T. Mar 24 '22 at 10:28
1 Answers
0
I'm going to have to answer the more general question here. That of binding the view controller to the view model.
Here's a good video for Reactive View Models
A well constructed reactive view model is a single function that takes a number of Observable inputs from the view inputs and returns a number of Observables that are bound to the views meant for output.
A simple example login view model...
func login(
callServer: @escaping (URLRequest) -> Observable<Data>,
email: Observable<String>,
password: Observable<String>,
login: Observable<Void>
) -> (
loggedIn: Observable<Void>,
errorMessage: Observable<String>
) {
let loginResponse = login
.withLatestFrom(
Observable.combineLatest(email, password) { makeLoginURLRequest(email: $0, password: $1) }
)
.flatMapLatest {
callServer($0)
.map { try JSONDecoder().decode(LoginResponse.self, from: $0) }
.materialize()
}
let loggedIn = loginResponse
.compactMap { $0.element != nil ? () : .none }
let errorMessage = loginResponse
.compactMap { $0.error?.localizedDescription }
return (
loggedIn: loggedIn,
errorMessage: errorMessage
)
}
Call it in the view controller's viewDidLoad like this:
let (loggedIn, errorMessage) = login(
callServer: URLSession.shared.rx.data(request:),
email: emailTextField.rx.text.orEmpty.asObservable(),
password: passwordTextField.rx.text.orEmpty.asObservable(),
login: loginButton.rx.tap.asObservable()
)
loggedIn
.bind(onNext: { print("user is now logged in!") })
.disposed(by: disposeBag)
errorMessage
.bind(onNext: { print("display error message", $0) })
.disposed(by: disposeBag)
Lastly, if you don't like using tuples, you can replace them with structs.

Daniel T.
- 32,821
- 6
- 50
- 72
-
I was using BehaviorRelay to push changes to ViewModel, data comes from multiple sources even from non-observable sources inside ViewController like method calls (for ex. setText: ) I was thinking of way to push just one direction events to ViewModel to eliminate cases that someone can push events to the same BehaviorRelay instance inside ViewModel – DarkSatyr Mar 24 '22 at 15:45
-
I suggest you push the changes directly from the inputs instead of using the relays, and replace the non-observable sources with observable ones. Simplify your life. Less code means fewer bugs. – Daniel T. Mar 24 '22 at 16:00
-
"replace the non-observable sources with observable ones" - but what if I have method like doA { } and inside it setText: argument of which I need to push to ViewModel? – DarkSatyr Mar 24 '22 at 16:04
-
Don't do that... I suggest you join the [RxSwift Slack](http://slack.rxswift.org) channel. – Daniel T. Mar 24 '22 at 19:39