Elementが非OptionalのObservableでエラー時にnextを流す

解決したい課題

Elementが非OptionalのObservableでエラーを無視したい時がある。
Observableはエラーが流れてきたらcompleted状態になりもう二度とnextで値が流れてくることがなくなってしまうため、大抵無視したい時はcatchError(_:)catchErrorJustReturn(_:)でerrorを握りつぶすが、Elementが非Optionalの場合はerrorの代わりに何を流せばいいか迷うことが多い。

hogeObservable // Elementが非OptionalのObservable
    .catchError {
        // 何返そう??
    }

Elementが非Optionalといっても、例えばElementがIntやStringの場合はもしデフォルト値が決まっていればそれを返せばいいので問題ない。

intObservable // : Observable<Int>
    .catchErrorJustReturn(0)

問題なのは、Elementがデフォルト値が決まっていない何かしらのクラス・構造体などのとき。

struct Hoge {
    let id: String
    let name: String
}

hogeObservable // Observable<Hoge>
    .catchError {
        // 何返そう??
    }

この時、emptyを流して問題なければいい場合もあるが、emptyを流したら困る時がある。
例えば、zipでそのObservableを使うときは、emptyが流れてしまうと処理が止まってしまい、zip以降に値が流れないということが起きてしまう。

let hoge = hogeObservable
    .catchError { _ in
        return .empty()
    }

let fuga: Observable<Hoge>

Observable.zip(hoge, fuga) { $0 }
    .subscribe(onNext: {
        // hogeにエラーが流れた場合は値が流れてこないのでここは呼ばれない
    })

解決策

一度Elementの型をOptionalにしてerrorの場合にnilを返すことでこの問題を回避できる。

hogeObservable // Observable<Hoge>
    .map { Optional($0) } // Observable<Hoge?>に変換
    // .map { element -> E? in element } でも可
    .catchErrorJustReturn(nil)

上の例のzipの場合は以下のようになる。

let hoge = hogeObservable
    .map { Optional($0) }
    .catchErrorJustReturn(nil)

let fuga: Observable<Hoge>

Observable.zip(hoge, fuga)
    .subscribe(onNext: {
        // errorの時は$0.0がnilで流れてくる
    })

参考記事

stackoverflow.com