What is the difference between 'bind(onNext:' and 'subscribe(onNext:'?
If we check 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 the stream should never emit an error and you are interested only in item events.
So you should use subscribe(onNext:...)
when you are 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 the difference between a and b?
map(...)
is a function declared on ObservableType
and returns a new Observable
Let's start from ObservableType
.
ObservableType
is a protocol that requires only one method: subscribe(...)
, this allows it to create a default implementation of func asObservable()
.
For you it means that you can create an Observable
from any type that conforms 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 a new Observable
wrapper around your stream.
And if you check the source of BehaviourRelay
you will find that it conforms to ObservableType
as well. So you can create Observable
from it anytime:
public final class BehaviorRelay<Element>: ObservableType { ... }
Now let's 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 calls asObservable()
inside and operates on a 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 an internal function but you get the main idea.
Calling asObservable
before other operators is redundant (most operators defined on ObservableType
) and just adds a small overhead.