Difference between `.drive()` and `.bind(to:)`
Asked Answered
C

1

13

I am learning RxSwift at the moment.

When would you use .drive(something) and when .bind(to: something)?

Example:

let disposeBag = DisposeBag()
let isEnabled = BehaviorRelay(value: true)
let button = UIButton()
// what is the benefit of this:
isEnabled.asDriver().drive(button.rx.isEnabled).disposed(by: disposeBag)
// over this:
isEnabled.bind(to: button.rx.isEnabled).disposed(by: disposeBag)
// or is the above better?

The answer in use RxSwift, driver and bind to doesn’t really help. I do not get why adding asDriver() is even necessary to make it to a thing that can’t fail.

Cryptology answered 20/5, 2020 at 9:33 Comment(3)
Does this answer your question? use RxSwift, driver and bind toPyaemia
Thank you but not really. I get that Driver can’t fail but why should I do asDriver().drive() rather than asObservable().bind()? (I will but more detail into my question)Cryptology
@Cryptology There is an explicit comparison of bind with syntax sugar drive method the second answer https://mcmap.net/q/490254/-use-rxswift-driver-and-bind-to-closedPicaroon
S
14

From GitHub page of RxSwift

This is the most elaborate trait. Its intention is to provide an intuitive way to write reactive code in the UI layer, or for any case where you want to model a stream of data Driving your application.

  1. Can't error out.
  2. Observe occurs on main scheduler.
  3. Shares side effects (share(replay: 1, scope: .whileConnected)).

Again copying from same page

Its intended use case was to model sequences that drive your application.

Now coming back to differences?

Driver ensures that observe occurs only on main thread:

Drive is one of the traits in RxSwift that ensures observe occurs only on MainThread, Which means no matter on what thread event is emitted and triggered driver, driver will always ensures to forward the event to next operator in the chain or to its subscribe block on main thread.

In Rx all events are propagated on the same thread in which the event occurred. So if you trigger a subject on say thread (100) using subject.onNext(, its subscribe block will be called on the same thread (100) until and unless you use observedOn or subscribedOn operator to ensure the manual thread switching.

If you are driving your UI components from the observables/subjects in your viewModel, drivers make perfect sense. Assume you make API call to fetch data from server on background thread, you wouldn't wanna access your UI components on background thread, connecting/converting your observables/subjects to driver (using asDriver and passing onErrorJustReturn) and driving your UI components via driver will ensure your UI components are always accessed on main thread

Can't error out.

Normally when error occurs, subscription will be terminated and if you are driving your UI components you wouldn't want you subscription/bind to break every time there is an error event.

Example: Assume you drive your tableView via CoreData, and for some reason while fetching the data from CoreData an error occurred, if you don't use drive and had used plane bind(to: its onError will be triggered and its bind with UIComponent will be broken. If you fetch the data again you will have to re-establish this bind. For UI component error in fetching / procuring the data shouldn't make any difference. It should be simply a stream of events which changes its state.

bindTo is nothing more than syntactic sugar coat on subscribe so if you use bindTo or subscribe to drive UI components you will loose all the benefits drive inherently brings to table.

You can always use observedOn to ensure manual switching of thread to main and also might have some retry mechanism to establish subscription back / retain subscription when an error occurs but ultimately you will end up writing your own drive trait

When should I use drive and when should I use bindTo

Thumb rule is are you trying to drive a UI component use drive else use bindTo. Generally if you want your subscribe to occur only on main thread and don't want your subscription to error out (like driving UI components) use driver else stick with bindTo or subscribe

EDIT 1:

OP's question in comment:

asDriver() in my example makes sure isEnabled is being observed on the main thread and I don’t have to pass onErrorJustReturn because BehaviorRelay also can’t fail? In this case drive() has the main thread benefit but not the failsafe benefit?

in my example makes sure isEnabled is being observed on the main thread - YES

I don’t have to pass onErrorJustReturn because BehaviorRelay also can’t fail? - BINGO

If you check a bit deeper on BehaviorRelay you will find

/// BehaviorRelay is a wrapper for BehaviorSubject. /// /// UnlikeBehaviorSubject it can't terminate with error or completed.

So clearly BehaviorRelay cant error out hence compiler is intelligent enough to understand and not to ask for onErrorJustReturn. If you really wanna see one use BehaviorSubject and compiler will ask for it :)

Credits to Daniel to point out my mistake, that drive isn't the only trait that ensures observe occurs only on MainThread hence edited my answer to reflect the same. Thank you

Septuple answered 20/5, 2020 at 10:3 Comment(3)
So, asDriver() in my example makes sure isEnabled is being observed on the main thread and I don’t have to pass onErrorJustReturn because BehaviorRelay also can’t fail? In this case drive() has the main thread benefit but not the failsafe benefit?Cryptology
When you said, "Drive is the only trait in RxSwift that ensures observe occurs only on MainThread," That isn't true... There is also the Signal, ControlProperty, and ControlEventIdem
@daniel-t: Sorry had forgotten about these traits :| lemme update my answer as one of the traits :)Septuple

© 2022 - 2024 — McMap. All rights reserved.