【RxSwift】Traitsについて

# はじめに
RxSwiftの`Subject`と`Relay`については理解して使えていますが、`Traits`については曖昧な状態で使っておりましたので、調べて整理してみました。

## Traits とは

RxSwiftの`Traits`とは、`Observable`をより特定の使用例をターゲットにしたもの（Observableのラッパー）


https://github.com/ReactiveX/RxSwift/blob/main/Documentation/Traits.md#traits-formerly-units

### RxSwift traits
#### [`Single`](https://github.com/ReactiveX/RxSwift/blob/main/RxSwift/Traits/PrimitiveSequence/Single.swift#L30)
Observableのバリエーションで、一連の要素を出す代わりに、常に1つの要素かエラーを出すことが保証されているもの
[`Single.create`](https://github.com/ReactiveX/RxSwift/blob/main/Documentation/Traits.md#creating-a-single)で生成
[`asSingle()`](https://github.com/ReactiveX/RxSwift/blob/main/RxSwift/Traits/PrimitiveSequence/ObservableType%2BPrimitiveSequence.swift#L18)や[`first()`](https://github.com/ReactiveX/RxSwift/blob/main/RxSwift/Traits/PrimitiveSequence/ObservableType%2BPrimitiveSequence.swift#L30)で`Obserbale`から変換可能
[`subscribe(onSuccess:onFailure:)`](https://github.com/ReactiveX/RxSwift/blob/main/RxSwift/Traits/PrimitiveSequence/Single.swift#L129)が使える

#### [`Completable`](https://github.com/ReactiveX/RxSwift/blob/main/RxSwift/Traits/PrimitiveSequence/Completable.swift#L37)
Observableのバリエーションで、完了またはエラーを出すかだけで、要素を出さないことが保証されているもの
[`Completable.create`](https://github.com/ReactiveX/RxSwift/blob/main/Documentation/Traits.md#creating-a-completable)で生成
[`subscribe(onCompleted:onError:)`](https://github.com/ReactiveX/RxSwift/blob/main/RxSwift/Traits/PrimitiveSequence/Completable.swift#L117)が使える

#### [`Maybe`](https://github.com/ReactiveX/RxSwift/blob/main/RxSwift/Traits/PrimitiveSequence/Maybe.swift#L40)
`Single`と`Completable`のちょうど中間のようなもの。1つの要素を出すことも、要素を出さずに完了することも、エラーを出すこともできる。ただし、イベント発生後はすぐ終了する。
[`Maybe.create`](https://github.com/ReactiveX/RxSwift/blob/main/Documentation/Traits.md#creating-a-maybe)で生成
[`asMaybe()`](https://github.com/ReactiveX/RxSwift/blob/main/RxSwift/Traits/PrimitiveSequence/ObservableType%2BPrimitiveSequence.swift#L42)で`Obserbale`から変換可能
[`subscribe(onSuccess:onError:onCompleted:)`](https://github.com/ReactiveX/RxSwift/blob/main/RxSwift/Traits/PrimitiveSequence/Maybe.swift#L132)が使える

### RxCocoa traits
#### [`ControlProperty`](https://github.com/ReactiveX/RxSwift/blob/main/Documentation/Traits.md#controlproperty)

UI要素のプロパティを表すもの
初期値とユーザが変更した値のみ送り、プログラムによる変更は無視される。
サブスクリプション時に、最後の要素が生成された場合はすぐに送る。
エラーにならず、必ずメインススレッドになり、解放されると完了を流す。

<details><summary>UISearchBar+Rxでの使用例</summary>

```swift
extension Reactive where Base: UISearchBar {
    /// Reactive wrapper for `text` property.
    public var value: ControlProperty<String?> {
        let source: Observable<String?> = Observable.deferred { [weak searchBar = self.base as UISearchBar] () -> Observable<String?> in
            let text = searchBar?.text
            
            return (searchBar?.rx.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBar(_:textDidChange:))) ?? Observable.empty())
                    .map { a in
                        return a[1] as? String
                    }
                    .startWith(text)
        }

        let bindingObserver = Binder(self.base) { (searchBar, text: String?) in
            searchBar.text = text
        }
        
        return ControlProperty(values: source, valueSink: bindingObserver)
    }
}
```
</details>

#### [`ControlEvent`](https://github.com/ReactiveX/RxSwift/blob/main/Documentation/Traits.md#controlevent)

UI要素のイベントを表すもの
失敗しない。サブスクリプション時に初期値を送らない。
エラーにならず、必ずメインススレッドになり、解放されると完了を流す。

<details><summary>UICollectionView+Rxでの使用例</summary>

```swift
extension Reactive where Base: UICollectionView {
    
    /// Reactive wrapper for `delegate` message `collectionView:didSelectItemAtIndexPath:`.
    public var itemSelected: ControlEvent<IndexPath> {
        let source = delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:didSelectItemAt:)))
            .map { a in
                return a[1] as! IndexPath
            }
        
        return ControlEvent(events: source)
    }
}
```

</details>

#### [`Driver`](https://github.com/ReactiveX/RxSwift/blob/main/Documentation/Traits.md#driver)
エラーにならず、必ずメインススレッドになる。
サブスクリプション時に、最後の要素が生成された場合はすぐに送る。
例えば、CoreDataモデルからUI表示するときなどに使用
[`asDriver`](https://github.com/ReactiveX/RxSwift/blob/main/RxCocoa/Traits/Driver/ControlProperty%2BDriver.swift#L15)で`ControlProperty`を`Driver`に変換
[`drive`](https://github.com/ReactiveX/RxSwift/blob/main/RxCocoa/Traits/Driver/Driver%2BSubscription.swift#L15)でUI要素にバインド

<details><summary>asDriverの使用例</summary>

`asDriver`を使用しないで実装する場合👇

```swift
let results = query.rx.text
    .throttle(.milliseconds(300), scheduler: MainScheduler.instance)
    .flatMapLatest { query in
        fetchAutoCompleteItems(query)
            .observeOn(MainScheduler.instance)  // results are returned on MainScheduler
            .catchErrorJustReturn([])           // in the worst case, errors are handled
    }
    .share(replay: 1)                           // HTTP requests are shared and results replayed
                                                // to all UI elements

results
    .map { "\($0.count)" }
    .bind(to: resultCount.rx.text)
    .disposed(by: disposeBag)

results
    .bind(to: resultsTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in
        cell.textLabel?.text = "\(result)"
    }
    .disposed(by: disposeBag)
```

`asDriver`で実装する場合👇

```swift
let results = query.rx.text.asDriver()        // This converts a normal sequence into a `Driver` sequence.
    .throttle(.milliseconds(300), scheduler: MainScheduler.instance)
    .flatMapLatest { query in
        fetchAutoCompleteItems(query)
            .asDriver(onErrorJustReturn: [])  // Builder just needs info about what to return in case of error.
    }

results
    .map { "\($0.count)" }
    .drive(resultCount.rx.text)               // If there is a `drive` method available instead of `bind(to:)`,
    .disposed(by: disposeBag)              // that means that the compiler has proven that all properties
                                              // are satisfied.
results
    .drive(resultsTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in
        cell.textLabel?.text = "\(result)"
    }
    .disposed(by: disposeBag)
```

</details>

#### [`Signal`](https://github.com/ReactiveX/RxSwift/blob/main/Documentation/Traits.md#signal)
`Driver`と似ているが、サブスクリプション時に最新のイベントを再生しない点が違い
エラーにならず、必ずメインススレッドになる点は`Driver`と同じ。
[`asSignal`](https://github.com/ReactiveX/RxSwift/blob/main/RxCocoa/Traits/Signal/ControlEvent%2BSignal.swift#L15)で`ControlEvent`を`Signal`に変換
[`emit`](https://github.com/ReactiveX/RxSwift/blob/main/RxCocoa/Traits/Signal/Signal%2BSubscription.swift#L12)でUI要素にバインド

# おわりに
もっと複雑なものかと思って怯えてましたが、整理してみると分かりやすくて安心しました。
これ以外にもRxSwift６で`Infallible`というtraitが追加されているようですが、使う機会があればまた調べて追記したいと思います。

# 参考
https://docs.rxswift.org/

https://qiita.com/yyokii/items/81dc182dc4a6f1b9fd1f

https://qiita.com/decoch/items/4cc6ca0b1ae80f1bb751