Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

rxswift bind(onNext: VS subscribe(onNext:

I have 2 questions:

  1. What difference between 'bind(onNext:' and 'subscribe(onNext:'?
struct Info {
    var index: Int?
    var data: String?
}

let infoData: BehaviorRelay<Info> = BehaviorRelay<Info>(value: Info())
var osInfo: Observable<String> { return self.infoData.map({ return $0.data }).distinctUntilChanged() }

osInfo.bind { (target) in
    print("bind!")
    }.disposed(by: self.disposeBag)

osInfo.subscribe { (target) in
    print("subscribe!")
    }
    .disposed(by: self.disposeBag)
  1. a has no asObservable(), but well executable. What is difference a and b?
a. var osInfo: Observable<String> { return self.infoData.map({ return $0.data }).distinctUntilChanged() }
b. var osInfo: Observable<String> { return self.infoData.asObservable().map({ return $0.data }).distinctUntilChanged() }

like image 996
dh0rmfpdlxm Avatar asked Mar 22 '19 06:03

dh0rmfpdlxm


People also ask

What is subscribe in RxSwift?

RxSwift: Subscribing to Observables It is the subscription that triggers an observable to begin emitting events until . error or . completed is emitted. Observing an observable is known as subscribing. Subscribe takes an escaping closure that takes an event of the supplied typed (that doesn't return anything).

What is Onnext in RxSwift?

Called by the Observable when it emits an item. Takes as a parameter the item emitted by the Observable. Calls are referred to as emissions .

Do vs subscribe in RxSwift?

Yea, you can think of it as subscribe is for the main effect, do is for other side effects. The do operator should be pretty uncommon in your code, while subscribe is necessary. – Daniel T.

What is BehaviorRelay?

Say hello to my little friend, BehaviorRelay . Behavior relays also will not terminate with a completed or error event. Because it wraps a behavior subject, a behavior relay is created with an initial value, and it will replay its latest or initial value to new subscribers.


1 Answers

What difference between 'bind(onNext:' and 'subscribe(onNext:'?

If we check out implementation of bind(...) we found that it does nothing else but just uses subscribe(...) underhood and crashes in Debug with error:

/**
Subscribes an element handler to an observable sequence. 

In case error occurs in debug mode, `fatalError` will be raised.
In case error occurs in release mode, `error` will be logged.

- parameter onNext: Action to invoke for each element in the observable sequence.
- returns: Subscription object used to unsubscribe from the observable sequence.
*/
public func bind(onNext: @escaping (E) -> Void) -> Disposable {
    return subscribe(onNext: onNext, onError: { error in
        rxFatalErrorInDebug("Binding error: \(error)")
    })
}

By using bind(onNext) you can express that stream should never emit error and you interested only in item events.

So you should use subscribe(onNext:...) when you interested in error / complete / disposed events and bind(onNext...) otherwise. But since it is part of RxCocoa and not RxSwift I usually use subscribe everywhere.

a has no asObservable(), but well executable. What is difference a and b?

map(...) is function declared on ObservableType and returning new Observable

Let's start from ObservableType.
ObservableType is protocol that require only one method: subscribe(...), this allow him to create default implementation of func asObservable().
For you it means that you can create Observable from any type that conform to ObservableType.

/// Represents a push style sequence.
public protocol ObservableType : ObservableConvertibleType {
    func subscribe<O: ObserverType>(_ observer: O) -> Disposable where O.E == E
}
extension ObservableType {
    /// Default implementation of converting `ObservableType` to `Observable`.
    public func asObservable() -> Observable<E> {
        // temporary workaround
        //return Observable.create(subscribe: self.subscribe)
        return Observable.create { o in
            return self.subscribe(o)
        }
    }
}

So each time you call asObservable() underhood RxSwift just creates new Observable wrapper around your stream.

And if you check source of BehaviourRelay you will find that it conform to ObservableType as well. So you can create Observable from it anytime:

public final class BehaviorRelay<Element>: ObservableType { ... }

Now lets check map function:

extension ObservableType {

    /**
     Projects each element of an observable sequence into a new form.

     - seealso: [map operator on reactivex.io](http://reactivex.io/documentation/operators/map.html)

     - parameter transform: A transform function to apply to each source element.
     - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source.

     */
    public func map<R>(_ transform: @escaping (E) throws -> R)
        -> Observable<R> {
        return self.asObservable().composeMap(transform)
    }
}

As expected map just call asObservable() inside and operate on new Observable.

If we "unwrap" map call we will get:

var osInfoA: Observable<String> {
    return infoData
        .asObservable()
        .composeMap { $0.data }
        .distinctUntilChanged()
}
var osInfoB: Observable<String> {
    return infoData
        .asObservable()
        .asObservable()
        .composeMap { $0.data }
        .distinctUntilChanged()
}

Sure it will not compile since composeMap is internal function but you got main idea.
Calling asObservable before other operators is redundant (most operators defined on ObservableType) and just add small overhead.

like image 99
ManWithBear Avatar answered Dec 15 '22 12:12

ManWithBear