RxSwiftに新しく追加されたSingle, Maybe, Completable で新しいUnitsたちを調べるのにRxSwiftはドキュメントがなかったので、ソースを読んで見たメモ。
実装ファイル
実装はこちら RxSwift/PrimitiveSequence.swift
新しいUnitsは Traits
というGroupと分類されている。
Traits = 性質という意味。 なるほど。
元となる構造体の定義
まず、PrimitiveSequence
というstructが定義されていている
このstructにはGenericsで Trait(性質)
と Element(要素)
が指定できる。
Elements
はObservable
のレスポンスの型になる。
/// Observable sequences containing 0 or 1 element.
public struct PrimitiveSequence<Trait, Element> {
fileprivate let source: Observable<Element>
init(raw: Observable<Element>) {
self.source = raw
}
}
新しいUnitsの定義
/// Sequence containing exactly 1 element
public enum SingleTrait { }
/// Represents a push style sequence containing 1 element.
public typealias Single<Element> = PrimitiveSequence<SingleTrait, Element>
/// Sequence containing 0 or 1 elements
public enum MaybeTrait { }
/// Represents a push style sequence containing 0 or 1 element.
public typealias Maybe<Element> = PrimitiveSequence<MaybeTrait, Element>
/// Sequence containing 0 elements
public enum CompletableTrait { }
/// Represents a push style sequence containing 0 elements.
public typealias Completable = PrimitiveSequence<CompletableTrait, Swift.Never>
SingleTrait
、MaybeTrait
、CompletableTrait
とそれぞれの性質を表すenumが定義されてる。
Single
、 Maybe
、Completable
が上述の PrimitiveSequence
の typealiasとして定義されてる。
Swift.Never
は初めて知った。何も返さないことはVoidじゃなくて Never
で表すらしい。
(Never - Swift Standard Library | Apple Developer Documentation)
PrimitiveSequenceTypeのプロトコル定義
/// Observable sequences containing 0 or 1 element
public protocol PrimitiveSequenceType {
/// Additional constraints
associatedtype TraitType
/// Sequence element type
associatedtype ElementType
// Converts `self` to primitive sequence.
///
/// - returns: Observable sequence that represents `self`.
var primitiveSequence: PrimitiveSequence<TraitType, ElementType> { get }
}
extension PrimitiveSequence: PrimitiveSequenceType {
/// Additional constraints
public typealias TraitType = Trait
/// Sequence element type
public typealias ElementType = Element
// Converts `self` to primitive sequence.
///
/// - returns: Observable sequence that represents `self`.
public var primitiveSequence: PrimitiveSequence<TraitType, ElementType> {
return self
}
}
ここで、PrimitiveSequenceType
のプロトコルが定義される。
正直ここで Protocolを定義するがよくわかっていない。。。なんでだろ?
今後の似たようなUnitが出てきたにも対応できるように?
ジェネリクスをtypealiasとしておいた方が、 後術のprotocol extensionは書きやすくなる気がする。
PrimitiveSeaquenceをObservableConvertibleTypeに適合
extension PrimitiveSequence: ObservableConvertibleType {
/// Type of elements in sequence.
public typealias E = Element
/// Converts `self` to `Observable` sequence.
///
/// - returns: Observable sequence that represents `self`.
public func asObservable() -> Observable<E> {
return source
}
}
ObservableConvertibleType
に適合させます。
とすることで、PrimitiveSequence
はObservableにもなれます
狭い川は広くすることはできる。逆は不可。
各Unitsごと実装
その後は各性質ごとにEventや、create
、subscribe
等の実装がされる。
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)
}
各性質ごとのfunctionの実装は PrimitiveSequenceType
のExtensionとして実装される。
where でジェネリクスの型をを指定することによって、適合する場合のみこのextensionが使えるようになる。
extension PrimitiveSequenceType where TraitType == SingleTrait {
public typealias SingleObserver = (SingleEvent<ElementType>) -> ()
public func subscribe(_ observer: @escaping (SingleEvent<ElementType>) -> ()) -> Disposable {
var stopped = false
return self.primitiveSequence.asObservable().subscribe { event in
if stopped { return }
stopped = true
switch event {
case .next(let element):
observer(.success(element))
case .error(let error):
observer(.error(error))
case .completed:
rxFatalError("Singles can't emit a completion event")
}
}
}
// その他の実装は省略
}
Single
のsubscribe
を見てもわかるように、success
か、error
しか流れない。
completed
が流れてくるのは Single
の仕様的にありえないので、fatalErrorでアプリが落ちる。
そのほか
そのほかの共通的な map
、filter
などのオペレーターは whereなしの PrimitiveSequenceType
のextensionとして実装。