Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: RxSwift's asObservable() method and type erasure

I am having problem understanding the rationale and purpose for asObservable method in RxSwift's Observable class.

/// A type-erased `ObservableType`. 
///
/// It represents a push style sequence.
public class Observable<Element> : ObservableType {
    /// Type of elements in sequence.
    public typealias E = Element

    public func subscribe<O: ObserverType>(_ observer: O) -> Disposable where O.E == E {
        abstractMethod()
    }

    public func asObservable() -> Observable<E> {
        return self
    }
}

What's the purpose of asObservable when it's returning self?

Surely if you can run this method, you already have access to the object. Also, what does the "type-erased ObservableType" in the comment means?

like image 497
Boon Avatar asked Feb 02 '17 12:02

Boon


2 Answers

I believe the answer is apparent if you look at the ObservableType protocol and what objects conform to it (namely things like subjects, etc.). The only guarantee is that the object will return an Observable in response to a call to asObservable. While conformance is trivial for an Observable, it may be less so for subjects and other units. But this guarantee allows you to use all type that can provide an Observable together in the same operator chain.

In essence this is similar to Strings conformance to CustomStringConvertible.

like image 129
Scott H Avatar answered Nov 18 '22 00:11

Scott H


You asked two questions:

1. What's the purpose of asObservable when it's returning self?

You almost don't need to ever use asObservable(). The only time I think you'd need is when you're assigning casting a Subject/Relay to an Observable.

Suppose you have a variable that is a BehaviorRelay. It can both, observe and be an observable. ViewModel.swift

let deviceOrientation = BehaviorRelay<UIInterfaceOrientation>(value: UIApplication.shared.statusBarOrientation)

And then you have a variable that is not both but only an observable like below.

ViewController.swift

lazy var incorrect : Observable<UIInterfaceOrientation> = {
        return self.viewModel.deviceOrientation // ERROR
}()

lazy var correct : Observable<UIInterfaceOrientation> = {
        return self.viewModel.deviceOrientation.asObservable()
}()

Then you'd need to cast so you'd have the correct type.

The incorrect variable would give the following error:

Cannot convert value of type BehaviorRelay<UIInterfaceOrientation> to closure result type Observable<UIInterfaceOrientation>

2. What does the "type-erased ObservableType" in the comment means?

I suspect Scott's comment is semi-correct. I mean for sure it's confusing. You can flatten the type of a BehvaiorRelay, PublishSubject to a Observable and then assign one Observable to another. Otherwise still they all require the associatedType to be given ie no type erasure happening.

let x : AnyHashable = 10
let y : AnyHashable = "Alex"

if x == y { print("equal" } // compiles!

var i = PublishSubject<Int>().asObservable()
var s = PublishSubject<String>().asObservable()

if i == s { print("equal" } // does NOT compile. Gives following error:

Binary operator == cannot be applied to operands of type Observable<Int> and Observable<String>

like image 25
mfaani Avatar answered Nov 18 '22 00:11

mfaani