RxSwiftのTraitについて

AbemaTVでiOSアプリ開発をしております[shoheiyokoyama](https://github.com/shoheiyokoyama)です。
今回は、[AbemaTV Advent Calendar 2017](https://adventar.org/calendars/2216#list-2017)の20日目の記事を担当させていただきます。

# RxSwift

`RxSwift`はReactive programingを実現するためのSwift製フレームワークで、AbemaTVのiOSアプリでメインフレームワークとして使用されています。Reactive Programmingや、それに関連したObserverパターンについては、過去に記事でまとめたものがあるのでこちらで雰囲気だけでも掴んでいただければと思います。

- [SwiftでReactive Programming](https://qiita.com/shoheiyokoyama/items/60aa617ab39778d73110)
- [デザインパターン「Observer」](https://qiita.com/shoheiyokoyama/items/d4b844ed29f84a80795b)

さて、タイトルでTraitという言葉がでてきましたが、`RxSwift`はTraitという様々なユースケースで使用できる`Observable`のラッパーを提供しています。iOSのcocoaフレームワーク用に`RxCocoa traits`というものもありますが、読者のみなさんが眠くなってしまう危険もありますので、今回は`RxSwift traits`の説明だけにとどめたいと思います。
`RxCocoa traits`の`Driver`に関しては、2日前に @inamiy さんが[RxSwift.Driver についての個人的見解](https://qiita.com/inamiy/items/d6fa90d0401fa0e83852)という素晴らしい記事を書いているので、ご覧いただければと思います。

## RxSwift Trait

今回は以下3つのTraitについて触れたいと思います。

- Single
- Completable
- Maybe

それぞれのTraitを説明する前に、`RxSwift traits`についてもう少し説明します。
[RxSwiftのドキュメント](https://github.com/ReactiveX/RxSwift)をみてみると、Traitは簡単な`Observable`のラッパーで、read-onlyなプロパティという説明があります。
また、`RxCocoa traits`と異なり、副作用がない`Observable`です。

```swift
struct Single<Element> {
    let source: Observable<Element>
}
```

しかしコードを追ってみると、正確には`typealias`で実体は`PrimitiveSequence`であることがわかります。

```swift
/// Represents a push style sequence containing 1 element.
public typealias Single<Element> = PrimitiveSequence<SingleTrait, Element>

/// Represents a push style sequence containing 0 elements.
public typealias Completable = PrimitiveSequence<CompletableTrait, Swift.Never>

/// Represents a push style sequence containing 0 or 1 element.
public typealias Maybe<Element> = PrimitiveSequence<MaybeTrait, Element>
```

この`PrimitiveSequence`は`Genirics Type`として`Trait`と`Element`をもつ構造体で、`TraitType`によって振る舞いを変える設計となっています。

```swift:PrimitiveSequence.swift

/// Observable sequences containing 0 or 1 element.
public struct PrimitiveSequence<Trait, Element> {
    let source: Observable<Element>

    init(raw: Observable<Element>) {
        self.source = raw
    }
}


extension PrimitiveSequence: PrimitiveSequenceType {
    /// Additional constraints
    public typealias TraitType = Trait
    /// Sequence element type
    public typealias ElementType = Element
    ...
```

```swift:Single.swift

extension PrimitiveSequenceType where TraitType == SingleTrait {
    public static func create(subscribe: @escaping (@escaping SingleObserver) -> Disposable) -> Single<ElementType> {
```

```swift:Completable.swift
extension PrimitiveSequenceType where Self.ElementType == Never, Self.TraitType == RxSwift.CompletableTrait {
    public static func create(subscribe: @escaping (@escaping PrimitiveSequenceType.CompletableObserver) -> Disposable) -> RxSwift.PrimitiveSequence<Self.TraitType, Self.ElementType>
```

```swift:Maybe.swift
public extension PrimitiveSequenceType where TraitType == MaybeTrait {
    public static func create(subscribe: @escaping (@escaping MaybeObserver) -> Disposable) -> PrimitiveSequence<TraitType, ElementType>
```

`Ruby`や`Python`などのTraitのような実装パターンですね。`RxSwift Trait`の名前もここからきてるんでしょうか。

それぞれのTraitでcreateが実装されてますが、これは`ReactiveX`で定義されている`Operator`で、`Observable`のファクトリーメソッドです。[API Design Guidelines](https://swift.org/documentation/api-design-guidelines/#strive-for-fluent-usage)で記載があるようにSwiftのファクトリーメソッドの命名では`make-prefix`が推奨されてますが、あくまでここは`ReactiveX`の慣習に習ってるようです。

ドキュメント：[ReactiveX / create](http://reactivex.io/documentation/operators/create.html)

標準のcreate operatorをは、カスタムの`Observable`を生成することができ、`onNext`, `onError`, `onCompleted`を自由に呼び出すことができます。

```swift
func customObservable() -> Observable<Element> {
        return Observable.create { observer in
            observer.onNext(element)
            observer.onError(error)
            observer.onCompleted()
            return Disposables.create()
        }
    }
```

### Single

> A Single is a variation of Observable that, instead of emitting a series of elements, is always guaranteed to emit either a single element or an error. [Traits (formerly Units)](https://github.com/ReactiveX/RxSwift/blob/master/Documentation/Traits.md)より一部抜粋

Singleは一回のみElementかErrorを送信することが保証されている`Observable`です。
一回イベントを送信すると、disposeされるようになってます。


```swift
var singleObservable: Single<Element> {
    return Single.create { single in
        fetchData { data, error in
            if let error = error {
                single(.error(error))
            }

            single(.success(data))
        }

        return Disposables.create()
    }
}
```

少し内部のコードを追ってみましょう。
`create`の内部では`subscribe`として`SingleObserver`(=`SingleEvent)`を返すようになっており、`SingleEvent`では`Result`のように`success`と`error`のみ定義されているため、実装者は`onComplete()`を呼び出せないようになっています。

`create`の内部では標準のcreate operatorが使用されており、`SingleEvent.success`が呼び出されたら即座に`on(.completed)`していることがわかります。

```swift:Single.swift

public enum SingleEvent<Element> {
    /// One and only sequence element is produced. (underlying observable sequence emits: `.next(Element)`, `.completed`)
    case success(Element)
    
    /// Sequence terminated with an error. (underlying observable sequence emits: `.error(Error)`)
    case error(Swift.Error)
}

public typealias SingleObserver = (SingleEvent<ElementType>) -> ()

public static func create(subscribe: @escaping (@escaping SingleObserver) -> Disposable) -> Single<ElementType> {
        let source = Observable<ElementType>.create { observer in
            return subscribe { event in
                switch event {
                case .success(let element):
                    observer.on(.next(element))
                    observer.on(.completed)  // Itemを送信したらcompletedする
                case .error(let error):
                    observer.on(.error(error))
                }
            }
        }
        
        return PrimitiveSequence(raw: source)
    }
```

`observer.on(.error(error))`の場合は`observer.on(.completed)`を呼び出していませんが、`on(_ event: Event<E>)`の実装をみてみると、`error`が放出された場合には`observer.on(.completed)`と同様`dispose()`が呼び出されることがわかります。

```swift
func on(_ event: Event<E>) {
        #if DEBUG
            _synchronizationTracker.register(synchronizationErrorMessage: .default)
            defer { _synchronizationTracker.unregister() }
        #endif
        switch event {
        case .next:
            if _isStopped == 1 {
                return
            }
            forwardOn(event)
        case .error, .completed: // errorとcompletedの挙動は同じ
            if AtomicCompareAndSwap(0, 1, &_isStopped) {
                forwardOn(event)
                dispose()
            }
        }
    }
```

ここまで理解できれば、残りのTraitも簡単です。

## Completable

`Completable`は名前の通り、`error`か`completed`のイベントのみ送信する`Observable`で、onNextによるイベントでItemがこないことが保証されています。


```swift
var completableObservable: Completable {
    return Completable.create { completable in
        excuteWork { result in
            if case let .error(e) = result {
                completable(.error(e))
            }
            completable(.completed)
        }

        return Disposables.create {}
    }
}
```

`error`か`completed`が送信されると`Observable`はdisposeされるので、一度のみ`Event`を送信することも保証されてますね。

```swift:Completable.swift
public enum CompletableEvent {
    /// Sequence terminated with an error. (underlying observable sequence emits: `.error(Error)`)
    case error(Swift.Error)
    
    /// Sequence completed successfully.
    case completed
}

...

public static func create(subscribe: @escaping (@escaping CompletableObserver) -> Disposable) -> PrimitiveSequence<TraitType, ElementType> {
        let source = Observable<ElementType>.create { observer in
            return subscribe { event in
                switch event {
                case .error(let error):
                    observer.on(.error(error))
                case .completed:
                    observer.on(.completed)
                }
            }
        }
        
        return PrimitiveSequence(raw: source)
    }
```

### Maybe

Maybeは通常のEvent同様`next`, `error`, `completed`を送信することができ、一度のみの`Event`送信が保証されています。いずれかのEventが送信されると即座に`Observable`が`dispose`されます。

```swift
var maybeObservable: Maybe<String> {
    return Maybe<String>.create { maybe in
        // OR
        maybe(.success("Maybe"))

        // OR
        // maybe(.completed)

        // OR
        // maybe(.error(error))

        return Disposables.create {}
    }
}

```

```swift:Maybe.swift
public enum MaybeEvent<Element> {
    /// One and only sequence element is produced. (underlying observable sequence emits: `.next(Element)`, `.completed`)
    case success(Element)
    
    /// Sequence terminated with an error. (underlying observable sequence emits: `.error(Error)`)
    case error(Swift.Error)
    
    /// Sequence completed successfully.
    case completed
}

...

public static func create(subscribe: @escaping (@escaping MaybeObserver) -> Disposable) -> PrimitiveSequence<TraitType, ElementType> {
        let source = Observable<ElementType>.create { observer in
            return subscribe { event in
                switch event {
                case .success(let element):
                    observer.on(.next(element))
                    observer.on(.completed) // nextのあとにcompletedされる
                case .error(let error):
                    observer.on(.error(error))
                case .completed:
                    observer.on(.completed)
                }
            }
        }
        
        return PrimitiveSequence(raw: source)
    }
```

今回は、`RxSwift traits`について簡単に説明しました。
Eventを一度のみだけ受け付けることができる制約は非常に有用で、AbemaTVでもAPIによる値の取得やDBへの書き込みなどは積極的にtraitが使われています。
また、コードを追うことで理解が深まる上に、様々な実装パターンをみることができるのでおすすめです。

簡単ですが、サンプルコードは[こちら](https://github.com/shoheiyokoyama/RxTraitSample/blob/master/Playground/Trait.playground/Contents.swift)をご覧ください

今年も残りわずかですが、2017年悔いが残らぬよう、精一杯RxSwift lifeをお楽しみください。