From c7c93b7ec49cf4ac3b372a552e81ef9cb4ca7d3d Mon Sep 17 00:00:00 2001 From: yury Date: Sat, 3 Oct 2015 13:41:17 +0300 Subject: [PATCH 001/210] prefix with _ private properties --- RxSwift/Concurrency/AsyncLock.swift | 32 +++++++++---------- RxSwift/Disposables/CompositeDisposable.swift | 16 +++++----- RxSwift/Disposables/DisposeBag.swift | 20 ++++++------ RxSwift/Disposables/SerialDisposable.swift | 8 ++--- .../SingleAssignmentDisposable.swift | 10 +++--- .../Observables/Implementations/Sink.swift | 14 ++++---- RxSwift/Subjects/PublishSubject.swift | 13 +++----- RxSwift/Subjects/Variable.swift | 8 ++--- 8 files changed, 59 insertions(+), 62 deletions(-) diff --git a/RxSwift/Concurrency/AsyncLock.swift b/RxSwift/Concurrency/AsyncLock.swift index 64b55ba6..86664d25 100644 --- a/RxSwift/Concurrency/AsyncLock.swift +++ b/RxSwift/Concurrency/AsyncLock.swift @@ -21,25 +21,25 @@ That means that enqueued work could possibly be executed later on a different th class AsyncLock : Disposable { typealias Action = () -> Void - private var lock = NSRecursiveLock() + private var _lock = NSRecursiveLock() - private var queue: Queue = Queue(capacity: 2) - private var isAcquired: Bool = false - private var hasFaulted: Bool = false + private var _queue: Queue = Queue(capacity: 2) + private var _isAcquired: Bool = false + private var _hasFaulted: Bool = false init() { } func wait(action: Action) { - let isOwner = lock.calculateLocked { () -> Bool in - if self.hasFaulted { + let isOwner = _lock.calculateLocked { () -> Bool in + if _hasFaulted { return false } - self.queue.enqueue(action) - let isOwner = !self.isAcquired - self.isAcquired = true + _queue.enqueue(action) + let isOwner = !_isAcquired + _isAcquired = true return isOwner } @@ -49,12 +49,12 @@ class AsyncLock : Disposable { } while true { - let nextAction = lock.calculateLocked { () -> Action? in - if self.queue.count > 0 { - return self.queue.dequeue() + let nextAction = _lock.calculateLocked { () -> Action? in + if _queue.count > 0 { + return _queue.dequeue() } else { - self.isAcquired = false + _isAcquired = false return nil } } @@ -69,9 +69,9 @@ class AsyncLock : Disposable { } func dispose() { - lock.performLocked { oldState in - self.queue = Queue(capacity: 0) - self.hasFaulted = true + _lock.performLocked { oldState in + _queue = Queue(capacity: 0) + _hasFaulted = true } } } \ No newline at end of file diff --git a/RxSwift/Disposables/CompositeDisposable.swift b/RxSwift/Disposables/CompositeDisposable.swift index ccedce97..ac772c13 100644 --- a/RxSwift/Disposables/CompositeDisposable.swift +++ b/RxSwift/Disposables/CompositeDisposable.swift @@ -14,14 +14,14 @@ Represents a group of disposable resources that are disposed together. public class CompositeDisposable : DisposeBase, Disposable, Cancelable { public typealias DisposeKey = Bag.KeyType - private var lock = SpinLock() + private var _lock = SpinLock() // state private var disposables: Bag? = Bag() public var disposed: Bool { get { - return self.lock.calculateLocked { + return _lock.calculateLocked { return disposables == nil } } @@ -34,8 +34,8 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable { Initializes a new instance of composite disposable with the specified number of disposables. */ public init(_ disposable1: Disposable, _ disposable2: Disposable) { - self.disposables!.insert(disposable1) - self.disposables!.insert(disposable2) + disposables!.insert(disposable1) + disposables!.insert(disposable2) } /** @@ -66,7 +66,7 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable { public func addDisposable(disposable: Disposable) -> DisposeKey? { // this should be let // bucause of compiler bug it's var - let key = self.lock.calculateLocked { () -> DisposeKey? in + let key = _lock.calculateLocked { () -> DisposeKey? in return disposables?.insert(disposable) } @@ -82,7 +82,7 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable { */ public var count: Int { get { - return self.lock.calculateLocked { + return _lock.calculateLocked { return disposables?.count ?? 0 } } @@ -94,7 +94,7 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable { - parameter disposeKey: Key used to identify disposable to be removed. */ public func removeDisposable(disposeKey: DisposeKey) { - let disposable = self.lock.calculateLocked { () -> Disposable? in + let disposable = _lock.calculateLocked { () -> Disposable? in return disposables?.removeKey(disposeKey) } @@ -107,7 +107,7 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable { Disposes all disposables in the group and removes them from the group. */ public func dispose() { - let oldDisposables = self.lock.calculateLocked { () -> Bag? in + let oldDisposables = _lock.calculateLocked { () -> Bag? in let disposeBag = disposables self.disposables = nil diff --git a/RxSwift/Disposables/DisposeBag.swift b/RxSwift/Disposables/DisposeBag.swift index 867aa91e..28f6c09e 100644 --- a/RxSwift/Disposables/DisposeBag.swift +++ b/RxSwift/Disposables/DisposeBag.swift @@ -33,11 +33,11 @@ In case explicit disposal is necessary, there is also `CompositeDisposable`. */ public class DisposeBag: DisposeBase { - private var lock = SpinLock() + private var _lock = SpinLock() // state - private var disposables = [Disposable]() - private var disposed = false + private var _disposables = [Disposable]() + private var _disposed = false /** Constructs new empty dispose bag. @@ -52,12 +52,12 @@ public class DisposeBag: DisposeBase { - parameter disposable: Disposable to add. */ public func addDisposable(disposable: Disposable) { - let dispose = lock.calculateLocked { () -> Bool in - if disposed { + let dispose = _lock.calculateLocked { () -> Bool in + if _disposed { return true } - disposables.append(disposable) + _disposables.append(disposable) return false } @@ -71,11 +71,11 @@ public class DisposeBag: DisposeBase { This is internal on purpose, take a look at `CompositeDisposable` instead. */ func dispose() { - let oldDisposables = lock.calculateLocked { () -> [Disposable] in - let disposables = self.disposables + let oldDisposables = _lock.calculateLocked { () -> [Disposable] in + let disposables = _disposables - self.disposables.removeAll(keepCapacity: false) - self.disposed = true + _disposables.removeAll(keepCapacity: false) + _disposed = true return disposables } diff --git a/RxSwift/Disposables/SerialDisposable.swift b/RxSwift/Disposables/SerialDisposable.swift index 2ca4ea53..571000cf 100644 --- a/RxSwift/Disposables/SerialDisposable.swift +++ b/RxSwift/Disposables/SerialDisposable.swift @@ -12,7 +12,7 @@ import Foundation Represents a disposable resource whose underlying disposable resource can be replaced by another disposable resource, causing automatic disposal of the previous underlying disposable resource. */ public class SerialDisposable : DisposeBase, Cancelable { - var lock = SpinLock() + private var _lock = SpinLock() // state private var _current = nil as Disposable? @@ -43,12 +43,12 @@ public class SerialDisposable : DisposeBase, Cancelable { */ public var disposable: Disposable { get { - return self.lock.calculateLocked { + return _lock.calculateLocked { return self.disposable } } set (newDisposable) { - let disposable: Disposable? = self.lock.calculateLocked { + let disposable: Disposable? = _lock.calculateLocked { if _disposed { return newDisposable } @@ -69,7 +69,7 @@ public class SerialDisposable : DisposeBase, Cancelable { Disposes the underlying disposable as well as all future replacements. */ public func dispose() { - let disposable: Disposable? = self.lock.calculateLocked { + let disposable: Disposable? = _lock.calculateLocked { if _disposed { return nil } diff --git a/RxSwift/Disposables/SingleAssignmentDisposable.swift b/RxSwift/Disposables/SingleAssignmentDisposable.swift index a5a419e6..bf7e3f21 100644 --- a/RxSwift/Disposables/SingleAssignmentDisposable.swift +++ b/RxSwift/Disposables/SingleAssignmentDisposable.swift @@ -14,7 +14,7 @@ Represents a disposable resource which only allows a single assignment of its un If an underlying disposable resource has already been set, future attempts to set the underlying disposable resource will throw an exception. */ public class SingleAssignmentDisposable : DisposeBase, Disposable, Cancelable { - private var lock = SpinLock() + private var _lock = SpinLock() // state private var _disposed = false @@ -26,7 +26,7 @@ public class SingleAssignmentDisposable : DisposeBase, Disposable, Cancelable { */ public var disposed: Bool { get { - return lock.calculateLocked { + return _lock.calculateLocked { return _disposed } } @@ -46,12 +46,12 @@ public class SingleAssignmentDisposable : DisposeBase, Disposable, Cancelable { */ public var disposable: Disposable { get { - return lock.calculateLocked { + return _lock.calculateLocked { return _disposable ?? NopDisposable.instance } } set { - let disposable: Disposable? = lock.calculateLocked { + let disposable: Disposable? = _lock.calculateLocked { if _disposableSet { rxFatalError("oldState.disposable != nil") } @@ -77,7 +77,7 @@ public class SingleAssignmentDisposable : DisposeBase, Disposable, Cancelable { Disposes the underlying disposable. */ public func dispose() { - let disposable: Disposable? = lock.calculateLocked { + let disposable: Disposable? = _lock.calculateLocked { _disposed = true let dispose = _disposable _disposable = nil diff --git a/RxSwift/Observables/Implementations/Sink.swift b/RxSwift/Observables/Implementations/Sink.swift index 48ec651b..c7b16261 100644 --- a/RxSwift/Observables/Implementations/Sink.swift +++ b/RxSwift/Observables/Implementations/Sink.swift @@ -9,22 +9,22 @@ import Foundation class Sink : Disposable { - private var lock = SpinLock() + private var _lock = SpinLock() // state - var _observer: O? - var _cancel: Disposable - var _disposed: Bool = false + private var _observer: O? + private var _cancel: Disposable + private var _disposed: Bool = false var observer: O? { get { - return lock.calculateLocked { _observer } + return _lock.calculateLocked { _observer } } } var cancel: Disposable { get { - return lock.calculateLocked { _cancel } + return _lock.calculateLocked { _cancel } } } @@ -37,7 +37,7 @@ class Sink : Disposable { } func dispose() { - let cancel: Disposable? = lock.calculateLocked { + let cancel: Disposable? = _lock.calculateLocked { if _disposed { return nil } diff --git a/RxSwift/Subjects/PublishSubject.swift b/RxSwift/Subjects/PublishSubject.swift index 143d1869..97440f9f 100644 --- a/RxSwift/Subjects/PublishSubject.swift +++ b/RxSwift/Subjects/PublishSubject.swift @@ -11,7 +11,7 @@ import Foundation class Subscription : Disposable { typealias KeyType = Bag>.KeyType - private var lock = SpinLock() + private var _lock = SpinLock() // state private var subject: PublishSubject? @@ -23,13 +23,10 @@ class Subscription : Disposable { } func dispose() { - lock.performLocked { - guard let subject = subject else { - return - } - - guard let key = key else { - return + _lock.performLocked { + guard let subject = subject, + let key = key else { + return } self.subject = nil diff --git a/RxSwift/Subjects/Variable.swift b/RxSwift/Subjects/Variable.swift index c8f810ed..2265cf36 100644 --- a/RxSwift/Subjects/Variable.swift +++ b/RxSwift/Subjects/Variable.swift @@ -18,7 +18,7 @@ public class Variable : ObservableType { let subject: BehaviorSubject - private var lock = SpinLock() + private var _lock = SpinLock() // state private var _value: E @@ -32,12 +32,12 @@ public class Variable : ObservableType { */ public var value: E { get { - return lock.calculateLocked { + return _lock.calculateLocked { return _value } } set(newValue) { - lock.performLocked { + _lock.performLocked { _value = newValue } self.subject.on(.Next(newValue)) @@ -50,7 +50,7 @@ public class Variable : ObservableType { - parameter value: Initial variable value. */ public init(_ value: Element) { - self._value = value + _value = value self.subject = BehaviorSubject(value: value) } From 4110362376e41f156621e7eff8721572464092a0 Mon Sep 17 00:00:00 2001 From: yury Date: Sat, 3 Oct 2015 15:06:43 +0300 Subject: [PATCH 002/210] Private _lock at ReplaySubscription and RecursiveScheduler --- RxSwift/Schedulers/RecursiveScheduler.swift | 26 ++++++++++----------- RxSwift/Subjects/ReplaySubject.swift | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/RxSwift/Schedulers/RecursiveScheduler.swift b/RxSwift/Schedulers/RecursiveScheduler.swift index 38e9b898..75cfc16c 100644 --- a/RxSwift/Schedulers/RecursiveScheduler.swift +++ b/RxSwift/Schedulers/RecursiveScheduler.swift @@ -31,10 +31,10 @@ Type erased recursive scheduler. public class RecursiveSchedulerOf { typealias Action = (state: State, scheduler: RecursiveSchedulerOf) -> Void - let lock = NSRecursiveLock() + private let _lock = NSRecursiveLock() // state - let group = CompositeDisposable() + private let group = CompositeDisposable() var action: Action? @@ -70,7 +70,7 @@ public class RecursiveSchedulerOf { return NopDisposable.instance } - let action = self.lock.calculateLocked { () -> Action? in + let action = self._lock.calculateLocked { () -> Action? in if isAdded { self.group.removeDisposable(removeKey!) } @@ -88,7 +88,7 @@ public class RecursiveSchedulerOf { return NopDisposable.instance } - lock.performLocked { + _lock.performLocked { if !isDone { removeKey = group.addDisposable(d) isAdded = true @@ -113,7 +113,7 @@ public class RecursiveSchedulerOf { return NopDisposable.instance } - let action = self.lock.calculateLocked { () -> Action? in + let action = self._lock.calculateLocked { () -> Action? in if isAdded { self.group.removeDisposable(removeKey!) } @@ -131,7 +131,7 @@ public class RecursiveSchedulerOf { return NopDisposable.instance } - lock.performLocked { + _lock.performLocked { if !isDone { removeKey = group.addDisposable(d) isAdded = true @@ -140,10 +140,10 @@ public class RecursiveSchedulerOf { } func dispose() { - self.lock.performLocked { + _lock.performLocked { self.action = nil } - self.group.dispose() + group.dispose() } } @@ -153,7 +153,7 @@ Type erased recursive scheduler. public class RecursiveImmediateSchedulerOf { typealias Action = (state: State, recurse: State -> Void) -> Void - var lock = SpinLock() + private var _lock = SpinLock() let group = CompositeDisposable() var action: Action? @@ -183,7 +183,7 @@ public class RecursiveImmediateSchedulerOf { return NopDisposable.instance } - let action = self.lock.calculateLocked { () -> Action? in + let action = self._lock.calculateLocked { () -> Action? in if isAdded { self.group.removeDisposable(removeKey!) } @@ -201,7 +201,7 @@ public class RecursiveImmediateSchedulerOf { return NopDisposable.instance } - lock.performLocked { + _lock.performLocked { if !isDone { removeKey = group.addDisposable(d) isAdded = true @@ -210,9 +210,9 @@ public class RecursiveImmediateSchedulerOf { } func dispose() { - self.lock.performLocked { + _lock.performLocked { self.action = nil } - self.group.dispose() + group.dispose() } } \ No newline at end of file diff --git a/RxSwift/Subjects/ReplaySubject.swift b/RxSwift/Subjects/ReplaySubject.swift index 7fcf3f32..72f222a6 100644 --- a/RxSwift/Subjects/ReplaySubject.swift +++ b/RxSwift/Subjects/ReplaySubject.swift @@ -236,7 +236,7 @@ class ReplaySubscription : Disposable { typealias Subject = ReplaySubject typealias DisposeKey = ReplayBufferBase.DisposeKey - var lock = SpinLock() + private var _lock = SpinLock() // state var subject: Subject? @@ -248,7 +248,7 @@ class ReplaySubscription : Disposable { } func dispose() { - let oldState = lock.calculateLocked { () -> (Subject?, DisposeKey?) in + let oldState = _lock.calculateLocked { () -> (Subject?, DisposeKey?) in let state = (self.subject, self.disposeKey) self.subject = nil self.disposeKey = nil From eaddcb2902ceb9d62a3b3943cd0112df4fe80263 Mon Sep 17 00:00:00 2001 From: yury Date: Sat, 3 Oct 2015 15:18:12 +0300 Subject: [PATCH 003/210] prefix private vars in Disposables --- RxSwift/Disposables/AnonymousDisposable.swift | 8 +++--- RxSwift/Disposables/BinaryDisposable.swift | 16 ++++++------ RxSwift/Disposables/CompositeDisposable.swift | 26 +++++++++---------- RxSwift/Disposables/ScopedDisposable.swift | 8 +++--- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/RxSwift/Disposables/AnonymousDisposable.swift b/RxSwift/Disposables/AnonymousDisposable.swift index 0b9bbe4b..dffdd30d 100644 --- a/RxSwift/Disposables/AnonymousDisposable.swift +++ b/RxSwift/Disposables/AnonymousDisposable.swift @@ -17,7 +17,7 @@ public final class AnonymousDisposable : DisposeBase, Cancelable { public typealias DisposeAction = () -> Void private var _disposed: Int32 = 0 - private var disposeAction: DisposeAction? + private var _disposeAction: DisposeAction? /** - returns: Was resource disposed. @@ -34,7 +34,7 @@ public final class AnonymousDisposable : DisposeBase, Cancelable { - parameter disposeAction: Disposal action which will be run upon calling `dispose`. */ public init(_ disposeAction: DisposeAction) { - self.disposeAction = disposeAction + _disposeAction = disposeAction super.init() } @@ -45,8 +45,8 @@ public final class AnonymousDisposable : DisposeBase, Cancelable { */ public func dispose() { if OSAtomicCompareAndSwap32(0, 1, &_disposed) { - if let action = self.disposeAction { - self.disposeAction = nil + if let action = _disposeAction { + _disposeAction = nil action() } } diff --git a/RxSwift/Disposables/BinaryDisposable.swift b/RxSwift/Disposables/BinaryDisposable.swift index b4417350..35a4d941 100644 --- a/RxSwift/Disposables/BinaryDisposable.swift +++ b/RxSwift/Disposables/BinaryDisposable.swift @@ -16,8 +16,8 @@ public final class BinaryDisposable : DisposeBase, Cancelable { private var _disposed: Int32 = 0 // state - private var disposable1: Disposable? - private var disposable2: Disposable? + private var _disposable1: Disposable? + private var _disposable2: Disposable? /** - returns: Was resource disposed. @@ -35,8 +35,8 @@ public final class BinaryDisposable : DisposeBase, Cancelable { - parameter disposable2: Second disposable */ init(_ disposable1: Disposable, _ disposable2: Disposable) { - self.disposable1 = disposable1 - self.disposable2 = disposable2 + _disposable1 = disposable1 + _disposable2 = disposable2 super.init() } @@ -47,10 +47,10 @@ public final class BinaryDisposable : DisposeBase, Cancelable { */ public func dispose() { if OSAtomicCompareAndSwap32(0, 1, &_disposed) { - disposable1?.dispose() - disposable2?.dispose() - disposable1 = nil - disposable2 = nil + _disposable1?.dispose() + _disposable2?.dispose() + _disposable1 = nil + _disposable2 = nil } } } \ No newline at end of file diff --git a/RxSwift/Disposables/CompositeDisposable.swift b/RxSwift/Disposables/CompositeDisposable.swift index ac772c13..c49bad3c 100644 --- a/RxSwift/Disposables/CompositeDisposable.swift +++ b/RxSwift/Disposables/CompositeDisposable.swift @@ -17,12 +17,12 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable { private var _lock = SpinLock() // state - private var disposables: Bag? = Bag() + private var _disposables: Bag? = Bag() public var disposed: Bool { get { return _lock.calculateLocked { - return disposables == nil + return _disposables == nil } } } @@ -34,17 +34,17 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable { Initializes a new instance of composite disposable with the specified number of disposables. */ public init(_ disposable1: Disposable, _ disposable2: Disposable) { - disposables!.insert(disposable1) - disposables!.insert(disposable2) + _disposables!.insert(disposable1) + _disposables!.insert(disposable2) } /** Initializes a new instance of composite disposable with the specified number of disposables. */ public init(_ disposable1: Disposable, _ disposable2: Disposable, _ disposable3: Disposable) { - disposables!.insert(disposable1) - disposables!.insert(disposable2) - disposables!.insert(disposable3) + _disposables!.insert(disposable1) + _disposables!.insert(disposable2) + _disposables!.insert(disposable3) } /** @@ -52,7 +52,7 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable { */ public init(disposables: [Disposable]) { for disposable in disposables { - self.disposables!.insert(disposable) + _disposables!.insert(disposable) } } @@ -67,7 +67,7 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable { // this should be let // bucause of compiler bug it's var let key = _lock.calculateLocked { () -> DisposeKey? in - return disposables?.insert(disposable) + return _disposables?.insert(disposable) } if key == nil { @@ -83,7 +83,7 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable { public var count: Int { get { return _lock.calculateLocked { - return disposables?.count ?? 0 + return _disposables?.count ?? 0 } } } @@ -95,7 +95,7 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable { */ public func removeDisposable(disposeKey: DisposeKey) { let disposable = _lock.calculateLocked { () -> Disposable? in - return disposables?.removeKey(disposeKey) + return _disposables?.removeKey(disposeKey) } if let disposable = disposable { @@ -108,8 +108,8 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable { */ public func dispose() { let oldDisposables = _lock.calculateLocked { () -> Bag? in - let disposeBag = disposables - self.disposables = nil + let disposeBag = _disposables + _disposables = nil return disposeBag } diff --git a/RxSwift/Disposables/ScopedDisposable.swift b/RxSwift/Disposables/ScopedDisposable.swift index d9d418a8..6c44b164 100644 --- a/RxSwift/Disposables/ScopedDisposable.swift +++ b/RxSwift/Disposables/ScopedDisposable.swift @@ -33,7 +33,7 @@ extension Disposable { This returns ARC (RAII) like resource management to `RxSwift`. */ public class ScopedDisposable : DisposeBase { - private var disposable: Disposable? + private var _disposable: Disposable? /** Initializes new instance with a single disposable. @@ -41,17 +41,17 @@ public class ScopedDisposable : DisposeBase { - parameter disposable: `Disposable` that will be disposed on scope exit. */ public init(disposable: Disposable) { - self.disposable = disposable + _disposable = disposable } /** This is intentionally private. */ func dispose() { - self.disposable?.dispose() + _disposable?.dispose() } deinit { - self.dispose() + dispose() } } \ No newline at end of file From 96d7035086a7d83a4ac93e97372cf6d97fc79f11 Mon Sep 17 00:00:00 2001 From: yury Date: Sat, 3 Oct 2015 16:05:18 +0300 Subject: [PATCH 004/210] prefix private vars in Observables implementations --- RxSwift/Observables/Implementations/Amb.swift | 12 ++-- .../Implementations/AnonymousObservable.swift | 6 +- .../Observables/Implementations/Buffer.swift | 6 +- .../Observables/Implementations/Catch.swift | 26 +++---- .../Implementations/Deferred.swift | 12 ++-- .../Implementations/FailWith.swift | 6 +- .../Observables/Implementations/Just.swift | 6 +- .../Implementations/Zip+CollectionType.swift | 70 +++++++++---------- RxSwift/Observables/Implementations/Zip.swift | 48 ++++++------- 9 files changed, 96 insertions(+), 96 deletions(-) diff --git a/RxSwift/Observables/Implementations/Amb.swift b/RxSwift/Observables/Implementations/Amb.swift index ee5ef3d4..10751aab 100644 --- a/RxSwift/Observables/Implementations/Amb.swift +++ b/RxSwift/Observables/Implementations/Amb.swift @@ -54,9 +54,9 @@ class AmbSink : Sink { let parent: Parent - let lock = NSRecursiveLock() + private let _lock = NSRecursiveLock() // state - var choice = AmbState.Neither + private var _choice = AmbState.Neither init(parent: Parent, observer: O, cancel: Disposable) { self.parent = parent @@ -73,15 +73,15 @@ class AmbSink : Sink { } let decide = { (o: AmbObserverType, event: Event, me: AmbState, otherSubscription: Disposable) in - self.lock.performLocked { - if self.choice == .Neither { - self.choice = me + self._lock.performLocked { + if self._choice == .Neither { + self._choice = me o.sink = forwardEvent o.cancel = disposeAll otherSubscription.dispose() } - if self.choice == me { + if self._choice == me { self.observer?.on(event) if event.isStopEvent { self.dispose() diff --git a/RxSwift/Observables/Implementations/AnonymousObservable.swift b/RxSwift/Observables/Implementations/AnonymousObservable.swift index 55f62bdc..87a59a28 100644 --- a/RxSwift/Observables/Implementations/AnonymousObservable.swift +++ b/RxSwift/Observables/Implementations/AnonymousObservable.swift @@ -14,7 +14,7 @@ class AnonymousObservableSink : Sink, ObserverType { typealias Parent = AnonymousObservable // state - var isStopped: Int32 = 0 + private var _isStopped: Int32 = 0 override init(observer: O, cancel: Disposable) { super.init(observer: observer, cancel: cancel) @@ -23,12 +23,12 @@ class AnonymousObservableSink : Sink, ObserverType { func on(event: Event) { switch event { case .Next: - if isStopped == 1 { + if _isStopped == 1 { return } self.observer?.on(event) case .Error, .Completed: - if OSAtomicCompareAndSwap32(0, 1, &isStopped) { + if OSAtomicCompareAndSwap32(0, 1, &_isStopped) { self.observer?.on(event) self.dispose() } diff --git a/RxSwift/Observables/Implementations/Buffer.swift b/RxSwift/Observables/Implementations/Buffer.swift index 3f7205a8..4e0a7fe0 100644 --- a/RxSwift/Observables/Implementations/Buffer.swift +++ b/RxSwift/Observables/Implementations/Buffer.swift @@ -34,7 +34,7 @@ class BufferTimeCountSink) { - self.lock.performLocked { + _lock.performLocked { switch event { case .Next(let element): buffer.append(element) @@ -94,7 +94,7 @@ class BufferTimeCountSink : ObserverType { typealias E = O.E typealias Parent = CatchSink - let parent: Parent + private let _parent: Parent init(parent: Parent) { - self.parent = parent + _parent = parent } func on(event: Event) { - parent.observer?.on(event) + _parent.observer?.on(event) switch event { case .Next: break case .Error: - parent.dispose() + _parent.dispose() case .Completed: - parent.dispose() + _parent.dispose() } } } @@ -38,20 +38,20 @@ class CatchSink : Sink, ObserverType { typealias E = O.E typealias Parent = Catch - let parent: Parent - let subscription = SerialDisposable() + private let _parent: Parent + private let _subscription = SerialDisposable() init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { let d1 = SingleAssignmentDisposable() - subscription.disposable = d1 - d1.disposable = parent.source.subscribeSafe(self) + _subscription.disposable = d1 + d1.disposable = _parent.source.subscribeSafe(self) - return subscription + return _subscription } func on(event: Event) { @@ -63,11 +63,11 @@ class CatchSink : Sink, ObserverType { self.dispose() case .Error(let error): do { - let catchSequence = try parent.handler(error) + let catchSequence = try _parent.handler(error) let observer = CatchSinkProxy(parent: self) - subscription.disposable = catchSequence.subscribeSafe(observer) + _subscription.disposable = catchSequence.subscribeSafe(observer) } catch let e { observer?.on(.Error(e)) diff --git a/RxSwift/Observables/Implementations/Deferred.swift b/RxSwift/Observables/Implementations/Deferred.swift index 88dec766..b9751fc8 100644 --- a/RxSwift/Observables/Implementations/Deferred.swift +++ b/RxSwift/Observables/Implementations/Deferred.swift @@ -12,16 +12,16 @@ class DeferredSink : Sink, ObserverType { typealias E = O.E typealias Parent = Deferred - let parent: Parent + private let _parent: Parent init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { do { - let result = try parent.eval() + let result = try _parent.eval() return result.subscribeSafe(self) } catch let e { @@ -48,10 +48,10 @@ class DeferredSink : Sink, ObserverType { class Deferred : Producer { typealias Factory = () throws -> Observable - let observableFactory : Factory + private let _observableFactory : Factory init(observableFactory: Factory) { - self.observableFactory = observableFactory + _observableFactory = observableFactory } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { @@ -61,6 +61,6 @@ class Deferred : Producer { } func eval() throws -> Observable { - return try observableFactory() + return try _observableFactory() } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/FailWith.swift b/RxSwift/Observables/Implementations/FailWith.swift index 3a36621a..8350175b 100644 --- a/RxSwift/Observables/Implementations/FailWith.swift +++ b/RxSwift/Observables/Implementations/FailWith.swift @@ -9,14 +9,14 @@ import Foundation class FailWith : Producer { - let error: ErrorType + private let _error: ErrorType init(error: ErrorType) { - self.error = error + _error = error } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - observer.on(.Error(error)) + observer.on(.Error(_error)) return NopDisposable.instance } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Just.swift b/RxSwift/Observables/Implementations/Just.swift index bb7453ec..1218bbb1 100644 --- a/RxSwift/Observables/Implementations/Just.swift +++ b/RxSwift/Observables/Implementations/Just.swift @@ -9,14 +9,14 @@ import Foundation class Just : Producer { - let element: Element + private let _element: Element init(element: Element) { - self.element = element + _element = element } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - observer.on(.Next(element)) + observer.on(.Next(_element)) observer.on(.Completed) return NopDisposable.instance } diff --git a/RxSwift/Observables/Implementations/Zip+CollectionType.swift b/RxSwift/Observables/Implementations/Zip+CollectionType.swift index 15d4d685..37ae4bd2 100644 --- a/RxSwift/Observables/Implementations/Zip+CollectionType.swift +++ b/RxSwift/Observables/Implementations/Zip+CollectionType.swift @@ -12,44 +12,44 @@ class ZipCollectionTypeSink typealias SourceElement = C.Generator.Element.E - let parent: Parent + private let _parent: Parent - let lock = NSRecursiveLock() + private let _lock = NSRecursiveLock() // state - var numberOfValues = 0 - var values: [Queue] - var isDone: [Bool] - var numberOfDone = 0 - var subscriptions: [SingleAssignmentDisposable] + private var _numberOfValues = 0 + private var _values: [Queue] + private var _isDone: [Bool] + private var _numberOfDone = 0 + private var _subscriptions: [SingleAssignmentDisposable] init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent - self.values = [Queue](count: parent.count, repeatedValue: Queue(capacity: 4)) - self.isDone = [Bool](count: parent.count, repeatedValue: false) - self.subscriptions = Array() - self.subscriptions.reserveCapacity(parent.count) + _parent = parent + _values = [Queue](count: parent.count, repeatedValue: Queue(capacity: 4)) + _isDone = [Bool](count: parent.count, repeatedValue: false) + _subscriptions = Array() + _subscriptions.reserveCapacity(parent.count) for _ in 0 ..< parent.count { - self.subscriptions.append(SingleAssignmentDisposable()) + _subscriptions.append(SingleAssignmentDisposable()) } super.init(observer: observer, cancel: cancel) } func on(event: Event, atIndex: Int) { - lock.performLocked { + _lock.performLocked { switch event { case .Next(let element): - values[atIndex].enqueue(element) + _values[atIndex].enqueue(element) - if values[atIndex].count == 1 { - numberOfValues++ + if _values[atIndex].count == 1 { + _numberOfValues++ } - if numberOfValues < parent.count { - let numberOfOthersThatAreDone = self.numberOfDone - (isDone[atIndex] ? 1 : 0) - if numberOfOthersThatAreDone == self.parent.count - 1 { + if _numberOfValues < _parent.count { + let numberOfOthersThatAreDone = _numberOfDone - (_isDone[atIndex] ? 1 : 0) + if numberOfOthersThatAreDone == _parent.count - 1 { self.observer?.on(.Completed) self.dispose() } @@ -58,19 +58,19 @@ class ZipCollectionTypeSink 0 { - numberOfValues++ + for i in 0 ..< _values.count { + arguments.append(_values[i].dequeue()) + if _values[i].count > 0 { + _numberOfValues++ } } - let result = try parent.resultSelector(arguments) + let result = try _parent.resultSelector(arguments) self.observer?.on(.Next(result)) } catch let error { @@ -82,19 +82,19 @@ class ZipCollectionTypeSink Disposable { var j = 0 - for i in parent.sources.startIndex ..< parent.sources.endIndex { + for i in _parent.sources.startIndex ..< _parent.sources.endIndex { let index = j - self.subscriptions[j].disposable = self.parent.sources[i].subscribeSafe(ObserverOf { event in + _subscriptions[j].disposable = _parent.sources[i].subscribeSafe(ObserverOf { event in self.on(event, atIndex: index) }) j++ } - return CompositeDisposable(disposables: self.subscriptions.map { $0 }) + return CompositeDisposable(disposables: _subscriptions.map { $0 }) } } diff --git a/RxSwift/Observables/Implementations/Zip.swift b/RxSwift/Observables/Implementations/Zip.swift index 1b008a98..b16534fd 100644 --- a/RxSwift/Observables/Implementations/Zip.swift +++ b/RxSwift/Observables/Implementations/Zip.swift @@ -23,10 +23,10 @@ class ZipSink : Sink, ZipSinkProtocol { let lock = NSRecursiveLock() // state - var isDone: [Bool] + private var _isDone: [Bool] init(arity: Int, observer: O, cancel: Disposable) { - self.isDone = [Bool](count: arity, repeatedValue: false) + _isDone = [Bool](count: arity, repeatedValue: false) self.arity = arity super.init(observer: observer, cancel: cancel) @@ -63,9 +63,9 @@ class ZipSink : Sink, ZipSinkProtocol { else { var allOthersDone = true - let arity = self.isDone.count + let arity = _isDone.count for var i = 0; i < arity; ++i { - if i != index && !isDone[i] { + if i != index && !_isDone[i] { allOthersDone = false break } @@ -84,11 +84,11 @@ class ZipSink : Sink, ZipSinkProtocol { } func done(index: Int) { - isDone[index] = true + _isDone[index] = true var allDone = true - for done in self.isDone { + for done in _isDone { if !done { allDone = false break @@ -106,46 +106,46 @@ class ZipObserver : ObserverType { typealias E = ElementType typealias ValueSetter = (ElementType) -> () - var parent: ZipSinkProtocol? + private var _parent: ZipSinkProtocol? - let lock: NSRecursiveLock + private let _lock: NSRecursiveLock // state - let index: Int - let this: Disposable - let setNextValue: ValueSetter + private let _index: Int + private let _this: Disposable + private let _setNextValue: ValueSetter init(lock: NSRecursiveLock, parent: ZipSinkProtocol, index: Int, setNextValue: ValueSetter, this: Disposable) { - self.lock = lock - self.parent = parent - self.index = index - self.this = this - self.setNextValue = setNextValue + _lock = lock + _parent = parent + _index = index + _this = this + _setNextValue = setNextValue } func on(event: Event) { - if let _ = parent { + if let _ = _parent { switch event { case .Next(_): break case .Error(_): - this.dispose() + _this.dispose() case .Completed: - this.dispose() + _this.dispose() } } - lock.performLocked { - if let parent = parent { + _lock.performLocked { + if let parent = _parent { switch event { case .Next(let value): - setNextValue(value) - parent.next(index) + _setNextValue(value) + parent.next(_index) case .Error(let error): parent.fail(error) case .Completed: - parent.done(index) + parent.done(_index) } } } From ef54330643f2681f0312a43bae5c2853fd61fa51 Mon Sep 17 00:00:00 2001 From: yury Date: Sat, 3 Oct 2015 16:32:56 +0300 Subject: [PATCH 005/210] private var in DoSink --- RxSwift/Observables/Implementations/Do.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RxSwift/Observables/Implementations/Do.swift b/RxSwift/Observables/Implementations/Do.swift index b7028e42..bfd15bdd 100644 --- a/RxSwift/Observables/Implementations/Do.swift +++ b/RxSwift/Observables/Implementations/Do.swift @@ -12,16 +12,16 @@ class DoSink : Sink, ObserverType { typealias Element = O.E typealias Parent = Do - let parent: Parent + private let _parent: Parent init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } func on(event: Event) { do { - try parent.eventHandler(event) + try _parent.eventHandler(event) observer?.on(event) if event.isStopEvent { self.dispose() From a8cd83e9566626ee202f128b378e7f291011f773 Mon Sep 17 00:00:00 2001 From: samgreen Date: Wed, 7 Oct 2015 08:17:47 -0700 Subject: [PATCH 006/210] Fixed a typo, used the type alias everywhere, and reorganized a few minor things for readability. --- .../Views/GitHubSignupViewController.swift | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift b/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift index 09eb1755..7acc2f9f 100644 --- a/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift +++ b/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift @@ -13,9 +13,6 @@ import RxSwift import RxCocoa #endif -let okColor = UIColor(red: 138.0 / 255.0, green: 221.0 / 255.0, blue: 109.0 / 255.0, alpha: 1.0) -let errorColor = UIColor.redColor() - typealias ValidationResult = (valid: Bool?, message: String?) typealias ValidationObservable = Observable @@ -30,7 +27,7 @@ class ValidationService { let minPasswordCount = 5 - func validateUsername(username: String) -> Observable { + func validateUsername(username: String) -> ValidationObservable { if username.characters.count == 0 { return just((false, nil)) } @@ -94,6 +91,9 @@ class GitHubSignupViewController : ViewController { @IBOutlet weak var signupOutlet: UIButton! @IBOutlet weak var signingUpOulet: UIActivityIndicatorView! + let okColor = UIColor(red: 138.0 / 255.0, green: 221.0 / 255.0, blue: 109.0 / 255.0, alpha: 1.0) + let errorColor = UIColor.redColor() + var disposeBag = DisposeBag() let API = GitHubAPI( @@ -109,8 +109,7 @@ class GitHubSignupViewController : ViewController { if let valid = v.valid { validationColor = valid ? okColor : errorColor - } - else { + } else { validationColor = UIColor.grayColor() } @@ -128,7 +127,6 @@ class GitHubSignupViewController : ViewController { super.viewDidLoad() let tapBackground = UITapGestureRecognizer(target: self, action: Selector("dismissKeyboard:")) - tapBackground.numberOfTouchesRequired = 1 view.addGestureRecognizer(tapBackground) self.disposeBag = DisposeBag() @@ -140,7 +138,7 @@ class GitHubSignupViewController : ViewController { let username = usernameOutlet.rx_text let password = passwordOutlet.rx_text let repeatPassword = repeatedPasswordOutlet.rx_text - let signupSampler = self.signupOutlet.rx_tap + let signupSampler = signupOutlet.rx_tap let usernameValidation = username .map { username in @@ -174,8 +172,11 @@ class GitHubSignupViewController : ViewController { passwordValidation, repeatPasswordValidation, signingProcess - ) { un, p, pr, signingState in - return (un.valid ?? false) && (p.valid ?? false) && (pr.valid ?? false) && signingState != SignupState.SigningUp + ) { username, password, repeatPassword, signingState in + return (username.valid ?? false) && + (password.valid ?? false) && + (repeatPassword.valid ?? false) && + signingState != SignupState.SigningUp } bindValidationResultToUI( @@ -224,11 +225,12 @@ class GitHubSignupViewController : ViewController { // This is one of the reasons why it's a good idea for disposal to be detached from allocations. // If resources weren't disposed before view controller is being deallocated, signup alert view - // could be presented on top of wrong screen or crash your app if it was being presented while - // navigation stack is popping. + // could be presented on top of the wrong screen or could crash your app if it was being presented + // while navigation stack is popping. + // This will work well with UINavigationController, but has an assumption that view controller will // never be readded as a child view controller. - // It it was readded UI wouldn't be bound anymore. + // If it was readded the UI wouldn't be bound anymore. override func willMoveToParentViewController(parent: UIViewController?) { if let parent = parent { assert(parent.isKindOfClass(UINavigationController), "Please read comments") From 5bbd23e64faea1d2019a8325b95b27b95841e855 Mon Sep 17 00:00:00 2001 From: yury Date: Thu, 8 Oct 2015 15:32:26 +0300 Subject: [PATCH 007/210] Add SkipUntil ohter and SkipUnit time rx operators --- Rx.xcodeproj/project.pbxproj | 8 + .../Implementations/SkipUntil.swift | 191 ++++++++++ RxSwift/Observables/Observable+Multiple.swift | 16 + RxSwift/Observables/Observable+Time.swift | 8 + .../Tests/Observable+MultipleTest.swift | 351 ++++++++++++++++++ .../Tests/Observable+TimeTest.swift | 167 ++++++++- 6 files changed, 740 insertions(+), 1 deletion(-) create mode 100644 RxSwift/Observables/Implementations/SkipUntil.swift diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index cec55033..9215be63 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -540,6 +540,9 @@ D2138C971BB9BEE700339B5C /* RxCLLocationManagerDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E9A1B8A732E0088E94D /* RxCLLocationManagerDelegateProxy.swift */; }; D2138C981BB9BEEE00339B5C /* RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E9B1B8A732E0088E94D /* RxCocoa.swift */; }; D2138C991BB9BEEE00339B5C /* RxTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E9C1B8A732E0088E94D /* RxTarget.swift */; }; + D2752D621BC5551A0070C418 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D285BAC31BC0231000B3F602 /* SkipUntil.swift */; }; + D2752D631BC5551B0070C418 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D285BAC31BC0231000B3F602 /* SkipUntil.swift */; }; + D285BAC41BC0231000B3F602 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D285BAC31BC0231000B3F602 /* SkipUntil.swift */; }; D2EBEADC1BB9B697003A27DC /* Cancelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C491B8A72BE0088E94D /* Cancelable.swift */; }; D2EBEADD1BB9B697003A27DC /* ConnectableObservableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C4D1B8A72BE0088E94D /* ConnectableObservableType.swift */; }; D2EBEADE1BB9B697003A27DC /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C521B8A72BE0088E94D /* Disposable.swift */; }; @@ -897,6 +900,7 @@ C8F0C04B1BBBFBB9001B112F /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8F0C0581BBBFBCE001B112F /* RxBlocking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxBlocking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D2138C751BB9BE9800339B5C /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D285BAC31BC0231000B3F602 /* SkipUntil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipUntil.swift; sourceTree = ""; }; D2EA280C1BB9B5A200880ED3 /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D2EBEB811BB9B99D003A27DC /* RxBlocking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxBlocking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F31F35AF1BB4FED800961002 /* UIStepper+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStepper+Rx.swift"; sourceTree = ""; }; @@ -1116,6 +1120,7 @@ C8093C871B8A72BE0088E94D /* Scan.swift */, C8093C881B8A72BE0088E94D /* Sink.swift */, C8093C891B8A72BE0088E94D /* Skip.swift */, + D285BAC31BC0231000B3F602 /* SkipUntil.swift */, C8093C8A1B8A72BE0088E94D /* StartWith.swift */, C8093C8B1B8A72BE0088E94D /* SubscribeOn.swift */, C8093C8C1B8A72BE0088E94D /* Switch.swift */, @@ -2002,6 +2007,7 @@ C8093CD81B8A72BE0088E94D /* BinaryDisposable.swift in Sources */, C8093D2A1B8A72BE0088E94D /* ObserveOn.swift in Sources */, C8093D361B8A72BE0088E94D /* Sample.swift in Sources */, + D2752D621BC5551A0070C418 /* SkipUntil.swift in Sources */, C8093CEA1B8A72BE0088E94D /* ScopedDisposable.swift in Sources */, C8093D261B8A72BE0088E94D /* Multicast.swift in Sources */, C8C3DA101B939767004D233E /* CurrentThreadScheduler.swift in Sources */, @@ -2114,6 +2120,7 @@ C8093CD71B8A72BE0088E94D /* BinaryDisposable.swift in Sources */, C8093D291B8A72BE0088E94D /* ObserveOn.swift in Sources */, C8093D351B8A72BE0088E94D /* Sample.swift in Sources */, + D285BAC41BC0231000B3F602 /* SkipUntil.swift in Sources */, C8093CE91B8A72BE0088E94D /* ScopedDisposable.swift in Sources */, C8093D251B8A72BE0088E94D /* Multicast.swift in Sources */, C8C3DA0F1B939767004D233E /* CurrentThreadScheduler.swift in Sources */, @@ -2472,6 +2479,7 @@ D2EBEB2A1BB9B6C5003A27DC /* Zip+CollectionType.swift in Sources */, D2EBEB401BB9B6DE003A27DC /* BehaviorSubject.swift in Sources */, D2EBEB271BB9B6C1003A27DC /* Timer.swift in Sources */, + D2752D631BC5551B0070C418 /* SkipUntil.swift in Sources */, D2EBEB351BB9B6D2003A27DC /* ObserverBase.swift in Sources */, D2EBEB0F1BB9B6C1003A27DC /* Generate.swift in Sources */, D2EBEB1F1BB9B6C1003A27DC /* Skip.swift in Sources */, diff --git a/RxSwift/Observables/Implementations/SkipUntil.swift b/RxSwift/Observables/Implementations/SkipUntil.swift new file mode 100644 index 00000000..d5c3b7f8 --- /dev/null +++ b/RxSwift/Observables/Implementations/SkipUntil.swift @@ -0,0 +1,191 @@ +// +// SkipUntil.swift +// Rx +// +// Created by Yury Korolev on 10/3/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +class SkipUntilSinkOther : ObserverType { + typealias Parent = SkipUntilSink + typealias E = Other + + private let _parent: Parent + + private let _singleAssignmentDisposable = SingleAssignmentDisposable() + + var disposable: Disposable { + get { + return abstractMethod() + } + set { + _singleAssignmentDisposable.disposable = newValue + } + } + + init(parent: Parent) { + _parent = parent + #if TRACE_RESOURCES + OSAtomicIncrement32(&resourceCount) + #endif + } + + func on(event: Event) { + // Do we need lock here? + _parent.lock.performLocked { + switch event { + case .Next: + _parent.__observer = _parent.observer + _singleAssignmentDisposable.dispose() + case .Error(let e): + _parent._observer?.onError(e) + _parent.dispose() + case .Completed: + _singleAssignmentDisposable.dispose() + } + } + } + + #if TRACE_RESOURCES + deinit { + OSAtomicDecrement32(&resourceCount) + } + #endif + +} + + +class SkipUntilSink : Sink, ObserverType { + typealias E = ElementType + typealias Parent = SkipUntil + + private let _parent: Parent + let lock = NSRecursiveLock() + var __observer: O? // Nop observer for start. Need better name + + private let _singleAssignmentDisposable = SingleAssignmentDisposable() + + var disposable: Disposable { + get { + return abstractMethod() + } + set { + _singleAssignmentDisposable.disposable = newValue + } + } + + init(parent: Parent, observer: O, cancel: Disposable) { + _parent = parent + super.init(observer: observer, cancel: cancel) + } + + func on(event: Event) { + switch event { + case .Next: + __observer?.on(event) + case .Error: + observer?.on(event) + dispose() + case .Completed: + __observer?.on(event) + _singleAssignmentDisposable.dispose() + } + } + + func run() -> Disposable { + let sourceSubscription = _parent._source.subscribeSafe(self) + let otherObserver = SkipUntilSinkOther(parent: self) + let otherSubscription = _parent._other.subscribeSafe(otherObserver) + disposable = sourceSubscription + otherObserver.disposable = otherSubscription + + return BinaryDisposable(sourceSubscription, otherSubscription) + } +} + +class SkipUntil: Producer { + + private let _source: Observable + private let _other: Observable + + init(source: Observable, other: Observable) { + _source = source + _other = other + } + + override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + let sink = SkipUntilSink(parent: self, observer: observer, cancel: cancel) + setSink(sink) + return sink.run() + } +} + +// MARK: SkipUntil time + +class SkipUntilTimeSink : Sink, ObserverType { + + typealias E = ElementType + typealias Parent = SkipUntilTime + + private let _parent: Parent + + // state + private var _open: Bool = false + + init(parent: Parent, observer: O, cancel: Disposable) { + _parent = parent + super.init(observer: observer, cancel: cancel) + } + + func on(event: Event) { + switch(event) { + case let .Next(element): + if _open { + observer?.onNext(element) + } + case let .Error(error): + _observer?.onError(error) + dispose() + case .Completed: + _observer?.onComplete() + dispose() + } + } + + func run() -> Disposable { + // Actually it should be abs time here. Or diff from now + let disposeTimer = _parent._scheduler.scheduleRelative((), dueTime:_parent._startTime) { + self._tick() + return NopDisposable.instance + } + let disposeSubscription = _parent._source.subscribeSafe(self) + return BinaryDisposable(disposeTimer, disposeSubscription) + } + + private func _tick() { + _open = true + } +} + +class SkipUntilTime: Producer { + typealias TimeInterval = S.TimeInterval + + private let _source: Observable + private let _startTime: TimeInterval + private let _scheduler: S + + init(source: Observable, startTime: TimeInterval, scheduler: S) { + _source = source + _startTime = startTime + _scheduler = scheduler + } + + override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + let sink = SkipUntilTimeSink(parent: self, observer: observer, cancel: cancel) + setSink(sink) + return sink.run() + } +} + diff --git a/RxSwift/Observables/Observable+Multiple.swift b/RxSwift/Observables/Observable+Multiple.swift index ccf30b25..d9829dda 100644 --- a/RxSwift/Observables/Observable+Multiple.swift +++ b/RxSwift/Observables/Observable+Multiple.swift @@ -165,6 +165,22 @@ extension ObservableType { } } +// skipUntil + +extension ObservableType { + + /** + Returns the elements from the source observable sequence until the other observable sequence produces an element. + + - parameter other: Observable sequence that terminates propagation of elements of the source sequence. + - returns: An observable sequence containing the elements of the source sequence up to the point the other sequence interrupted further propagation. + */ + public func skipUntil(other: O) + -> Observable { + return SkipUntil(source: self.asObservable(), other: other.asObservable()) + } +} + // amb extension ObservableType { diff --git a/RxSwift/Observables/Observable+Time.swift b/RxSwift/Observables/Observable+Time.swift index b086b8c6..eec02c70 100644 --- a/RxSwift/Observables/Observable+Time.swift +++ b/RxSwift/Observables/Observable+Time.swift @@ -197,4 +197,12 @@ extension ObservableType { -> Observable<[E]> { return BufferTimeCount(source: self.asObservable(), timeSpan: timeSpan, count: count, scheduler: scheduler) } +} + +// SkipUntil time + +extension ObservableType { + public func skipUntil(startTime: S.TimeInterval, _ scheduler: S) -> Observable { + return SkipUntilTime(source: self.asObservable(), startTime: startTime, scheduler: scheduler) + } } \ No newline at end of file diff --git a/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift b/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift index ccdcee51..7de57089 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift @@ -3597,4 +3597,355 @@ extension ObservableMultipleTest { XCTAssertEqual(e2.subscriptions, [Subscription(200, 230)]) XCTAssertEqual(e3.subscriptions, [Subscription(200, 230)]) } +} + + +// MARK: skipUntil + +extension ObservableMultipleTest { + func testSkipUntil_SomeData_Next() { + let scheduler = TestScheduler(initialClock: 0) + + let l = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + next(220, 3), + next(230, 4), //! + next(240, 5), //! + completed(250) + ]) + + let r = scheduler.createHotObservable([ + next(150, 1), + next(225, 99), + completed(230) + ]) + + let res = scheduler.start { + l.skipUntil(r) + } + + XCTAssertEqual(res.messages, [ + next(230, 4), + next(240, 5), + completed(250) + ]) + + XCTAssertEqual(l.subscriptions, [ + Subscription(200, 250) + ]) + + XCTAssertEqual(r.subscriptions, [ + Subscription(200, 225) + ]) + } + + func testSkipUntil_SomeData_Error() { + let scheduler = TestScheduler(initialClock: 0) + + let l = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5), + completed(250) + ]) + + let r = scheduler.createHotObservable([ + next(150, 1), + error(225, testError) + ]) + + let res = scheduler.start { + l.skipUntil(r) + } + + XCTAssertEqual(res.messages, [ + error(225, testError), + ]) + + XCTAssertEqual(l.subscriptions, [ + Subscription(200, 225) + ]) + + XCTAssertEqual(r.subscriptions, [ + Subscription(200, 225) + ]) + } + + func testSkipUntil_Error_SomeData() { + let scheduler = TestScheduler(initialClock: 0) + + let l = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + error(220, testError) + + ]) + + let r = scheduler.createHotObservable([ + next(150, 1), + next(230, 2), + completed(250) + ]) + + let res = scheduler.start { + l.skipUntil(r) + } + + XCTAssertEqual(res.messages, [ + error(220, testError), + ]) + + XCTAssertEqual(l.subscriptions, [ + Subscription(200, 220) + ]) + + XCTAssertEqual(r.subscriptions, [ + Subscription(200, 220) + ]) + } + + func testSkipUntil_SomeData_Empty() { + let scheduler = TestScheduler(initialClock: 0) + + let l = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5), + completed(250) + ]) + + let r = scheduler.createHotObservable([ + next(150, 1), + completed(225) + ]) + + let res = scheduler.start { + l.skipUntil(r) + } + + XCTAssertEqual(res.messages, [ + ]) + + XCTAssertEqual(l.subscriptions, [ + Subscription(200, 250) + ]) + + XCTAssertEqual(r.subscriptions, [ + Subscription(200, 225) + ]) + } + + func testSkipUntil_Never_Next() { + let scheduler = TestScheduler(initialClock: 0) + + let l = scheduler.createHotObservable([ + next(150, 1) + ]) + + let r = scheduler.createHotObservable([ + next(150, 1), + next(225, 2), //! + completed(250) + ]) + + let res = scheduler.start { + l.skipUntil(r) + } + + XCTAssertEqual(res.messages, [ + ]) + + XCTAssertEqual(l.subscriptions, [ + Subscription(200, 1000) + ]) + + XCTAssertEqual(r.subscriptions, [ + Subscription(200, 225) + ]) + } + + func testSkipUntil_Never_Error1() { + let scheduler = TestScheduler(initialClock: 0) + + let l = scheduler.createHotObservable([ + next(150, 1) + ]) + + let r = scheduler.createHotObservable([ + next(150, 1), + error(225, testError) + ]) + + let res = scheduler.start { + l.skipUntil(r) + } + + XCTAssertEqual(res.messages, [ + error(225, testError) + ]) + + XCTAssertEqual(l.subscriptions, [ + Subscription(200, 225) + ]) + + XCTAssertEqual(r.subscriptions, [ + Subscription(200, 225) + ]) + } + + func testSkipUntil_SomeData_Error2() { + let scheduler = TestScheduler(initialClock: 0) + + let l = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5), + completed(250) + ]) + + let r = scheduler.createHotObservable([ + next(150, 1), + error(300, testError) + ]) + + let res = scheduler.start { + l.skipUntil(r) + } + + XCTAssertEqual(res.messages, [ + error(300, testError) + ]) + + XCTAssertEqual(l.subscriptions, [ + Subscription(200, 250) + ]) + + XCTAssertEqual(r.subscriptions, [ + Subscription(200, 300) + ]) + } + + func testSkipUntil_SomeData_Never() { + let scheduler = TestScheduler(initialClock: 0) + + let l = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5), + completed(250) + ]) + + let r = scheduler.createHotObservable([ + next(150, 1), + ]) + + let res = scheduler.start { + l.skipUntil(r) + } + + XCTAssertEqual(res.messages, [ + ]) + + XCTAssertEqual(l.subscriptions, [ + Subscription(200, 250) + ]) + + XCTAssertEqual(r.subscriptions, [ + Subscription(200, 1000) + ]) + } + + func testSkipUntil_Never_Empty() { + let scheduler = TestScheduler(initialClock: 0) + + let l = scheduler.createHotObservable([ + next(150, 1), + ]) + + let r = scheduler.createHotObservable([ + next(150, 1), + completed(225) + ]) + + let res = scheduler.start { + l.skipUntil(r) + } + + XCTAssertEqual(res.messages, [ + ]) + + XCTAssertEqual(l.subscriptions, [ + Subscription(200, 1000) + ]) + + XCTAssertEqual(r.subscriptions, [ + Subscription(200, 225) + ]) + } + + func testSkipUntil_Never_Never() { + let scheduler = TestScheduler(initialClock: 0) + + let l = scheduler.createHotObservable([ + next(150, 1), + ]) + + let r = scheduler.createHotObservable([ + next(150, 1), + ]) + + let res = scheduler.start { + l.skipUntil(r) + } + + XCTAssertEqual(res.messages, [ + ]) + + XCTAssertEqual(l.subscriptions, [ + Subscription(200, 1000) + ]) + + XCTAssertEqual(r.subscriptions, [ + Subscription(200, 1000) + ]) + } + + func testSkipUntil_HasCompletedCausesDisposal() { + let scheduler = TestScheduler(initialClock: 0) + + var disposed = false + + let l = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5), + completed(250) + ]) + + let r: Observable = create { o in + return AnonymousDisposable { + disposed = true + } + } + + let res = scheduler.start { + l.skipUntil(r) + } + + XCTAssertEqual(res.messages, [ + ]) + + XCTAssert(disposed, "disposed") + } } \ No newline at end of file diff --git a/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift b/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift index 15a043b5..29d8e0b0 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift @@ -957,7 +957,7 @@ extension ObservableTimeTest { ]) let res = scheduler.start { - xs.take(35, scheduler) + xs.take(55, scheduler).take(35, scheduler) } XCTAssertEqual(res.messages, [ @@ -1303,4 +1303,169 @@ extension ObservableTimeTest { XCTAssertEqual(result!, [4, 5, 6]) } + + // MARK: SkipUntil + + func testSkipUntil_Zero() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(210, 1), + next(220, 2), + completed(230) + ]) + + let res = scheduler.start() { + xs.skipUntil(0, scheduler) + } + + XCTAssertEqual(res.messages, [ + next(210, 1), + next(220, 2), + completed(230) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 230) + ]) + } + + func testSkipUntil_Some() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(210, 1), + next(220, 2), + completed(230) + ]) + + let res = scheduler.start() { + xs.skipUntil(215, scheduler) + } + + XCTAssertEqual(res.messages, [ + next(220, 2), + completed(230) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 230) + ]) + } + + func testSkipUntil_Late() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(210, 1), + next(220, 2), + completed(230) + ]) + + let res = scheduler.start() { + xs.skipUntil(250, scheduler) + } + + XCTAssertEqual(res.messages, [ + completed(230) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 230) + ]) + } + + func testSkipUntil_Error() { + let scheduler = TestScheduler(initialClock: 0) + + let xs:HotObservable = scheduler.createHotObservable([ + error(210, testError) + ]) + + let res = scheduler.start() { + xs.skipUntil(250, scheduler) + } + + XCTAssertEqual(res.messages, [ + error(210, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 210) + ]) + } + + func testSkipUntil_Never() { + let scheduler = TestScheduler(initialClock: 0) + + let xs:HotObservable = scheduler.createHotObservable([]) + + let res = scheduler.start() { + xs.skipUntil(250, scheduler) + } + + XCTAssertEqual(res.messages, []) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 1000) + ]) + } + + func testSkipUntil_Twice1() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(210, 1), + next(220, 2), + next(230, 3), + next(240, 4), + next(250, 5), + next(260, 6), + completed(270) + ]) + + let res = scheduler.start() { + xs.skipUntil(215, scheduler).skipUntil(230, scheduler) + } + + XCTAssertEqual(res.messages, [ + next(240, 4), + next(250, 5), + next(260, 6), + completed(270) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 270) + ]) + } + + func testSkipUntil_Twice2() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(210, 1), + next(220, 2), + next(230, 3), + next(240, 4), + next(250, 5), + next(260, 6), + completed(270) + ]) + + let res = scheduler.start() { + xs.skipUntil(230, scheduler).skipUntil(215, scheduler) + } + + XCTAssertEqual(res.messages, [ + next(240, 4), + next(250, 5), + next(260, 6), + completed(270) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 270) + ]) + } } \ No newline at end of file From c524c8a6ce758bbe4d00547eb9fbed86dd3f93eb Mon Sep 17 00:00:00 2001 From: yury Date: Thu, 8 Oct 2015 16:07:46 +0300 Subject: [PATCH 008/210] Include SkipUntil.swift in RxSwift-watchOS target --- Rx.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index 9215be63..4f997bc2 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -540,6 +540,7 @@ D2138C971BB9BEE700339B5C /* RxCLLocationManagerDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E9A1B8A732E0088E94D /* RxCLLocationManagerDelegateProxy.swift */; }; D2138C981BB9BEEE00339B5C /* RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E9B1B8A732E0088E94D /* RxCocoa.swift */; }; D2138C991BB9BEEE00339B5C /* RxTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E9C1B8A732E0088E94D /* RxTarget.swift */; }; + D21C29311BC6A1C300448E70 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D285BAC31BC0231000B3F602 /* SkipUntil.swift */; }; D2752D621BC5551A0070C418 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D285BAC31BC0231000B3F602 /* SkipUntil.swift */; }; D2752D631BC5551B0070C418 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D285BAC31BC0231000B3F602 /* SkipUntil.swift */; }; D285BAC41BC0231000B3F602 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D285BAC31BC0231000B3F602 /* SkipUntil.swift */; }; @@ -2233,6 +2234,7 @@ C8F0BFB61BBBFB8B001B112F /* BinaryDisposable.swift in Sources */, C8F0BFB71BBBFB8B001B112F /* ObserveOn.swift in Sources */, C8F0BFB81BBBFB8B001B112F /* Sample.swift in Sources */, + D21C29311BC6A1C300448E70 /* SkipUntil.swift in Sources */, C8F0BFB91BBBFB8B001B112F /* ScopedDisposable.swift in Sources */, C8F0BFBA1BBBFB8B001B112F /* Multicast.swift in Sources */, C8F0BFBB1BBBFB8B001B112F /* CurrentThreadScheduler.swift in Sources */, From 9c3a6d506128bcd54d34b0e5143288631244002b Mon Sep 17 00:00:00 2001 From: samgreen Date: Thu, 8 Oct 2015 09:19:05 -0400 Subject: [PATCH 009/210] Clearer comment about dispose bags. --- .../GitHubSignup/Views/GitHubSignupViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift b/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift index 7acc2f9f..4b98bef9 100644 --- a/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift +++ b/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift @@ -229,8 +229,8 @@ class GitHubSignupViewController : ViewController { // while navigation stack is popping. // This will work well with UINavigationController, but has an assumption that view controller will - // never be readded as a child view controller. - // If it was readded the UI wouldn't be bound anymore. + // never be added as a child view controller. If we didn't recreate the dispose bag here, + // then our resources would never be properly released. override func willMoveToParentViewController(parent: UIViewController?) { if let parent = parent { assert(parent.isKindOfClass(UINavigationController), "Please read comments") From fe8e6d79655630fd0c9c2b66dec76f7d79ca2287 Mon Sep 17 00:00:00 2001 From: samgreen Date: Thu, 8 Oct 2015 09:22:37 -0400 Subject: [PATCH 010/210] Add self to access colors from closure. --- .../GitHubSignup/Views/GitHubSignupViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift b/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift index 4b98bef9..9ec1e6b5 100644 --- a/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift +++ b/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift @@ -108,7 +108,7 @@ class GitHubSignupViewController : ViewController { let validationColor: UIColor if let valid = v.valid { - validationColor = valid ? okColor : errorColor + validationColor = valid ? self.okColor : self.errorColor } else { validationColor = UIColor.grayColor() } From f065189ba9f5b2a129c22972cb7bf7fc80cddcb4 Mon Sep 17 00:00:00 2001 From: yury Date: Thu, 8 Oct 2015 16:32:44 +0300 Subject: [PATCH 011/210] Remove SkipUntilTime --- .../Implementations/SkipUntil.swift | 68 -------- RxSwift/Observables/Observable+Time.swift | 8 - .../Tests/Observable+TimeTest.swift | 164 ------------------ 3 files changed, 240 deletions(-) diff --git a/RxSwift/Observables/Implementations/SkipUntil.swift b/RxSwift/Observables/Implementations/SkipUntil.swift index d5c3b7f8..0b556f4f 100644 --- a/RxSwift/Observables/Implementations/SkipUntil.swift +++ b/RxSwift/Observables/Implementations/SkipUntil.swift @@ -121,71 +121,3 @@ class SkipUntil: Producer { return sink.run() } } - -// MARK: SkipUntil time - -class SkipUntilTimeSink : Sink, ObserverType { - - typealias E = ElementType - typealias Parent = SkipUntilTime - - private let _parent: Parent - - // state - private var _open: Bool = false - - init(parent: Parent, observer: O, cancel: Disposable) { - _parent = parent - super.init(observer: observer, cancel: cancel) - } - - func on(event: Event) { - switch(event) { - case let .Next(element): - if _open { - observer?.onNext(element) - } - case let .Error(error): - _observer?.onError(error) - dispose() - case .Completed: - _observer?.onComplete() - dispose() - } - } - - func run() -> Disposable { - // Actually it should be abs time here. Or diff from now - let disposeTimer = _parent._scheduler.scheduleRelative((), dueTime:_parent._startTime) { - self._tick() - return NopDisposable.instance - } - let disposeSubscription = _parent._source.subscribeSafe(self) - return BinaryDisposable(disposeTimer, disposeSubscription) - } - - private func _tick() { - _open = true - } -} - -class SkipUntilTime: Producer { - typealias TimeInterval = S.TimeInterval - - private let _source: Observable - private let _startTime: TimeInterval - private let _scheduler: S - - init(source: Observable, startTime: TimeInterval, scheduler: S) { - _source = source - _startTime = startTime - _scheduler = scheduler - } - - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = SkipUntilTimeSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() - } -} - diff --git a/RxSwift/Observables/Observable+Time.swift b/RxSwift/Observables/Observable+Time.swift index eec02c70..a0c6f897 100644 --- a/RxSwift/Observables/Observable+Time.swift +++ b/RxSwift/Observables/Observable+Time.swift @@ -198,11 +198,3 @@ extension ObservableType { return BufferTimeCount(source: self.asObservable(), timeSpan: timeSpan, count: count, scheduler: scheduler) } } - -// SkipUntil time - -extension ObservableType { - public func skipUntil(startTime: S.TimeInterval, _ scheduler: S) -> Observable { - return SkipUntilTime(source: self.asObservable(), startTime: startTime, scheduler: scheduler) - } -} \ No newline at end of file diff --git a/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift b/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift index 29d8e0b0..eae22e2d 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift @@ -1304,168 +1304,4 @@ extension ObservableTimeTest { XCTAssertEqual(result!, [4, 5, 6]) } - // MARK: SkipUntil - - func testSkipUntil_Zero() { - let scheduler = TestScheduler(initialClock: 0) - - let xs = scheduler.createHotObservable([ - next(210, 1), - next(220, 2), - completed(230) - ]) - - let res = scheduler.start() { - xs.skipUntil(0, scheduler) - } - - XCTAssertEqual(res.messages, [ - next(210, 1), - next(220, 2), - completed(230) - ]) - - XCTAssertEqual(xs.subscriptions, [ - Subscription(200, 230) - ]) - } - - func testSkipUntil_Some() { - let scheduler = TestScheduler(initialClock: 0) - - let xs = scheduler.createHotObservable([ - next(210, 1), - next(220, 2), - completed(230) - ]) - - let res = scheduler.start() { - xs.skipUntil(215, scheduler) - } - - XCTAssertEqual(res.messages, [ - next(220, 2), - completed(230) - ]) - - XCTAssertEqual(xs.subscriptions, [ - Subscription(200, 230) - ]) - } - - func testSkipUntil_Late() { - let scheduler = TestScheduler(initialClock: 0) - - let xs = scheduler.createHotObservable([ - next(210, 1), - next(220, 2), - completed(230) - ]) - - let res = scheduler.start() { - xs.skipUntil(250, scheduler) - } - - XCTAssertEqual(res.messages, [ - completed(230) - ]) - - XCTAssertEqual(xs.subscriptions, [ - Subscription(200, 230) - ]) - } - - func testSkipUntil_Error() { - let scheduler = TestScheduler(initialClock: 0) - - let xs:HotObservable = scheduler.createHotObservable([ - error(210, testError) - ]) - - let res = scheduler.start() { - xs.skipUntil(250, scheduler) - } - - XCTAssertEqual(res.messages, [ - error(210, testError) - ]) - - XCTAssertEqual(xs.subscriptions, [ - Subscription(200, 210) - ]) - } - - func testSkipUntil_Never() { - let scheduler = TestScheduler(initialClock: 0) - - let xs:HotObservable = scheduler.createHotObservable([]) - - let res = scheduler.start() { - xs.skipUntil(250, scheduler) - } - - XCTAssertEqual(res.messages, []) - - XCTAssertEqual(xs.subscriptions, [ - Subscription(200, 1000) - ]) - } - - func testSkipUntil_Twice1() { - let scheduler = TestScheduler(initialClock: 0) - - let xs = scheduler.createHotObservable([ - next(210, 1), - next(220, 2), - next(230, 3), - next(240, 4), - next(250, 5), - next(260, 6), - completed(270) - ]) - - let res = scheduler.start() { - xs.skipUntil(215, scheduler).skipUntil(230, scheduler) - } - - XCTAssertEqual(res.messages, [ - next(240, 4), - next(250, 5), - next(260, 6), - completed(270) - ]) - - XCTAssertEqual(xs.subscriptions, [ - Subscription(200, 270) - ]) - } - - func testSkipUntil_Twice2() { - let scheduler = TestScheduler(initialClock: 0) - - let xs = scheduler.createHotObservable([ - next(210, 1), - next(220, 2), - next(230, 3), - next(240, 4), - next(250, 5), - next(260, 6), - completed(270) - ]) - - let res = scheduler.start() { - xs.skipUntil(230, scheduler).skipUntil(215, scheduler) - } - - XCTAssertEqual(res.messages, [ - next(240, 4), - next(250, 5), - next(260, 6), - completed(270) - ]) - - XCTAssertEqual(xs.subscriptions, [ - Subscription(200, 270) - ]) - } } \ No newline at end of file From 6d12d3786ebcdd7825c7f5b1db788ecdb89707a9 Mon Sep 17 00:00:00 2001 From: yury Date: Thu, 8 Oct 2015 16:51:23 +0300 Subject: [PATCH 012/210] Use _forwardElements boolean flag instead of optional observer --- .../Observables/Implementations/SkipUntil.swift | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/RxSwift/Observables/Implementations/SkipUntil.swift b/RxSwift/Observables/Implementations/SkipUntil.swift index 0b556f4f..643f0805 100644 --- a/RxSwift/Observables/Implementations/SkipUntil.swift +++ b/RxSwift/Observables/Implementations/SkipUntil.swift @@ -37,7 +37,7 @@ class SkipUntilSinkOther - private let _parent: Parent let lock = NSRecursiveLock() - var __observer: O? // Nop observer for start. Need better name + private let _parent: Parent + private var _forwardElements = false private let _singleAssignmentDisposable = SingleAssignmentDisposable() @@ -84,12 +84,16 @@ class SkipUntilSink) { switch event { case .Next: - __observer?.on(event) + if _forwardElements { + _observer?.on(event) + } case .Error: observer?.on(event) dispose() case .Completed: - __observer?.on(event) + if _forwardElements { + _observer?.on(event) + } _singleAssignmentDisposable.dispose() } } From c5b6868072192684cdd3ee05849bbd2c73b1493e Mon Sep 17 00:00:00 2001 From: yury Date: Thu, 8 Oct 2015 17:06:00 +0300 Subject: [PATCH 013/210] Improve locking --- .../Implementations/SkipUntil.swift | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/RxSwift/Observables/Implementations/SkipUntil.swift b/RxSwift/Observables/Implementations/SkipUntil.swift index 643f0805..0a111999 100644 --- a/RxSwift/Observables/Implementations/SkipUntil.swift +++ b/RxSwift/Observables/Implementations/SkipUntil.swift @@ -33,18 +33,19 @@ class SkipUntilSinkOther) { - // Do we need lock here? - _parent.lock.performLocked { - switch event { - case .Next: + switch event { + case .Next: + _parent._lock.performLocked { _parent._forwardElements = true _singleAssignmentDisposable.dispose() - case .Error(let e): + } + case .Error(let e): + _parent._lock.performLocked { _parent._observer?.onError(e) _parent.dispose() - case .Completed: - _singleAssignmentDisposable.dispose() } + case .Completed: + _singleAssignmentDisposable.dispose() } } @@ -61,7 +62,7 @@ class SkipUntilSink - let lock = NSRecursiveLock() + private let _lock = NSRecursiveLock() private let _parent: Parent private var _forwardElements = false @@ -82,19 +83,21 @@ class SkipUntilSink) { - switch event { - case .Next: - if _forwardElements { - _observer?.on(event) + _lock.performLocked { + switch event { + case .Next: + if _forwardElements { + _observer?.on(event) + } + case .Error: + observer?.on(event) + dispose() + case .Completed: + if _forwardElements { + _observer?.on(event) + } + _singleAssignmentDisposable.dispose() } - case .Error: - observer?.on(event) - dispose() - case .Completed: - if _forwardElements { - _observer?.on(event) - } - _singleAssignmentDisposable.dispose() } } From 99ddac7848269e2286a9450f6ddfedb6db705169 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Thu, 8 Oct 2015 16:31:28 +0200 Subject: [PATCH 014/210] Moves validation colors in it's own struct. --- .../Views/GitHubSignupViewController.swift | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift b/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift index 9ec1e6b5..00ebe08b 100644 --- a/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift +++ b/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift @@ -90,9 +90,11 @@ class GitHubSignupViewController : ViewController { @IBOutlet weak var signupOutlet: UIButton! @IBOutlet weak var signingUpOulet: UIActivityIndicatorView! - - let okColor = UIColor(red: 138.0 / 255.0, green: 221.0 / 255.0, blue: 109.0 / 255.0, alpha: 1.0) - let errorColor = UIColor.redColor() + + struct ValidationColors { + static let okColor = UIColor(red: 138.0 / 255.0, green: 221.0 / 255.0, blue: 109.0 / 255.0, alpha: 1.0) + static let errorColor = UIColor.redColor() + } var disposeBag = DisposeBag() @@ -108,7 +110,7 @@ class GitHubSignupViewController : ViewController { let validationColor: UIColor if let valid = v.valid { - validationColor = valid ? self.okColor : self.errorColor + validationColor = valid ? ValidationColors.okColor : ValidationColors.errorColor } else { validationColor = UIColor.grayColor() } From c64d392ba652084df3a43e420bd0f50d08cf3adc Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Thu, 8 Oct 2015 16:36:51 +0200 Subject: [PATCH 015/210] Removes unnecessary lock from Skip time version. --- .../Observables/Implementations/Skip.swift | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/RxSwift/Observables/Implementations/Skip.swift b/RxSwift/Observables/Implementations/Skip.swift index 1ed43f17..9c69b9e1 100644 --- a/RxSwift/Observables/Implementations/Skip.swift +++ b/RxSwift/Observables/Implementations/Skip.swift @@ -69,8 +69,6 @@ class SkipTimeSink) { - lock.performLocked { - switch event { - case .Next(let value): - if open { - observer?.on(.Next(value)) - } - case .Error: - observer?.on(event) - self.dispose() - case .Completed: - observer?.on(event) - self.dispose() + switch event { + case .Next(let value): + if open { + observer?.on(.Next(value)) } + case .Error: + observer?.on(event) + self.dispose() + case .Completed: + observer?.on(event) + self.dispose() } } From 403389beec2b6acc350cd56a6b8234a26b374886 Mon Sep 17 00:00:00 2001 From: yury Date: Thu, 8 Oct 2015 19:07:12 +0300 Subject: [PATCH 016/210] Add SkipUntil to RxExample-iOS-no-module --- RxExample/RxExample.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index 9a5726e9..0372b305 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -292,6 +292,7 @@ C8DF92EA1B0B38C0009BCF9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; }; C8DF92EB1B0B38C0009BCF9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; }; C8DF92F61B0B43A4009BCF9A /* IntroductionExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DF92F51B0B43A4009BCF9A /* IntroductionExampleViewController.swift */; }; + D245D9F41BC6CA0900CAB388 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D245D9E61BC6C60800CAB388 /* SkipUntil.swift */; }; EC91FB951BBA144400973245 /* GitHubSearchRepositoriesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC91FB941BBA144400973245 /* GitHubSearchRepositoriesViewController.swift */; }; /* End PBXBuildFile section */ @@ -650,6 +651,7 @@ C8DF92F01B0B3E67009BCF9A /* Info-OSX.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-OSX.plist"; sourceTree = ""; }; C8DF92F21B0B3E71009BCF9A /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = ""; }; C8DF92F51B0B43A4009BCF9A /* IntroductionExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = IntroductionExampleViewController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D245D9E61BC6C60800CAB388 /* SkipUntil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipUntil.swift; sourceTree = ""; }; EC91FB941BBA144400973245 /* GitHubSearchRepositoriesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubSearchRepositoriesViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -1079,6 +1081,7 @@ C864093C1BA5909000D3C4E8 /* Implementations */ = { isa = PBXGroup; children = ( + D245D9E61BC6C60800CAB388 /* SkipUntil.swift */, C864093D1BA5909000D3C4E8 /* Amb.swift */, C864093E1BA5909000D3C4E8 /* AnonymousObservable.swift */, C864093F1BA5909000D3C4E8 /* AsObservable.swift */, @@ -1592,6 +1595,7 @@ C864099D1BA5909000D3C4E8 /* NAryDisposable.swift in Sources */, C86409C51BA5909000D3C4E8 /* ObserveOn.swift in Sources */, C84B3A331BA4345A001B7D88 /* ControlProperty.swift in Sources */, + D245D9F41BC6CA0900CAB388 /* SkipUntil.swift in Sources */, C86409FA1BA5909000D3C4E8 /* Variable.swift in Sources */, C8297E3A1B6CF905000589EA /* WikipediaSearchViewController.swift in Sources */, C84B3A5A1BA4345A001B7D88 /* UIGestureRecognizer+Rx.swift in Sources */, From 978b4eb6291a8cf3aaade0e71a88d7e3e7c5e3da Mon Sep 17 00:00:00 2001 From: Yoshinori Sano Date: Fri, 9 Oct 2015 09:15:59 +0900 Subject: [PATCH 017/210] Fix outdated comments on UISearchBar's rx_searchText property. --- README.md | 4 ++-- RxCocoa/Common/DelegateProxyType.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5acb77e4..30b1ef8b 100644 --- a/README.md +++ b/README.md @@ -321,7 +321,7 @@ extension UISearchBar { return proxyForObject(self) as RxSearchBarDelegateProxy } - public var rx_searchText: Observable { + public var rx_text: Observable { return defer { [weak self] in let text = self?.text ?? "" @@ -341,7 +341,7 @@ This is how that API can be now used ```swift -searchBar.rx_searchText +searchBar.rx_text .subscribeNext { searchText in print("Current search text '\(searchText)'") } diff --git a/RxCocoa/Common/DelegateProxyType.swift b/RxCocoa/Common/DelegateProxyType.swift index 562f0078..5b7b77fa 100644 --- a/RxCocoa/Common/DelegateProxyType.swift +++ b/RxCocoa/Common/DelegateProxyType.swift @@ -160,7 +160,7 @@ Returns existing proxy for object or installs new instance of delegate proxy. return proxyForObject(self) as RxSearchBarDelegateProxy } - public var rx_searchText: ControlProperty { + public var rx_text: ControlProperty { let source: Observable = self.rx_delegate.observe("searchBar:textDidChange:") ... } From b4d4e4b4d625aa4ae633ac5a8d250a9d252985d4 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sat, 10 Oct 2015 17:26:51 +0200 Subject: [PATCH 018/210] Moves retry logic in Github example to right place. --- ...tHubSearchRepositoriesViewController.swift | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift index 3ec656d3..5852a51c 100644 --- a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift +++ b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift @@ -95,29 +95,31 @@ class GitHubSearchRepositoriesAPI { } private func recursivelySearch(loadedSoFar: [Repository], loadNextURL: NSURL, loadNextPageTrigger: Observable) -> Observable { - return loadSearchURL(loadNextURL).flatMap { (newPageRepositoriesResponse, nextURL) -> Observable in - // in case access denied, just stop - guard case .Repositories(let newPageRepositories) = newPageRepositoriesResponse else { - return just(newPageRepositoriesResponse) + return loadSearchURL(loadNextURL) + .retry(3) + .flatMap { (newPageRepositoriesResponse, nextURL) -> Observable in + // in case access denied, just stop + guard case .Repositories(let newPageRepositories) = newPageRepositoriesResponse else { + return just(newPageRepositoriesResponse) + } + + var loadedRepositories = loadedSoFar + loadedRepositories.appendContentsOf(newPageRepositories) + + // if next page can't be loaded, just return what was loaded, and stop + guard let nextURL = nextURL else { + return just(.Repositories(loadedRepositories)) + } + + return [ + // return loaded immediately + just(.Repositories(loadedRepositories)), + // wait until next page can be loaded + never().takeUntil(loadNextPageTrigger), + // load next page + self.recursivelySearch(loadedRepositories, loadNextURL: nextURL, loadNextPageTrigger: loadNextPageTrigger) + ].concat() } - - var loadedRepositories = loadedSoFar - loadedRepositories.appendContentsOf(newPageRepositories) - - // if next page can't be loaded, just return what was loaded, and stop - guard let nextURL = nextURL else { - return just(.Repositories(loadedRepositories)) - } - - return [ - // return loaded immediately - just(.Repositories(loadedRepositories)), - // wait until next page can be loaded - never().takeUntil(loadNextPageTrigger), - // load next page - self.recursivelySearch(loadedRepositories, loadNextURL: nextURL, loadNextPageTrigger: loadNextPageTrigger) - ].concat() - } } private func loadSearchURL(searchURL: NSURL) -> Observable<(response: SearchRepositoryResponse, nextURL: NSURL?)> { @@ -230,7 +232,6 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat return just(.Repositories([])) } else { return GitHubSearchRepositoriesAPI.sharedAPI.search(query, loadNextPageTrigger: loadNextPageTrigger) - .retry(3) .catchErrorJustReturn(.Repositories([])) } } From cbdf027d3d590e14fbcc48e93928bc6678ca7752 Mon Sep 17 00:00:00 2001 From: yury Date: Mon, 12 Oct 2015 11:59:20 +0300 Subject: [PATCH 019/210] Add SkipWhile operator --- Rx.xcodeproj/project.pbxproj | 10 + RxExample/RxExample.xcodeproj/project.pbxproj | 4 + .../Implementations/SkipWhile.swift | 115 +++++ ...Observable+StandardSequenceOperators.swift | 13 + ...rvable+StandardSequenceOperatorsTest.swift | 435 ++++++++++++++++++ 5 files changed, 577 insertions(+) create mode 100644 RxSwift/Observables/Implementations/SkipWhile.swift diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index 4f997bc2..c6e0a40e 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -541,6 +541,7 @@ D2138C981BB9BEEE00339B5C /* RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E9B1B8A732E0088E94D /* RxCocoa.swift */; }; D2138C991BB9BEEE00339B5C /* RxTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E9C1B8A732E0088E94D /* RxTarget.swift */; }; D21C29311BC6A1C300448E70 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D285BAC31BC0231000B3F602 /* SkipUntil.swift */; }; + D22B6D261BC8504A00BCE0AB /* SkipWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22B6D251BC8504A00BCE0AB /* SkipWhile.swift */; }; D2752D621BC5551A0070C418 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D285BAC31BC0231000B3F602 /* SkipUntil.swift */; }; D2752D631BC5551B0070C418 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D285BAC31BC0231000B3F602 /* SkipUntil.swift */; }; D285BAC41BC0231000B3F602 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D285BAC31BC0231000B3F602 /* SkipUntil.swift */; }; @@ -650,6 +651,9 @@ D2EBEB431BB9B6DE003A27DC /* SubjectType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CC11B8A72BE0088E94D /* SubjectType.swift */; }; D2EBEB441BB9B6DE003A27DC /* Variable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CC21B8A72BE0088E94D /* Variable.swift */; }; D2EBEB8A1BB9B9EE003A27DC /* Observable+Blocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093F581B8A73A20088E94D /* Observable+Blocking.swift */; }; + D2FC15B31BCB95E5007361FF /* SkipWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22B6D251BC8504A00BCE0AB /* SkipWhile.swift */; }; + D2FC15B41BCB95E7007361FF /* SkipWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22B6D251BC8504A00BCE0AB /* SkipWhile.swift */; }; + D2FC15B51BCB95E8007361FF /* SkipWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22B6D251BC8504A00BCE0AB /* SkipWhile.swift */; }; F31F35B01BB4FED800961002 /* UIStepper+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = F31F35AF1BB4FED800961002 /* UIStepper+Rx.swift */; }; /* End PBXBuildFile section */ @@ -901,6 +905,7 @@ C8F0C04B1BBBFBB9001B112F /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8F0C0581BBBFBCE001B112F /* RxBlocking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxBlocking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D2138C751BB9BE9800339B5C /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D22B6D251BC8504A00BCE0AB /* SkipWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipWhile.swift; sourceTree = ""; }; D285BAC31BC0231000B3F602 /* SkipUntil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipUntil.swift; sourceTree = ""; }; D2EA280C1BB9B5A200880ED3 /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D2EBEB811BB9B99D003A27DC /* RxBlocking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxBlocking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1134,6 +1139,7 @@ C8093C921B8A72BE0088E94D /* Zip+arity.swift */, C8093C931B8A72BE0088E94D /* Zip+arity.tt */, C8C3D9FD1B935EDF004D233E /* Zip+CollectionType.swift */, + D22B6D251BC8504A00BCE0AB /* SkipWhile.swift */, ); path = Implementations; sourceTree = ""; @@ -1997,6 +2003,7 @@ C8093D041B8A72BE0088E94D /* AsObservable.swift in Sources */, C8093D061B8A72BE0088E94D /* Catch.swift in Sources */, C8093D0C1B8A72BE0088E94D /* CombineLatest.swift in Sources */, + D2FC15B31BCB95E5007361FF /* SkipWhile.swift in Sources */, C8093D5E1B8A72BE0088E94D /* Observable+Multiple.swift in Sources */, C8093D741B8A72BE0088E94D /* ObserverBase.swift in Sources */, C8093D121B8A72BE0088E94D /* ConnectableObservable.swift in Sources */, @@ -2110,6 +2117,7 @@ C8093D031B8A72BE0088E94D /* AsObservable.swift in Sources */, C8093D051B8A72BE0088E94D /* Catch.swift in Sources */, C8093D0B1B8A72BE0088E94D /* CombineLatest.swift in Sources */, + D22B6D261BC8504A00BCE0AB /* SkipWhile.swift in Sources */, C8093D5D1B8A72BE0088E94D /* Observable+Multiple.swift in Sources */, C8093D731B8A72BE0088E94D /* ObserverBase.swift in Sources */, C8093D111B8A72BE0088E94D /* ConnectableObservable.swift in Sources */, @@ -2223,6 +2231,7 @@ C8F0BFAB1BBBFB8B001B112F /* AsObservable.swift in Sources */, C8F0BFAC1BBBFB8B001B112F /* Catch.swift in Sources */, C8F0BFAD1BBBFB8B001B112F /* CombineLatest.swift in Sources */, + D2FC15B51BCB95E8007361FF /* SkipWhile.swift in Sources */, C8F0BFAE1BBBFB8B001B112F /* Observable+Multiple.swift in Sources */, C8F0BFAF1BBBFB8B001B112F /* ObserverBase.swift in Sources */, C8F0BFB01BBBFB8B001B112F /* ConnectableObservable.swift in Sources */, @@ -2470,6 +2479,7 @@ D2EBEB381BB9B6D8003A27DC /* ConcurrentDispatchQueueScheduler.swift in Sources */, D2EBEB131BB9B6C1003A27DC /* Multicast.swift in Sources */, D2EBEB111BB9B6C1003A27DC /* Map.swift in Sources */, + D2FC15B41BCB95E7007361FF /* SkipWhile.swift in Sources */, D2EBEB071BB9B6C1003A27DC /* Deferred.swift in Sources */, D2EBEB2C1BB9B6CA003A27DC /* Observable+Binding.swift in Sources */, D2EBEB041BB9B6C1003A27DC /* Concat.swift in Sources */, diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index 0372b305..01ac11d0 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -293,6 +293,7 @@ C8DF92EB1B0B38C0009BCF9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; }; C8DF92F61B0B43A4009BCF9A /* IntroductionExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DF92F51B0B43A4009BCF9A /* IntroductionExampleViewController.swift */; }; D245D9F41BC6CA0900CAB388 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D245D9E61BC6C60800CAB388 /* SkipUntil.swift */; }; + D2FC15C41BCBAA13007361FF /* SkipWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FC15B61BCBAA01007361FF /* SkipWhile.swift */; }; EC91FB951BBA144400973245 /* GitHubSearchRepositoriesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC91FB941BBA144400973245 /* GitHubSearchRepositoriesViewController.swift */; }; /* End PBXBuildFile section */ @@ -652,6 +653,7 @@ C8DF92F21B0B3E71009BCF9A /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = ""; }; C8DF92F51B0B43A4009BCF9A /* IntroductionExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = IntroductionExampleViewController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D245D9E61BC6C60800CAB388 /* SkipUntil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipUntil.swift; sourceTree = ""; }; + D2FC15B61BCBAA01007361FF /* SkipWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipWhile.swift; sourceTree = ""; }; EC91FB941BBA144400973245 /* GitHubSearchRepositoriesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubSearchRepositoriesViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -1081,6 +1083,7 @@ C864093C1BA5909000D3C4E8 /* Implementations */ = { isa = PBXGroup; children = ( + D2FC15B61BCBAA01007361FF /* SkipWhile.swift */, D245D9E61BC6C60800CAB388 /* SkipUntil.swift */, C864093D1BA5909000D3C4E8 /* Amb.swift */, C864093E1BA5909000D3C4E8 /* AnonymousObservable.swift */, @@ -1599,6 +1602,7 @@ C86409FA1BA5909000D3C4E8 /* Variable.swift in Sources */, C8297E3A1B6CF905000589EA /* WikipediaSearchViewController.swift in Sources */, C84B3A5A1BA4345A001B7D88 /* UIGestureRecognizer+Rx.swift in Sources */, + D2FC15C41BCBAA13007361FF /* SkipWhile.swift in Sources */, C86409AC1BA5909000D3C4E8 /* AnonymousObservable.swift in Sources */, C84B3A401BA4345A001B7D88 /* NSURLSession+Rx.swift in Sources */, C8297E3B1B6CF905000589EA /* String+extensions.swift in Sources */, diff --git a/RxSwift/Observables/Implementations/SkipWhile.swift b/RxSwift/Observables/Implementations/SkipWhile.swift new file mode 100644 index 00000000..cd5d6e53 --- /dev/null +++ b/RxSwift/Observables/Implementations/SkipWhile.swift @@ -0,0 +1,115 @@ +// +// SkipWhile.swift +// Rx +// +// Created by Yury Korolev on 10/9/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +class SkipWhileSink : Sink, ObserverType { + + typealias Parent = SkipWhile + typealias Element = ElementType + + private let _parent: Parent + private var _running = false + + init(parent: Parent, observer: O, cancel: Disposable) { + _parent = parent + super.init(observer: observer, cancel: cancel) + } + + func on(event: Event) { + switch event { + case .Next(let value): + if !_running { + do { + _running = try !_parent._predicate(value) + } catch let e { + _observer?.onError(e) + dispose() + return + } + } + + if _running { + _observer?.onNext(value) + } + case .Error, .Completed: + _observer?.on(event) + dispose() + } + } +} + +class SkipWhileSinkIndexed : Sink, ObserverType { + + typealias Parent = SkipWhile + typealias Element = ElementType + + private let _parent: Parent + private var _index = 0 + private var _running = false + + init(parent: Parent, observer: O, cancel: Disposable) { + _parent = parent + super.init(observer: observer, cancel: cancel) + } + + func on(event: Event) { + switch event { + case .Next(let value): + if !_running { + do { + _running = try !_parent._predicateIndexed(value, _index) + _index += 1 + } catch let e { + _observer?.onError(e) + dispose() + return + } + } + + if _running { + _observer?.onNext(value) + } + case .Error, .Completed: + _observer?.on(event) + dispose() + } + } +} + +class SkipWhile: Producer { + typealias Predicate = (Element) throws -> Bool + typealias PredicateIndexed = (Element, Int) throws -> Bool + + private let _source: Observable + private let _predicate: Predicate! + private let _predicateIndexed: PredicateIndexed! + + init(source: Observable, predicate: Predicate) { + _source = source + _predicate = predicate + _predicateIndexed = nil + } + + init(source: Observable, predicate: PredicateIndexed) { + _source = source + _predicate = nil + _predicateIndexed = predicate + } + + override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + if let _ = _predicate { + let sink = SkipWhileSink(parent: self, observer: observer, cancel: cancel) + setSink(sink) + return _source.subscribeSafe(sink) + } + else { + let sink = SkipWhileSinkIndexed(parent: self, observer: observer, cancel: cancel) + setSink(sink) + return _source.subscribeSafe(sink) + } + } +} diff --git a/RxSwift/Observables/Observable+StandardSequenceOperators.swift b/RxSwift/Observables/Observable+StandardSequenceOperators.swift index 021f6c65..f4cc161a 100644 --- a/RxSwift/Observables/Observable+StandardSequenceOperators.swift +++ b/RxSwift/Observables/Observable+StandardSequenceOperators.swift @@ -90,6 +90,19 @@ extension ObservableType { } } +// SkipWhile + +extension ObservableType { + + public func skipWhile(predicate: (E) throws -> Bool) -> Observable { + return SkipWhile(source: self.asObservable(), predicate: predicate) + } + + public func skipWhileIndexed(predicate: (E, Int) throws -> Bool) -> Observable { + return SkipWhile(source: self.asObservable(), predicate: predicate) + } +} + // map aka select extension ObservableType { diff --git a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift index 5a27aff3..bca0a779 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift @@ -3128,4 +3128,439 @@ extension ObservableStandardSequenceOperators { Subscription(200, 400) ]) } +} + +// MARK: SkipWhile +extension ObservableStandardSequenceOperators { + + func testSkipWhile_Complete_Before() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, -1), + next(110, -1), + next(210, 2), + next(260, 5), + next(290, 13), + next(320, 3), + completed(330), + next(350, 7), + next(390, 4), + next(410, 17), + next(450, 8), + next(500, 23), + completed(600) + ]) + + var invoked = 0 + + let res = scheduler.start() { + xs.skipWhile { x in + invoked += 1 + return isPrime(x) + } + } + + XCTAssertEqual(res.messages, [ + completed(330) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 330) + ]) + + XCTAssertEqual(4, invoked) + } + + func testSkipWhile_Complete_After() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, -1), + next(110, -1), + next(210, 2), + next(260, 5), + next(290, 13), + next(320, 3), + next(350, 7), + next(390, 4), + next(410, 17), + next(450, 8), + next(500, 23), + completed(600) + ]) + + var invoked = 0 + + let res = scheduler.start() { + xs.skipWhile { x in + invoked += 1 + return isPrime(x) + } + } + + XCTAssertEqual(res.messages, [ + next(390, 4), + next(410, 17), + next(450, 8), + next(500, 23), + completed(600) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 600) + ]) + + XCTAssertEqual(6, invoked) + } + + func testSkipWhile_Error_Before() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, -1), + next(110, -1), + next(210, 2), + next(260, 5), + error(270, testError), + next(290, 13), + next(320, 3), + next(350, 7), + next(390, 4), + next(410, 17), + next(450, 8), + next(500, 23) + ]) + + var invoked = 0 + + let res = scheduler.start() { + xs.skipWhile { x in + invoked += 1 + return isPrime(x) + } + } + + + + XCTAssertEqual(res.messages, [ + error(270, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 270) + ]) + + XCTAssertEqual(2, invoked) + } + + func testSkipWhile_Error_After() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, -1), + next(110, -1), + next(210, 2), + next(260, 5), + next(290, 13), + next(320, 3), + next(350, 7), + next(390, 4), + next(410, 17), + next(450, 8), + next(500, 23), + error(600, testError) + ]) + + var invoked = 0 + + let res = scheduler.start() { + xs.skipWhile { x in + invoked += 1 + return isPrime(x) + } + } + + XCTAssertEqual(res.messages, [ + next(390, 4), + next(410, 17), + next(450, 8), + next(500, 23), + error(600, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 600) + ]) + + XCTAssertEqual(6, invoked) + } + + func testSkipWhile_Dispose_Before() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, -1), + next(110, -1), + next(210, 2), + next(260, 5), + next(290, 13), + next(320, 3), + next(350, 7), + next(390, 4), + next(410, 17), + next(450, 8), + next(500, 23), + completed(600) + ]) + + var invoked = 0 + + let res = scheduler.start(300) { + xs.skipWhile { x in + invoked += 1 + return isPrime(x) + } + } + + XCTAssertEqual(res.messages, []) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 300) + ]) + + XCTAssertEqual(3, invoked) + } + + func testSkipWhile_Dispose_After() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, -1), + next(110, -1), + next(210, 2), + next(260, 5), + next(290, 13), + next(320, 3), + next(350, 7), + next(390, 4), + next(410, 17), + next(450, 8), + next(500, 23), + completed(600) + ]) + + var invoked = 0 + + let res = scheduler.start(470) { + xs.skipWhile { x in + invoked += 1 + return isPrime(x) + } + } + + XCTAssertEqual(res.messages, [ + next(390, 4), + next(410, 17), + next(450, 8) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 470) + ]) + + XCTAssertEqual(6, invoked) + } + + func testSkipWhile_Zero() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, -1), + next(110, -1), + next(205, 100), + next(210, 2), + next(260, 5), + next(290, 13), + next(320, 3), + next(350, 7), + next(390, 4), + next(410, 17), + next(450, 8), + next(500, 23), + completed(600) + ]) + + var invoked = 0 + + let res = scheduler.start() { + xs.skipWhile { x in + invoked += 1 + return isPrime(x) + } + } + + XCTAssertEqual(res.messages, [ + next(205, 100), + next(210, 2), + next(260, 5), + next(290, 13), + next(320, 3), + next(350, 7), + next(390, 4), + next(410, 17), + next(450, 8), + next(500, 23), + completed(600) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 600) + ]) + + XCTAssertEqual(1, invoked) + } + + func testSkipWhile_Throw() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, -1), + next(110, -1), + next(210, 2), + next(260, 5), + next(290, 13), + next(320, 3), + next(350, 7), + next(390, 4), + next(410, 17), + next(450, 8), + next(500, 23), + completed(600) + ]) + + var invoked = 0 + + let res = scheduler.start() { + xs.skipWhile { x in + invoked += 1 + if invoked == 3 { + throw testError + } + return isPrime(x) + } + } + + XCTAssertEqual(res.messages, [ + error(290, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 290) + ]) + + XCTAssertEqual(3, invoked) + } + + func testSkipWhile_Index() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, -1), + next(110, -1), + next(205, 100), + next(210, 2), + next(260, 5), + next(290, 13), + next(320, 3), + next(350, 7), + next(390, 4), + next(410, 17), + next(450, 8), + next(500, 23), + completed(600) + ]) + + let res = scheduler.start() { + xs.skipWhileIndexed { x, i in i < 5 } + } + + XCTAssertEqual(res.messages, [ + next(350, 7), + next(390, 4), + next(410, 17), + next(450, 8), + next(500, 23), + completed(600) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 600) + ]) + } + + func testSkipWhile_Index_Throw() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, -1), + next(110, -1), + next(205, 100), + next(210, 2), + next(260, 5), + next(290, 13), + next(320, 3), + next(350, 7), + next(390, 4), + error(400, testError) + ]) + + let res = scheduler.start() { + xs.skipWhileIndexed { x, i in i < 5 } + } + + XCTAssertEqual(res.messages, [ + next(350, 7), + next(390, 4), + error(400, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 400) + ]) + } + + func testSkipWhile_Index_SelectorThrows() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, -1), + next(110, -1), + next(205, 100), + next(210, 2), + next(260, 5), + next(290, 13), + next(320, 3), + next(350, 7), + next(390, 4), + completed(400) + ]) + + let res = scheduler.start() { + xs.skipWhileIndexed { x, i in + if i < 5 { + return true + } + throw testError + } + } + + XCTAssertEqual(res.messages, [ + error(350, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 350) + ]) + } } \ No newline at end of file From fd6050ae2bf09bc69da465041246bf12d1facfc3 Mon Sep 17 00:00:00 2001 From: yury Date: Mon, 12 Oct 2015 14:36:19 +0300 Subject: [PATCH 020/210] Rename skipWhileIndexed to skipWhileWithIndex --- .../Observables/Implementations/SkipWhile.swift | 16 ++++++++-------- .../Observable+StandardSequenceOperators.swift | 2 +- ...bservable+StandardSequenceOperatorsTest.swift | 14 +++++++++++--- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/RxSwift/Observables/Implementations/SkipWhile.swift b/RxSwift/Observables/Implementations/SkipWhile.swift index cd5d6e53..eb10c043 100644 --- a/RxSwift/Observables/Implementations/SkipWhile.swift +++ b/RxSwift/Observables/Implementations/SkipWhile.swift @@ -42,7 +42,7 @@ class SkipWhileSink : Sin } } -class SkipWhileSinkIndexed : Sink, ObserverType { +class SkipWhileSinkWithIndex : Sink, ObserverType { typealias Parent = SkipWhile typealias Element = ElementType @@ -61,7 +61,7 @@ class SkipWhileSinkIndexed: Producer { typealias Predicate = (Element) throws -> Bool - typealias PredicateIndexed = (Element, Int) throws -> Bool + typealias PredicateWithIndex = (Element, Int) throws -> Bool private let _source: Observable private let _predicate: Predicate! - private let _predicateIndexed: PredicateIndexed! + private let _predicateWithIndex: PredicateWithIndex! init(source: Observable, predicate: Predicate) { _source = source _predicate = predicate - _predicateIndexed = nil + _predicateWithIndex = nil } - init(source: Observable, predicate: PredicateIndexed) { + init(source: Observable, predicate: PredicateWithIndex) { _source = source _predicate = nil - _predicateIndexed = predicate + _predicateWithIndex = predicate } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { @@ -107,7 +107,7 @@ class SkipWhile: Producer { return _source.subscribeSafe(sink) } else { - let sink = SkipWhileSinkIndexed(parent: self, observer: observer, cancel: cancel) + let sink = SkipWhileSinkWithIndex(parent: self, observer: observer, cancel: cancel) setSink(sink) return _source.subscribeSafe(sink) } diff --git a/RxSwift/Observables/Observable+StandardSequenceOperators.swift b/RxSwift/Observables/Observable+StandardSequenceOperators.swift index f4cc161a..b2ddfefd 100644 --- a/RxSwift/Observables/Observable+StandardSequenceOperators.swift +++ b/RxSwift/Observables/Observable+StandardSequenceOperators.swift @@ -98,7 +98,7 @@ extension ObservableType { return SkipWhile(source: self.asObservable(), predicate: predicate) } - public func skipWhileIndexed(predicate: (E, Int) throws -> Bool) -> Observable { + public func skipWhileWithIndex(predicate: (E, Int) throws -> Bool) -> Observable { return SkipWhile(source: self.asObservable(), predicate: predicate) } } diff --git a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift index bca0a779..7f0c0395 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift @@ -3482,7 +3482,7 @@ extension ObservableStandardSequenceOperators { ]) let res = scheduler.start() { - xs.skipWhileIndexed { x, i in i < 5 } + xs.skipWhileWithIndex { x, i in i < 5 } } XCTAssertEqual(res.messages, [ @@ -3515,8 +3515,16 @@ extension ObservableStandardSequenceOperators { error(400, testError) ]) + let x = 0 + if x == Int.max { + throw // Error + } + let res = x + x += 1 + return res + let res = scheduler.start() { - xs.skipWhileIndexed { x, i in i < 5 } + xs.skipWhileWithIndex { x, i in i < 5 } } XCTAssertEqual(res.messages, [ @@ -3547,7 +3555,7 @@ extension ObservableStandardSequenceOperators { ]) let res = scheduler.start() { - xs.skipWhileIndexed { x, i in + xs.skipWhileWithIndex { x, i in if i < 5 { return true } From c52c4f3cd7fb0ae56043a31d5fff69a5d0fd3efa Mon Sep 17 00:00:00 2001 From: yury Date: Mon, 12 Oct 2015 14:38:28 +0300 Subject: [PATCH 021/210] Remove test code --- .../Tests/Observable+StandardSequenceOperatorsTest.swift | 8 -------- 1 file changed, 8 deletions(-) diff --git a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift index 7f0c0395..40125dfd 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift @@ -3515,14 +3515,6 @@ extension ObservableStandardSequenceOperators { error(400, testError) ]) - let x = 0 - if x == Int.max { - throw // Error - } - let res = x - x += 1 - return res - let res = scheduler.start() { xs.skipWhileWithIndex { x, i in i < 5 } } From 8f7f6ccefd2af031e7f4c7fad16b1db6b056dac2 Mon Sep 17 00:00:00 2001 From: yury Date: Mon, 12 Oct 2015 23:24:03 +0300 Subject: [PATCH 022/210] Add docs for skipWhile --- Documentation/API.md | 1 + .../Observable+StandardSequenceOperators.swift | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/Documentation/API.md b/Documentation/API.md index 112eb515..e0549f25 100644 --- a/Documentation/API.md +++ b/Documentation/API.md @@ -61,6 +61,7 @@ Operators are stateless by default. #### Conditional and Boolean Operators * [`amb`](http://reactivex.io/documentation/operators/amb.html) + * [`skipWhile`](http://reactivex.io/documentation/operators/skipwhile.html) * [`takeUntil`](http://reactivex.io/documentation/operators/takeuntil.html) * [`takeWhile`](http://reactivex.io/documentation/operators/takewhile.html) diff --git a/RxSwift/Observables/Observable+StandardSequenceOperators.swift b/RxSwift/Observables/Observable+StandardSequenceOperators.swift index b2ddfefd..2fe13cc2 100644 --- a/RxSwift/Observables/Observable+StandardSequenceOperators.swift +++ b/RxSwift/Observables/Observable+StandardSequenceOperators.swift @@ -93,11 +93,24 @@ extension ObservableType { // SkipWhile extension ObservableType { + + /** + Bypasses elements in an observable sequence as long as a specified condition is true and then returns the remaining elements. + - parameter predicate: A function to test each element for a condition. + - returns: An observable sequence that contains the elements from the input sequence starting at the first element in the linear series that does not pass the test specified by predicate. + */ public func skipWhile(predicate: (E) throws -> Bool) -> Observable { return SkipWhile(source: self.asObservable(), predicate: predicate) } + + /** + Bypasses elements in an observable sequence as long as a specified condition is true and then returns the remaining elements. + The element's index is used in the logic of the predicate function. + - parameter predicate: A function to test each element for a condition; the second parameter of the function represents the index of the source element. + - returns: An observable sequence that contains the elements from the input sequence starting at the first element in the linear series that does not pass the test specified by predicate. + */ public func skipWhileWithIndex(predicate: (E, Int) throws -> Bool) -> Observable { return SkipWhile(source: self.asObservable(), predicate: predicate) } From 0ad230f39bb4c889c42b76af0980185bdbb9e0b0 Mon Sep 17 00:00:00 2001 From: yury Date: Tue, 13 Oct 2015 11:03:19 +0300 Subject: [PATCH 023/210] TakeWhile predicates can throw --- .../Implementations/TakeWhile.swift | 129 +++++++++--------- ...Observable+StandardSequenceOperators.swift | 8 +- ...rvable+StandardSequenceOperatorsTest.swift | 12 +- 3 files changed, 73 insertions(+), 76 deletions(-) diff --git a/RxSwift/Observables/Implementations/TakeWhile.swift b/RxSwift/Observables/Implementations/TakeWhile.swift index 23164c13..98cfab5b 100644 --- a/RxSwift/Observables/Implementations/TakeWhile.swift +++ b/RxSwift/Observables/Implementations/TakeWhile.swift @@ -8,118 +8,121 @@ import Foundation -class TakeWhileSink1 : Sink, ObserverType { +class TakeWhileSink : Sink, ObserverType { typealias Parent = TakeWhile typealias Element = ElementType - let parent: Parent + private let _parent: Parent - var running = true + private var _running = true init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } func on(event: Event) { - if !running { - return - } switch event { case .Next(let value): + if !_running { + return + } - running = self.parent.predicate1(value) - - if running { - observer?.on(.Next(value)) + do { + _running = try _parent._predicate(value) + } catch let e { + _observer?.onError(e) + dispose() + return } - else { - observer?.on(.Completed) - self.dispose() + + if _running { + _observer?.onNext(value) + } else { + _observer?.onComplete() + dispose() } - case .Error: - observer?.on(event) - self.dispose() - case .Completed: - observer?.on(event) - self.dispose() + case .Error, .Completed: + _observer?.on(event) + dispose() } } } -class TakeWhileSink2 : Sink, ObserverType { +class TakeWhileSinkWithIndex : Sink, ObserverType { typealias Parent = TakeWhile typealias Element = ElementType - let parent: Parent + private let _parent: Parent - var running = true - var index = 0 + private var _running = true + private var _index = 0 init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } func on(event: Event) { - if !running { - return - } switch event { case .Next(let value): - - running = self.parent.predicate2(value, index) - self.index = index + 1 - - if running { - observer?.on(.Next(value)) + if !_running { + return } - else { - observer?.on(.Completed) - self.dispose() + + do { + _running = try _parent._predicateWithIndex(value, _index) + _index += 1 + } catch let e { + _observer?.onError(e) + dispose() + return } - case .Error: - observer?.on(event) - self.dispose() - case .Completed: - observer?.on(event) - self.dispose() + + if _running { + _observer?.onNext(value) + } else { + _observer?.onComplete() + dispose() + } + case .Error, .Completed: + _observer?.on(event) + dispose() } } } class TakeWhile: Producer { - typealias Predicate1 = (Element) -> Bool - typealias Predicate2 = (Element, Int) -> Bool + typealias Predicate = (Element) throws -> Bool + typealias PredicateWithIndex = (Element, Int) throws -> Bool - let source: Observable - let predicate1: Predicate1! - let predicate2: Predicate2! + private let _source: Observable + private let _predicate: Predicate! + private let _predicateWithIndex: PredicateWithIndex! - init(source: Observable, predicate: Predicate1) { - self.source = source - self.predicate1 = predicate - self.predicate2 = nil + init(source: Observable, predicate: Predicate) { + _source = source + _predicate = predicate + _predicateWithIndex = nil } - init(source: Observable, predicate: Predicate2) { - self.source = source - self.predicate1 = nil - self.predicate2 = predicate + init(source: Observable, predicate: PredicateWithIndex) { + _source = source + _predicate = nil + _predicateWithIndex = predicate } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - if let _ = self.predicate1 { - let sink = TakeWhileSink1(parent: self, observer: observer, cancel: cancel) + if let _ = _predicate { + let sink = TakeWhileSink(parent: self, observer: observer, cancel: cancel) setSink(sink) - return source.subscribeSafe(sink) - } - else { - let sink = TakeWhileSink2(parent: self, observer: observer, cancel: cancel) + return _source.subscribeSafe(sink) + } else { + let sink = TakeWhileSinkWithIndex(parent: self, observer: observer, cancel: cancel) setSink(sink) - return source.subscribeSafe(sink) + return _source.subscribeSafe(sink) } } } \ No newline at end of file diff --git a/RxSwift/Observables/Observable+StandardSequenceOperators.swift b/RxSwift/Observables/Observable+StandardSequenceOperators.swift index 021f6c65..a091206a 100644 --- a/RxSwift/Observables/Observable+StandardSequenceOperators.swift +++ b/RxSwift/Observables/Observable+StandardSequenceOperators.swift @@ -34,9 +34,9 @@ extension ObservableType { - parameter predicate: A function to test each element for a condition. - returns: An observable sequence that contains the elements from the input sequence that occur before the element at which the test no longer passes. */ - public func takeWhile(predicate: (E) -> Bool) + public func takeWhile(predicate: (E) throws -> Bool) -> Observable { - return TakeWhile(source: self.asObservable(), predicate: predicate) + return TakeWhile(source: asObservable(), predicate: predicate) } /** @@ -47,9 +47,9 @@ extension ObservableType { - parameter predicate: A function to test each element for a condition; the second parameter of the function represents the index of the source element. - returns: An observable sequence that contains the elements from the input sequence that occur before the element at which the test no longer passes. */ - public func takeWhile(predicate: (E, Int) -> Bool) + public func takeWhileWithIndex(predicate: (E, Int) throws -> Bool) -> Observable { - return TakeWhile(source: self.asObservable(), predicate: predicate) + return TakeWhile(source: asObservable(), predicate: predicate) } } diff --git a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift index 5a27aff3..ad9a0143 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift @@ -524,9 +524,7 @@ extension ObservableStandardSequenceOperators { ]) let res = scheduler.start { () -> Observable in - return xs.takeWhile { (num: Int, index) -> Bool in - return index < 5 - } + return xs.takeWhileWithIndex { num, index in index < 5 } } XCTAssertEqual(res.messages, [ @@ -560,9 +558,7 @@ extension ObservableStandardSequenceOperators { ]) let res = scheduler.start { () -> Observable in - return xs.takeWhile { (num: Int, index) -> Bool in - return index >= 0 - } + return xs.takeWhileWithIndex { num , index in return index >= 0 } } XCTAssertEqual(res.messages, [ @@ -598,9 +594,7 @@ extension ObservableStandardSequenceOperators { ]) let res = scheduler.start { () -> Observable in - return xs.takeWhile { (num: Int, index) -> Bool in - return index >= 0 - } + return xs.takeWhileWithIndex { num, index in index >= 0 } } XCTAssertEqual(res.messages, [ From 3cb53617a08b5a65adc1f69005eef36f69789cc8 Mon Sep 17 00:00:00 2001 From: yury Date: Tue, 13 Oct 2015 11:34:26 +0300 Subject: [PATCH 024/210] Use observer getter instead of _observer --- .../Observables/Implementations/TakeWhile.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/RxSwift/Observables/Implementations/TakeWhile.swift b/RxSwift/Observables/Implementations/TakeWhile.swift index 98cfab5b..370ca233 100644 --- a/RxSwift/Observables/Implementations/TakeWhile.swift +++ b/RxSwift/Observables/Implementations/TakeWhile.swift @@ -31,19 +31,19 @@ class TakeWhileSink : Sin do { _running = try _parent._predicate(value) } catch let e { - _observer?.onError(e) + observer?.onError(e) dispose() return } if _running { - _observer?.onNext(value) + observer?.onNext(value) } else { - _observer?.onComplete() + observer?.onComplete() dispose() } case .Error, .Completed: - _observer?.on(event) + observer?.on(event) dispose() } } @@ -75,19 +75,19 @@ class TakeWhileSinkWithIndex Date: Tue, 13 Oct 2015 11:50:32 +0300 Subject: [PATCH 025/210] Test throw in TakeWhile --- ...rvable+StandardSequenceOperatorsTest.swift | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift index ad9a0143..6e0d45bf 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift @@ -504,6 +504,51 @@ extension ObservableStandardSequenceOperators { XCTAssertEqual(1, invoked) } + func testTakeWhile_Throw() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, -1), + next(110, -1), + next(210, 2), + next(260, 5), + next(290, 13), + next(320, 3), + next(350, 7), + next(390, 4), + next(410, 17), + next(450, 8), + next(500, 23), + completed(600) + ]) + + var invoked = 0 + + let res = scheduler.start() { () -> Observable in + return xs.takeWhile { num in + invoked++ + + if invoked == 3 { + throw testError + } + + return isPrime(num) + } + } + + XCTAssertEqual(res.messages, [ + next(210, 2), + next(260, 5), + error(290, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 290) + ]) + + XCTAssertEqual(3, invoked) + } + func testTakeWhile_Index1() { let scheduler = TestScheduler(initialClock: 0) @@ -612,6 +657,48 @@ extension ObservableStandardSequenceOperators { Subscription(200, 400) ]) } + + + func testTakeWhile_Index_SelectorThrows() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, -1), + next(110, -1), + next(205, 100), + next(210, 2), + next(260, 5), + next(290, 13), + next(320, 3), + next(350, 7), + next(390, 4), + completed(400) + ]) + + let res = scheduler.start { () -> Observable in + return xs.takeWhileWithIndex { num, index in + if index < 5 { + return true + } + + throw testError + } + } + + XCTAssertEqual(res.messages, [ + next(205, 100), + next(210, 2), + next(260, 5), + next(290, 13), + next(320, 3), + error(350, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 350) + ]) + } + } // map From 2e556c209e667cf708d7c9fdf4e00fc4380e468f Mon Sep 17 00:00:00 2001 From: yury Date: Tue, 13 Oct 2015 12:29:49 +0300 Subject: [PATCH 026/210] Make private state of Sink private --- RxSwift/Observables/Implementations/Sink.swift | 14 +++++++------- .../Observables/Implementations/SkipUntil.swift | 6 +++--- .../Observables/Implementations/SkipWhile.swift | 12 ++++++------ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/RxSwift/Observables/Implementations/Sink.swift b/RxSwift/Observables/Implementations/Sink.swift index 48ec651b..c7b16261 100644 --- a/RxSwift/Observables/Implementations/Sink.swift +++ b/RxSwift/Observables/Implementations/Sink.swift @@ -9,22 +9,22 @@ import Foundation class Sink : Disposable { - private var lock = SpinLock() + private var _lock = SpinLock() // state - var _observer: O? - var _cancel: Disposable - var _disposed: Bool = false + private var _observer: O? + private var _cancel: Disposable + private var _disposed: Bool = false var observer: O? { get { - return lock.calculateLocked { _observer } + return _lock.calculateLocked { _observer } } } var cancel: Disposable { get { - return lock.calculateLocked { _cancel } + return _lock.calculateLocked { _cancel } } } @@ -37,7 +37,7 @@ class Sink : Disposable { } func dispose() { - let cancel: Disposable? = lock.calculateLocked { + let cancel: Disposable? = _lock.calculateLocked { if _disposed { return nil } diff --git a/RxSwift/Observables/Implementations/SkipUntil.swift b/RxSwift/Observables/Implementations/SkipUntil.swift index 0a111999..6ce123ce 100644 --- a/RxSwift/Observables/Implementations/SkipUntil.swift +++ b/RxSwift/Observables/Implementations/SkipUntil.swift @@ -41,7 +41,7 @@ class SkipUntilSinkOther : Sin do { _running = try !_parent._predicate(value) } catch let e { - _observer?.onError(e) + observer?.onError(e) dispose() return } } if _running { - _observer?.onNext(value) + observer?.onNext(value) } case .Error, .Completed: - _observer?.on(event) + observer?.on(event) dispose() } } @@ -64,17 +64,17 @@ class SkipWhileSinkWithIndex Date: Wed, 14 Oct 2015 18:40:51 -0700 Subject: [PATCH 027/210] References #193 about adding tvOS focused rx_ helpers to UICollectionView --- RxCocoa/iOS/UICollectionView+Rx.swift | 66 ++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/RxCocoa/iOS/UICollectionView+Rx.swift b/RxCocoa/iOS/UICollectionView+Rx.swift index 92d1518b..d5ba452c 100644 --- a/RxCocoa/iOS/UICollectionView+Rx.swift +++ b/RxCocoa/iOS/UICollectionView+Rx.swift @@ -152,5 +152,69 @@ extension UICollectionView { return ControlEvent(source: source) } } - +#endif + +#if os(tvOS) + +extension UICollectionView { + + /** + Reactive wrapper for `delegate` message `collectionView:didUpdateFocusInContext:withAnimationCoordinator:`. + */ + public var rx_nextFocusedUpdated: ControlEvent { + let source = rx_delegate.observe("collectionView:didUpdateFocusInContext:withAnimationCoordinator:") + .map { a -> NSIndexPath in + let context = a[1] as! UICollectionViewFocusUpdateContext + return context.nextFocusedIndexPath! as NSIndexPath + } + + return ControlEvent(source: source) + } + + /** + Reactive wrapper for `delegate` message `collectionView:didUpdateFocusInContext:withAnimationCoordinator:`. + */ + public var rx_previousFocusedUpdated: ControlEvent { + let source = rx_delegate.observe("collectionView:didUpdateFocusInContext:withAnimationCoordinator:") + .map { a -> NSIndexPath in + let context = a[1] as! UICollectionViewFocusUpdateContext + return context.previouslyFocusedIndexPath! as NSIndexPath + } + + return ControlEvent(source: source) + } + + /** + Reactive wrapper for `delegate` message `collectionView:didUpdateFocusInContext:withAnimationCoordinator:` + that returns the next focused NSIndexPath. + + It can be only used when one of the `rx_itemsWith*` methods is used to bind observable sequence. + */ + public func rx_modelForNextFocusedUpdated() -> ControlEvent { + let source: Observable = rx_nextFocusedUpdated .map { indexPath in + let dataSource: RxCollectionViewReactiveArrayDataSource = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_itemsWith*` methods was used.") + + return dataSource.modelAtIndex(indexPath.item)! + } + + return ControlEvent(source: source) + } + + /** + Reactive wrapper for `delegate` message `collectionView:didUpdateFocusInContext:withAnimationCoordinator:` + that returns the next focused NSIndexPath. + + It can be only used when one of the `rx_itemsWith*` methods is used to bind observable sequence. + */ + public func rx_modelForPreviouslyFocusedUpdated() -> ControlEvent { + let source: Observable = rx_previousFocusedUpdated .map { indexPath in + let dataSource: RxCollectionViewReactiveArrayDataSource = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_itemsWith*` methods was used.") + + return dataSource.modelAtIndex(indexPath.item)! + } + + return ControlEvent(source: source) + } +} + #endif From 36dad62830ccd2313b3b172f3cad363eb473a396 Mon Sep 17 00:00:00 2001 From: yury Date: Thu, 15 Oct 2015 12:27:03 +0300 Subject: [PATCH 028/210] Check for overflow on operators with index --- RxSwift/Error.swift | 3 +++ RxSwift/Observables/Implementations/FlatMap.swift | 5 +++-- RxSwift/Observables/Implementations/Map.swift | 4 ++-- RxSwift/Observables/Implementations/SkipWhile.swift | 2 +- RxSwift/Observables/Implementations/TakeWhile.swift | 2 +- RxSwift/Rx.swift | 9 +++++++++ 6 files changed, 19 insertions(+), 6 deletions(-) diff --git a/RxSwift/Error.swift b/RxSwift/Error.swift index a907583d..acb7afff 100644 --- a/RxSwift/Error.swift +++ b/RxSwift/Error.swift @@ -22,6 +22,7 @@ public enum RxErrorCode : Int { case Unknown = 0 case Cast = 2 case Disposed = 3 + case Overflow = 4 } /** @@ -42,5 +43,7 @@ public struct RxError { Singleton instance of doing something on a disposed object */ public static let DisposedError = NSError(domain: RxErrorDomain, code: RxErrorCode.Disposed.rawValue, userInfo: nil) + + public static let OverflowError = NSError(domain: RxErrorDomain, code: RxErrorCode.Overflow.rawValue, userInfo: nil) } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/FlatMap.swift b/RxSwift/Observables/Implementations/FlatMap.swift index 8f21ef7d..7395e01a 100644 --- a/RxSwift/Observables/Implementations/FlatMap.swift +++ b/RxSwift/Observables/Implementations/FlatMap.swift @@ -144,14 +144,15 @@ class FlatMapSink1 : FlatMapSink { - var index = 0 + + private var _index = 0 override init(parent: Parent, observer: O, cancel: Disposable) { super.init(parent: parent, observer: observer, cancel: cancel) } override func performMap(element: SourceType) throws -> S { - return try self.parent.selector2!(element, index++) + return try parent.selector2!(element, try incrementChecked(&_index)) } } diff --git a/RxSwift/Observables/Implementations/Map.swift b/RxSwift/Observables/Implementations/Map.swift index 44a3ef7a..58cba636 100644 --- a/RxSwift/Observables/Implementations/Map.swift +++ b/RxSwift/Observables/Implementations/Map.swift @@ -62,13 +62,13 @@ class MapSink1 : MapSink { class MapSink2 : MapSink { typealias ResultType = O.E - var index = 0 + private var _index = 0 override init(parent: Map, observer: O, cancel: Disposable) { super.init(parent: parent, observer: observer, cancel: cancel) } override func performMap(element: SourceType) throws -> ResultType { - return try self.parent.selector2!(element, index++) + return try self.parent.selector2!(element, try incrementChecked(&_index)) } } diff --git a/RxSwift/Observables/Implementations/SkipWhile.swift b/RxSwift/Observables/Implementations/SkipWhile.swift index 9c048ffa..36942bec 100644 --- a/RxSwift/Observables/Implementations/SkipWhile.swift +++ b/RxSwift/Observables/Implementations/SkipWhile.swift @@ -62,7 +62,7 @@ class SkipWhileSinkWithIndex Int { + if i == Int.max { + throw RxError.OverflowError + } + let result = i + i += 1 + return result +} + extension NSObject { func rx_synchronized(@noescape action: () -> T) -> T { objc_sync_enter(self) From 59c828e1608cb95ea6d9df15a8938a4a0f85944c Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 15 Oct 2015 13:01:00 +0300 Subject: [PATCH 029/210] println -> print Swift 2 update --- Documentation/Examples.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/Examples.md b/Documentation/Examples.md index 8a8dec63..40adb9b8 100644 --- a/Documentation/Examples.md +++ b/Documentation/Examples.md @@ -56,14 +56,14 @@ let c = combineLatest(a, b) { $0 + $1 } // combines latest values of variabl // To pull values out of rx variable `c`, subscribe to values from `c`. // `subscribeNext` means subscribe to next (fresh) values of variable `c`. // That also includes the inital value "3 is positive". -c.subscribeNext { println($0) } // prints: "3 is positive" +c.subscribeNext { print($0) } // prints: "3 is positive" // Now let's increase the value of `a` // a = 4 is in RxSwift a.next(4) // prints: 6 is positive // Sum of latest values is now `4 + 2`, `6` is >= 0, map operator // produces "6 is positive" and that result is "assigned" to `c`. -// Since the value of `c` changed, `{ println($0) }` will get called, +// Since the value of `c` changed, `{ print($0) }` will get called, // and "6 is positive" is printed. // Now let's change the value of `b` @@ -73,7 +73,7 @@ b.next(-8) // doesn't print anything // get executed. // That means that `c` still contains "6 is positive" and that's correct. // Since `c` hasn't been updated, that means next value hasn't been produced, -// and `{ println($0) }` won't be called. +// and `{ print($0) }` won't be called. // ... ``` From a5f0b4fcafa7d2ef6bcebc607bd4bae8ab66fc39 Mon Sep 17 00:00:00 2001 From: Greg Pardo Date: Thu, 15 Oct 2015 10:36:35 -0400 Subject: [PATCH 030/210] References #193 Allows for nil on focus (Focus can be elsewhere) --- RxCocoa/iOS/UICollectionView+Rx.swift | 30 ++++++++++++++++++--------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/RxCocoa/iOS/UICollectionView+Rx.swift b/RxCocoa/iOS/UICollectionView+Rx.swift index d5ba452c..b0de1120 100644 --- a/RxCocoa/iOS/UICollectionView+Rx.swift +++ b/RxCocoa/iOS/UICollectionView+Rx.swift @@ -161,11 +161,11 @@ extension UICollectionView { /** Reactive wrapper for `delegate` message `collectionView:didUpdateFocusInContext:withAnimationCoordinator:`. */ - public var rx_nextFocusedUpdated: ControlEvent { + public var rx_nextFocusedUpdated: ControlEvent { let source = rx_delegate.observe("collectionView:didUpdateFocusInContext:withAnimationCoordinator:") - .map { a -> NSIndexPath in + .map { a -> NSIndexPath? in let context = a[1] as! UICollectionViewFocusUpdateContext - return context.nextFocusedIndexPath! as NSIndexPath + return context.nextFocusedIndexPath! } return ControlEvent(source: source) @@ -174,11 +174,11 @@ extension UICollectionView { /** Reactive wrapper for `delegate` message `collectionView:didUpdateFocusInContext:withAnimationCoordinator:`. */ - public var rx_previousFocusedUpdated: ControlEvent { + public var rx_previousFocusedUpdated: ControlEvent { let source = rx_delegate.observe("collectionView:didUpdateFocusInContext:withAnimationCoordinator:") - .map { a -> NSIndexPath in + .map { a -> NSIndexPath? in let context = a[1] as! UICollectionViewFocusUpdateContext - return context.previouslyFocusedIndexPath! as NSIndexPath + return context.previouslyFocusedIndexPath! } return ControlEvent(source: source) @@ -190,8 +190,13 @@ extension UICollectionView { It can be only used when one of the `rx_itemsWith*` methods is used to bind observable sequence. */ - public func rx_modelForNextFocusedUpdated() -> ControlEvent { - let source: Observable = rx_nextFocusedUpdated .map { indexPath in + public func rx_modelForNextFocusedUpdated() -> ControlEvent { + let source: Observable = rx_nextFocusedUpdated .map { indexPath in + + guard let indexPath = indexPath else { + return nil + } + let dataSource: RxCollectionViewReactiveArrayDataSource = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_itemsWith*` methods was used.") return dataSource.modelAtIndex(indexPath.item)! @@ -206,8 +211,13 @@ extension UICollectionView { It can be only used when one of the `rx_itemsWith*` methods is used to bind observable sequence. */ - public func rx_modelForPreviouslyFocusedUpdated() -> ControlEvent { - let source: Observable = rx_previousFocusedUpdated .map { indexPath in + public func rx_modelForPreviouslyFocusedUpdated() -> ControlEvent { + let source: Observable = rx_previousFocusedUpdated .map { indexPath in + + guard let indexPath = indexPath else { + return nil + } + let dataSource: RxCollectionViewReactiveArrayDataSource = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_itemsWith*` methods was used.") return dataSource.modelAtIndex(indexPath.item)! From 2958e0a796e85d3b5b7d93bf42bff1ead88664bf Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 20 Sep 2015 23:08:43 +0200 Subject: [PATCH 031/210] Prototype of units. --- Documentation/GettingStarted.md | 4 +- Documentation/Units.md | 22 + README.md | 2 + Rx.xcodeproj/project.pbxproj | 90 +- RxBlocking/Observable+Blocking.swift | 2 +- .../CocoaUnits/ControlEvent+Driver.swift | 29 + .../CocoaUnits/ControlProperty+Driver.swift | 29 + .../Common/CocoaUnits/Driver+Operators.swift | 292 +++ .../CocoaUnits/Driver+Subscription.swift | 80 + RxCocoa/Common/CocoaUnits/Driver.swift | 152 ++ .../ObservableConvertibleType+Driver.swift | 60 + ...Extensions.swift => Observable+Bind.swift} | 12 +- RxCocoa/Common/RxCocoa.swift | 13 +- RxExample/RxExample.xcodeproj/project.pbxproj | 1712 +++++++++-------- .../ViewModels/SearchResultViewModel.swift | 14 +- .../ViewModels/SearchViewModel.swift | 10 +- .../Views/WikipediaSearchCell.swift | 6 +- .../Views/WikipediaSearchViewController.swift | 11 +- .../RxExample/Services/ImageService.swift | 4 +- RxSwift/Concurrency/AsyncLock.swift | 2 +- RxSwift/Observable+Extensions.swift | 36 +- RxSwift/Observable.swift | 2 +- RxSwift/ObservableConvertibleType.swift | 26 + RxSwift/ObservableType.swift | 12 +- .../Observables/Implementations/Catch.swift | 4 +- .../Implementations/CombineLatest.swift | 2 +- .../Observables/Implementations/Concat.swift | 4 +- .../Observables/Implementations/FlatMap.swift | 15 +- RxSwift/Observables/Implementations/Map.swift | 2 +- .../Observables/Implementations/Merge.swift | 14 +- .../Implementations/ObserveOn.swift | 2 + .../Implementations/Producer.swift | 2 +- .../Implementations/SkipUntil.swift | 4 +- .../Observables/Implementations/Switch.swift | 8 +- .../Implementations/TakeUntil.swift | 2 +- RxSwift/Observables/Implementations/Zip.swift | 4 +- RxSwift/Observables/Observable+Multiple.swift | 15 +- RxSwift/Observables/Observable+Single.swift | 14 +- ...Observable+StandardSequenceOperators.swift | 4 +- RxSwift/Observables/Observable+Time.swift | 2 +- RxSwift/Observers/ObserverBase.swift | 3 +- RxSwift/Observers/TailRecursiveSink.swift | 6 +- RxSwift/Rx.swift | 6 +- RxSwift/Schedulers/RecursiveScheduler.swift | 4 +- RxSwift/Subjects/ReplaySubject.swift | 10 +- .../Tests/Observable+CreationTest.swift | 2 +- .../Tests/Observable+TimeTest.swift | 4 +- .../RxSwiftTests/Tests/ObserverTests.swift | 46 +- 48 files changed, 1799 insertions(+), 1002 deletions(-) create mode 100644 Documentation/Units.md create mode 100644 RxCocoa/Common/CocoaUnits/ControlEvent+Driver.swift create mode 100644 RxCocoa/Common/CocoaUnits/ControlProperty+Driver.swift create mode 100644 RxCocoa/Common/CocoaUnits/Driver+Operators.swift create mode 100644 RxCocoa/Common/CocoaUnits/Driver+Subscription.swift create mode 100644 RxCocoa/Common/CocoaUnits/Driver.swift create mode 100644 RxCocoa/Common/CocoaUnits/ObservableConvertibleType+Driver.swift rename RxCocoa/Common/{Observable+CocoaExtensions.swift => Observable+Bind.swift} (80%) create mode 100644 RxSwift/ObservableConvertibleType.swift diff --git a/Documentation/GettingStarted.md b/Documentation/GettingStarted.md index a8f38b47..279f3b3b 100644 --- a/Documentation/GettingStarted.md +++ b/Documentation/GettingStarted.md @@ -6,7 +6,7 @@ This project tries to be consistent with [ReactiveX.io](http://reactivex.io/). T 1. [Observables aka Sequences](#observables-aka-sequences) 1. [Disposing](#disposing) 1. [Implicit `Observable` guarantees](#implicit-observable-guarantees) -1. [Creating your first `Observable` (aka sequence producers)](#creating-your-own-observable-aka-sequence-producers) +1. [Creating your first `Observable` (aka observable sequence)](#creating-your-own-observable-aka-observable-sequence) 1. [Creating an `Observable` that performs work](#creating-an-observable-that-performs-work) 1. [Sharing subscription and `shareReplay` operator](#sharing-subscription-and-sharereplay-operator) 1. [Operators](#operators) @@ -265,7 +265,7 @@ Event processing ended Event processing ended ``` -## Creating your own `Observable` (aka sequence producers) +## Creating your own `Observable` (aka observable sequence) There is one crucial thing to understand about observables. diff --git a/Documentation/Units.md b/Documentation/Units.md new file mode 100644 index 00000000..904474aa --- /dev/null +++ b/Documentation/Units.md @@ -0,0 +1,22 @@ +Units +===== + +This document will try to describe what are units, why are they a useful concept, how to use them and how to create them. + +* [Why](#why) +* [Design Rationale](#design-rationale) +* ... + +# Why + +The purpose of units is to use the Swift compiler static type checking to prove your code is behaving like designed. + +RxCocoa project already contains several units, but the most elaborate one is called `Driver`, so this unit will be used to explain the idea behind units. + +`Driver` was named that way because it describes sequences that drive certain parts of the app. Those sequences will usually drive UI bindings, UI event pumps that keep your application responsive but also drive application services, etc. + +The purpose of `Driver` unit is to ensure the underlying observable sequence has the following properties. + +* can't fail, all failures are being handled properly +* elements are delivered on main thread +* sequence computation resources are shared diff --git a/README.md b/README.md index 30b1ef8b..4b03fe58 100644 --- a/README.md +++ b/README.md @@ -104,11 +104,13 @@ Hang out with us on [rxswift.slack.com](http://slack.rxswift.org) "; }; C8093F581B8A73A20088E94D /* Observable+Blocking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Blocking.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C8093F591B8A73A20088E94D /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - C80D338E1B91EF9E0014629D /* Observable+CocoaExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+CocoaExtensions.swift"; sourceTree = ""; }; + C80D338E1B91EF9E0014629D /* Observable+Bind.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Bind.swift"; sourceTree = ""; }; C80D33931B922FB00014629D /* ControlEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlEvent.swift; sourceTree = ""; }; C80D33941B922FB00014629D /* ControlProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlProperty.swift; sourceTree = ""; }; C80D342D1B9245A40014629D /* CombineLatest+CollectionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CombineLatest+CollectionType.swift"; sourceTree = ""; }; C821DBA11BA4DCAB008F3809 /* Buffer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Buffer.swift; sourceTree = ""; }; + C8226BC11BADDD3600D7F20C /* Driver+Subscription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Driver+Subscription.swift"; sourceTree = ""; }; + C8226BC61BADE87100D7F20C /* ControlProperty+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControlProperty+Driver.swift"; sourceTree = ""; }; + C8226BC91BADE87D00D7F20C /* ControlEvent+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControlEvent+Driver.swift"; sourceTree = ""; }; + C8226BCC1BADE8D600D7F20C /* ObservableConvertibleType+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObservableConvertibleType+Driver.swift"; sourceTree = ""; }; + C8226BCF1BADEBDF00D7F20C /* Driver+Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Driver+Operators.swift"; sourceTree = ""; }; + C836E8E51BA2165500AFEF77 /* Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Driver.swift; sourceTree = ""; }; + C849BE2A1BAB5D070019AD27 /* ObservableConvertibleType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableConvertibleType.swift; sourceTree = ""; }; C84B38E71BA43380001B7D88 /* ScheduledItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduledItem.swift; sourceTree = ""; }; C84B38ED1BA433CD001B7D88 /* Generate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Generate.swift; sourceTree = ""; }; C86409FB1BA593F500D3C4E8 /* Range.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Range.swift; sourceTree = ""; }; @@ -1012,6 +1047,7 @@ C8093C651B8A72BE0088E94D /* ImmediateSchedulerType.swift */, C8093C681B8A72BE0088E94D /* Observable.swift */, C8093C671B8A72BE0088E94D /* Observable+Extensions.swift */, + C849BE2A1BAB5D070019AD27 /* ObservableConvertibleType.swift */, C8093C9E1B8A72BE0088E94D /* ObservableType.swift */, C8093CA01B8A72BE0088E94D /* ObserverOf.swift */, C8093CAB1B8A72BE0088E94D /* ObserverType.swift */, @@ -1205,7 +1241,7 @@ C8093E871B8A732E0088E94D /* _RXKVOObserver.m */, C8093E881B8A732E0088E94D /* _RXSwizzling.h */, C8093E891B8A732E0088E94D /* _RXSwizzling.m */, - C80D338E1B91EF9E0014629D /* Observable+CocoaExtensions.swift */, + C80D338E1B91EF9E0014629D /* Observable+Bind.swift */, C8093E8A1B8A732E0088E94D /* CLLocationManager+Rx.swift */, C8093E8B1B8A732E0088E94D /* DelegateProxy.swift */, C8093E8C1B8A732E0088E94D /* DelegateProxyType.swift */, @@ -1276,6 +1312,12 @@ C80D33911B922FB00014629D /* CocoaUnits */ = { isa = PBXGroup; children = ( + C836E8E51BA2165500AFEF77 /* Driver.swift */, + C8226BCF1BADEBDF00D7F20C /* Driver+Operators.swift */, + C8226BC11BADDD3600D7F20C /* Driver+Subscription.swift */, + C8226BC61BADE87100D7F20C /* ControlProperty+Driver.swift */, + C8226BC91BADE87D00D7F20C /* ControlEvent+Driver.swift */, + C8226BCC1BADE8D600D7F20C /* ObservableConvertibleType+Driver.swift */, C80D33931B922FB00014629D /* ControlEvent.swift */, C80D33941B922FB00014629D /* ControlProperty.swift */, ); @@ -1886,11 +1928,14 @@ C882542E1B8A752B00B02D69 /* UILabel+Rx.swift in Sources */, C88254211B8A752B00B02D69 /* RxSearchBarDelegateProxy.swift in Sources */, C882541D1B8A752B00B02D69 /* RxAlertViewDelegateProxy.swift in Sources */, - C80D338F1B91EF9E0014629D /* Observable+CocoaExtensions.swift in Sources */, + C80D338F1B91EF9E0014629D /* Observable+Bind.swift in Sources */, C88254311B8A752B00B02D69 /* UISegmentedControl+Rx.swift in Sources */, + C8226BD01BADEBDF00D7F20C /* Driver+Operators.swift in Sources */, + C8226BC41BADDF2800D7F20C /* Driver+Subscription.swift in Sources */, C8093EED1B8A732E0088E94D /* KVOObservable.swift in Sources */, C88254281B8A752B00B02D69 /* UIButton+Rx.swift in Sources */, C8093EDF1B8A732E0088E94D /* CLLocationManager+Rx.swift in Sources */, + C8226BC71BADE87100D7F20C /* ControlProperty+Driver.swift in Sources */, C8093EEB1B8A732E0088E94D /* DeinitAction.swift in Sources */, C882541C1B8A752B00B02D69 /* RxActionSheetDelegateProxy.swift in Sources */, C8093ED51B8A732E0088E94D /* _RXDelegateProxy.m in Sources */, @@ -1900,11 +1945,13 @@ C88254251B8A752B00B02D69 /* UIActionSheet+Rx.swift in Sources */, C80D339A1B922FB00014629D /* ControlProperty.swift in Sources */, C882542B1B8A752B00B02D69 /* UIDatePicker+Rx.swift in Sources */, + C8226BCD1BADE8D600D7F20C /* ObservableConvertibleType+Driver.swift in Sources */, C88254221B8A752B00B02D69 /* RxTableViewDataSourceProxy.swift in Sources */, C8093EDD1B8A732E0088E94D /* _RXSwizzling.m in Sources */, C8093EE91B8A732E0088E94D /* Deallocating.swift in Sources */, C882542C1B8A752B00B02D69 /* UIGestureRecognizer+Rx.swift in Sources */, C8093EE11B8A732E0088E94D /* DelegateProxy.swift in Sources */, + C8226BCA1BADE87D00D7F20C /* ControlEvent+Driver.swift in Sources */, C8093EF91B8A732E0088E94D /* RxCLLocationManagerDelegateProxy.swift in Sources */, C88254331B8A752B00B02D69 /* UISwitch+Rx.swift in Sources */, C8093EE51B8A732E0088E94D /* Logging.swift in Sources */, @@ -1912,6 +1959,7 @@ C882541A1B8A752B00B02D69 /* RxCollectionViewDataSourceType.swift in Sources */, C8093EF11B8A732E0088E94D /* NSNotificationCenter+Rx.swift in Sources */, C88254351B8A752B00B02D69 /* UITextField+Rx.swift in Sources */, + C836E8E71BA2165500AFEF77 /* Driver.swift in Sources */, C8093EF71B8A732E0088E94D /* NSURLSession+Rx.swift in Sources */, C8093EE71B8A732E0088E94D /* ControlTarget.swift in Sources */, C88254301B8A752B00B02D69 /* UISearchBar+Rx.swift in Sources */, @@ -1934,24 +1982,30 @@ C8093EFC1B8A732E0088E94D /* RxCocoa.swift in Sources */, C80D33991B922FB00014629D /* ControlEvent.swift in Sources */, C80D339B1B922FB00014629D /* ControlProperty.swift in Sources */, + C8226BCE1BADE8D600D7F20C /* ObservableConvertibleType+Driver.swift in Sources */, C8093EF41B8A732E0088E94D /* NSObject+Rx+CoreGraphics.swift in Sources */, + C836E8E81BA2165500AFEF77 /* Driver.swift in Sources */, C8093EF01B8A732E0088E94D /* KVOObserver.swift in Sources */, C8093EEE1B8A732E0088E94D /* KVOObservable.swift in Sources */, C8093EE01B8A732E0088E94D /* CLLocationManager+Rx.swift in Sources */, C8093EEC1B8A732E0088E94D /* DeinitAction.swift in Sources */, + C8226BD11BADEBDF00D7F20C /* Driver+Operators.swift in Sources */, + C8226BC51BADDF2800D7F20C /* Driver+Subscription.swift in Sources */, C8093F461B8A732E0088E94D /* NSButton+Rx.swift in Sources */, C8093ED61B8A732E0088E94D /* _RXDelegateProxy.m in Sources */, C8093EF61B8A732E0088E94D /* NSObject+Rx.swift in Sources */, + C8226BC81BADE87100D7F20C /* ControlProperty+Driver.swift in Sources */, C8093EDE1B8A732E0088E94D /* _RXSwizzling.m in Sources */, C8093EEA1B8A732E0088E94D /* Deallocating.swift in Sources */, C8093EE21B8A732E0088E94D /* DelegateProxy.swift in Sources */, C8093EFA1B8A732E0088E94D /* RxCLLocationManagerDelegateProxy.swift in Sources */, C8093EE61B8A732E0088E94D /* Logging.swift in Sources */, C8093EF21B8A732E0088E94D /* NSNotificationCenter+Rx.swift in Sources */, + C8226BCB1BADE87D00D7F20C /* ControlEvent+Driver.swift in Sources */, C8093EF81B8A732E0088E94D /* NSURLSession+Rx.swift in Sources */, C8093F4C1B8A732E0088E94D /* NSSlider+Rx.swift in Sources */, C8093EE81B8A732E0088E94D /* ControlTarget.swift in Sources */, - C80D33901B91EF9E0014629D /* Observable+CocoaExtensions.swift in Sources */, + C80D33901B91EF9E0014629D /* Observable+Bind.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1981,6 +2035,7 @@ C8093CE61B8A72BE0088E94D /* NopDisposable.swift in Sources */, C8093CD41B8A72BE0088E94D /* Disposable.swift in Sources */, C8093CEE1B8A72BE0088E94D /* SingleAssignmentDisposable.swift in Sources */, + C849BE2C1BAB5D070019AD27 /* ObservableConvertibleType.swift in Sources */, C8C3DA0A1B93941E004D233E /* FailWith.swift in Sources */, C8093D9C1B8A72BE0088E94D /* SchedulerServices+Emulation.swift in Sources */, C8093D6A1B8A72BE0088E94D /* ObserverOf.swift in Sources */, @@ -2095,6 +2150,7 @@ C8093CE51B8A72BE0088E94D /* NopDisposable.swift in Sources */, C8093CD31B8A72BE0088E94D /* Disposable.swift in Sources */, C8093CED1B8A72BE0088E94D /* SingleAssignmentDisposable.swift in Sources */, + C849BE2B1BAB5D070019AD27 /* ObservableConvertibleType.swift in Sources */, C8C3DA091B93941E004D233E /* FailWith.swift in Sources */, C8093D9B1B8A72BE0088E94D /* SchedulerServices+Emulation.swift in Sources */, C8093D691B8A72BE0088E94D /* ObserverOf.swift in Sources */, @@ -2209,6 +2265,7 @@ C8F0BF951BBBFB8B001B112F /* NopDisposable.swift in Sources */, C8F0BF961BBBFB8B001B112F /* Disposable.swift in Sources */, C8F0BF971BBBFB8B001B112F /* SingleAssignmentDisposable.swift in Sources */, + C89461751BC6C1210055219D /* ObservableConvertibleType.swift in Sources */, C8F0BF981BBBFB8B001B112F /* FailWith.swift in Sources */, C8F0BF991BBBFB8B001B112F /* SchedulerServices+Emulation.swift in Sources */, C8F0BF9A1BBBFB8B001B112F /* ObserverOf.swift in Sources */, @@ -2331,6 +2388,8 @@ C8F0C0111BBBFBB9001B112F /* UIStepper+Rx.swift in Sources */, C8F0C0121BBBFBB9001B112F /* UIImageView+Rx.swift in Sources */, C8F0C0131BBBFBB9001B112F /* ControlEvent.swift in Sources */, + C8945FE91BC6C09D0055219D /* ControlEvent+Driver.swift in Sources */, + C8945FE71BC6C09D0055219D /* Driver+Subscription.swift in Sources */, C8F0C0141BBBFBB9001B112F /* NSObject+Rx+CoreGraphics.swift in Sources */, C8F0C0151BBBFBB9001B112F /* UIControl+Rx.swift in Sources */, C8F0C0161BBBFBB9001B112F /* UITableView+Rx.swift in Sources */, @@ -2342,7 +2401,8 @@ C8F0C01C1BBBFBB9001B112F /* UILabel+Rx.swift in Sources */, C8F0C01D1BBBFBB9001B112F /* RxSearchBarDelegateProxy.swift in Sources */, C8F0C01E1BBBFBB9001B112F /* RxAlertViewDelegateProxy.swift in Sources */, - C8F0C01F1BBBFBB9001B112F /* Observable+CocoaExtensions.swift in Sources */, + C8F0C01F1BBBFBB9001B112F /* Observable+Bind.swift in Sources */, + C8945FEA1BC6C09D0055219D /* ObservableConvertibleType+Driver.swift in Sources */, C8F0C0201BBBFBB9001B112F /* UISegmentedControl+Rx.swift in Sources */, C8F0C0211BBBFBB9001B112F /* KVOObservable.swift in Sources */, C8F0C0221BBBFBB9001B112F /* UIButton+Rx.swift in Sources */, @@ -2358,10 +2418,13 @@ C8F0C02C1BBBFBB9001B112F /* UIDatePicker+Rx.swift in Sources */, C8F0C02D1BBBFBB9001B112F /* RxTableViewDataSourceProxy.swift in Sources */, C8F0C02E1BBBFBB9001B112F /* _RXSwizzling.m in Sources */, + C8945FE61BC6C09D0055219D /* Driver+Operators.swift in Sources */, C8F0C02F1BBBFBB9001B112F /* Deallocating.swift in Sources */, C8F0C0301BBBFBB9001B112F /* UIGestureRecognizer+Rx.swift in Sources */, C8F0C0311BBBFBB9001B112F /* DelegateProxy.swift in Sources */, C8F0C0321BBBFBB9001B112F /* RxCLLocationManagerDelegateProxy.swift in Sources */, + C8945FE51BC6C09D0055219D /* Driver.swift in Sources */, + C8945FE81BC6C09D0055219D /* ControlProperty+Driver.swift in Sources */, C8F0C0331BBBFBB9001B112F /* UISwitch+Rx.swift in Sources */, C8F0C0341BBBFBB9001B112F /* Logging.swift in Sources */, C8F0C0351BBBFBB9001B112F /* UICollectionView+Rx.swift in Sources */, @@ -2402,6 +2465,8 @@ D2138C951BB9BEDA00339B5C /* NSObject+Rx.swift in Sources */, D2138C881BB9BEBE00339B5C /* DelegateProxy.swift in Sources */, D203C5101BB9C53E00D02D00 /* UISwitch+Rx.swift in Sources */, + C8945FE31BC6C09D0055219D /* ControlEvent+Driver.swift in Sources */, + C8945FE11BC6C09D0055219D /* Driver+Subscription.swift in Sources */, D203C5121BB9C53E00D02D00 /* UITextField+Rx.swift in Sources */, D203C4F91BB9C53700D02D00 /* RxAlertViewDelegateProxy.swift in Sources */, D203C4F31BB9C4CA00D02D00 /* RxCollectionViewReactiveArrayDataSource.swift in Sources */, @@ -2414,6 +2479,7 @@ D2138C8E1BB9BED600339B5C /* ControlTarget.swift in Sources */, D203C5041BB9C53E00D02D00 /* UIButton+Rx.swift in Sources */, D2138C891BB9BEBE00339B5C /* DelegateProxyType.swift in Sources */, + C8945FE41BC6C09D0055219D /* ObservableConvertibleType+Driver.swift in Sources */, D2138C921BB9BED600339B5C /* KVOObserver.swift in Sources */, D2138C831BB9BEBE00339B5C /* _RXKVOObserver.m in Sources */, D203C5061BB9C53E00D02D00 /* UIControl+Rx.swift in Sources */, @@ -2426,13 +2492,16 @@ D203C5071BB9C53E00D02D00 /* UIDatePicker+Rx.swift in Sources */, D2138C941BB9BEDA00339B5C /* NSObject+Rx+CoreGraphics.swift in Sources */, D203C50D1BB9C53E00D02D00 /* UISegmentedControl+Rx.swift in Sources */, - D2138C861BB9BEBE00339B5C /* Observable+CocoaExtensions.swift in Sources */, + D2138C861BB9BEBE00339B5C /* Observable+Bind.swift in Sources */, D203C50A1BB9C53E00D02D00 /* UILabel+Rx.swift in Sources */, D2138C901BB9BED600339B5C /* DeinitAction.swift in Sources */, + C8945FE01BC6C09D0055219D /* Driver+Operators.swift in Sources */, D203C4F51BB9C52900D02D00 /* ItemEvents.swift in Sources */, D2138C911BB9BED600339B5C /* KVOObservable.swift in Sources */, D203C4FA1BB9C53700D02D00 /* RxCollectionViewDataSourceProxy.swift in Sources */, D2138C7F1BB9BEBE00339B5C /* _RX.m in Sources */, + C8945FDF1BC6C09D0055219D /* Driver.swift in Sources */, + C8945FE21BC6C09D0055219D /* ControlProperty+Driver.swift in Sources */, D203C4FE1BB9C53700D02D00 /* RxTableViewDataSourceProxy.swift in Sources */, D203C5001BB9C53700D02D00 /* RxTextViewDelegateProxy.swift in Sources */, D203C5091BB9C53E00D02D00 /* UIImageView+Rx.swift in Sources */, @@ -2457,6 +2526,7 @@ D2EBEAEB1BB9B69E003A27DC /* AsyncLock.swift in Sources */, D2EBEB281BB9B6C1003A27DC /* Zip.swift in Sources */, D2EBEB3E1BB9B6D8003A27DC /* SerialDispatchQueueScheduler.swift in Sources */, + C89461761BC6C1220055219D /* ObservableConvertibleType.swift in Sources */, D2EBEAF71BB9B6B2003A27DC /* ScheduledDisposable.swift in Sources */, D2EBEAE11BB9B697003A27DC /* ImmediateSchedulerType.swift in Sources */, D2EBEB0B1BB9B6C1003A27DC /* Empty.swift in Sources */, diff --git a/RxBlocking/Observable+Blocking.swift b/RxBlocking/Observable+Blocking.swift index 799cbf98..d58b6d56 100644 --- a/RxBlocking/Observable+Blocking.swift +++ b/RxBlocking/Observable+Blocking.swift @@ -22,7 +22,7 @@ extension ObservableType { public func toArray() throws -> [E] { let condition = NSCondition() - var elements = [E]() + var elements: [E] = Array() var error: ErrorType? diff --git a/RxCocoa/Common/CocoaUnits/ControlEvent+Driver.swift b/RxCocoa/Common/CocoaUnits/ControlEvent+Driver.swift new file mode 100644 index 00000000..b904db7b --- /dev/null +++ b/RxCocoa/Common/CocoaUnits/ControlEvent+Driver.swift @@ -0,0 +1,29 @@ +// +// ControlEvent+Driver.swift +// Rx +// +// Created by Krunoslav Zaher on 9/19/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE +import RxSwift +#endif + +extension ControlEvent { + /** + Converts `ControlEvent` to `Driver` unit. + + `ControlEvent` already can't fail, so no special case needs to be handled. + */ + public func asDriver() -> Driver { + return self.asDriver { (error) -> Driver in + #if DEBUG + rxFatalError("Somehow driver received error from a pipe that was marked as ") + #else + return Drive.empty() + #endif + } + } +} \ No newline at end of file diff --git a/RxCocoa/Common/CocoaUnits/ControlProperty+Driver.swift b/RxCocoa/Common/CocoaUnits/ControlProperty+Driver.swift new file mode 100644 index 00000000..88db2fd4 --- /dev/null +++ b/RxCocoa/Common/CocoaUnits/ControlProperty+Driver.swift @@ -0,0 +1,29 @@ +// +// ControlProperty+Driver.swift +// Rx +// +// Created by Krunoslav Zaher on 9/19/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE +import RxSwift +#endif + +extension ControlProperty { + /** + Converts `ControlProperty` to `Driver` unit. + + `ControlProperty` already can't fail, so no special case needs to be handled. + */ + public func asDriver() -> Driver { + return self.asDriver { (error) -> Driver in + #if DEBUG + rxFatalError("Somehow driver received error from a pipe that was marked as ") + #else + return Drive.empty() + #endif + } + } +} \ No newline at end of file diff --git a/RxCocoa/Common/CocoaUnits/Driver+Operators.swift b/RxCocoa/Common/CocoaUnits/Driver+Operators.swift new file mode 100644 index 00000000..3b8a74ba --- /dev/null +++ b/RxCocoa/Common/CocoaUnits/Driver+Operators.swift @@ -0,0 +1,292 @@ +// +// Driver+Operators.swift +// Rx +// +// Created by Krunoslav Zaher on 9/19/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE +import RxSwift +#endif + +extension Driver { + + /** + Projects each element of an observable sequence into a new form. + + - parameter selector: A transform function to apply to each source element. + - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source. + */ + public func map(selector: E -> R) -> Driver { + let source = _source + .map(selector) + return Driver(source) + } + + /** + Projects each element of an observable sequence into a new form by incorporating the element's index. + + - parameter selector: A transform function to apply to each source element; the second parameter of the function represents the index of the source element. + - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source. + */ + public func mapWithIndex(selector: (E, Int) -> R) -> Driver { + let source = _source + .mapWithIndex(selector) + return Driver(source) + } + + /** + Filters the elements of an observable sequence based on a predicate. + + - parameter predicate: A function to test each source element for a condition. + - returns: An observable sequence that contains elements from the input sequence that satisfy the condition. + */ + public func filter(predicate: (E) -> Bool) -> Driver { + let source = _source + .filter(predicate) + return Driver(source) + } +} + +extension Driver where Element : DriverConvertibleType { + + /** + Transforms an observable sequence of observable sequences into an observable sequence + producing values only from the most recent observable sequence. + + Each time a new inner observable sequence is received, unsubscribe from the + previous inner observable sequence. + + - returns: The observable sequence that at any point in time produces the elements of the most recent inner observable sequence that has been received. + */ + public func switchLatest() -> Driver { + let source: Observable = _source + .map { $0.asDriver() } + .switchLatest() + return Driver(source) + } +} + +extension Driver { + + /** + Invokes an action for each event in the observable sequence, and propagates all observer messages through the result sequence. + + - parameter eventHandler: Action to invoke for each event in the observable sequence. + - returns: The source sequence with the side-effecting behavior applied. + */ + public func doOn(eventHandler: (Event) -> Void) + -> Driver { + let source = _source + .doOn(eventHandler) + + return Driver(source) + } + + /** + Invokes an action for each event in the observable sequence, and propagates all observer messages through the result sequence. + + - parameter onNext: Action to invoke for each element in the observable sequence. + - parameter onError: Action to invoke upon errored termination of the observable sequence. + - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence. + - returns: The source sequence with the side-effecting behavior applied. + */ + public func doOn(onNext onNext: (E -> Void)? = nil, onError: (ErrorType -> Void)? = nil, onCompleted: (() -> Void)? = nil) + -> Driver { + let source = _source + .doOn(onNext: onNext, onError: onError, onCompleted: onCompleted) + + return Driver(source) + } +} + +extension Driver { + + /** + Prints received events for all observers on standard output. + + - parameter identifier: Identifier that is printed together with event description to standard output. + - returns: An observable sequence whose events are printed to standard output. + */ + public func debug(identifier: String = "\(__FILE__):\(__LINE__)") -> Driver { + let source = _source + .debug(identifier) + return Driver(source) + } +} + +extension Driver where Element: Equatable { + + /** + Returns an observable sequence that contains only distinct contiguous elements according to equality operator. + + - returns: An observable sequence only containing the distinct contiguous elements, based on equality operator, from the source sequence. + */ + public func distinctUntilChanged() + -> Driver { + let source = _source + .self.distinctUntilChanged({ $0 }, comparer: { ($0 == $1) }) + + return Driver(source) + } +} + +extension Driver { + + /** + Returns an observable sequence that contains only distinct contiguous elements according to the `keySelector`. + + - parameter keySelector: A function to compute the comparison key for each element. + - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value, from the source sequence. + */ + public func distinctUntilChanged(keySelector: (E) -> K) -> Driver { + let source = _source + .distinctUntilChanged(keySelector, comparer: { $0 == $1 }) + return Driver(source) + } + + /** + Returns an observable sequence that contains only distinct contiguous elements according to the `comparer`. + + - parameter comparer: Equality comparer for computed key values. + - returns: An observable sequence only containing the distinct contiguous elements, based on `comparer`, from the source sequence. + */ + public func distinctUntilChanged(comparer: (lhs: E, rhs: E) -> Bool) -> Driver { + let source = _source + .distinctUntilChanged({ $0 }, comparer: comparer) + return Driver(source) + } + + /** + Returns an observable sequence that contains only distinct contiguous elements according to the keySelector and the comparer. + + - parameter keySelector: A function to compute the comparison key for each element. + - parameter comparer: Equality comparer for computed key values. + - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value and the comparer, from the source sequence. + */ + public func distinctUntilChanged(keySelector: (E) -> K, comparer: (lhs: K, rhs: K) -> Bool) -> Driver { + let source = _source + .distinctUntilChanged(keySelector, comparer: comparer) + return Driver(source) + } +} + + +extension Driver { + + /** + Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence. + + - parameter selector: A transform function to apply to each element. + - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence. + */ + public func flatMap(selector: (E) -> Driver) -> Driver { + let source = _source + .flatMap(selector) + + return Driver(source) + } + + /** + Projects each element of an observable sequence to an observable sequence by incorporating the element's index and merges the resulting observable sequences into one observable sequence. + + - parameter selector: A transform function to apply to each element; the second parameter of the function represents the index of the source element. + - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence. + */ + public func flatMapWithIndex(selector: (E, Int) -> Driver) + -> Driver { + let source = _source + .flatMapWithIndex(selector) + + return Driver(source.asObservable()) + } +} + +// merge +extension Driver where Element : DriverConvertibleType { + + /** + Merges elements from all observable sequences in the given enumerable sequence into a single observable sequence. + + - parameter maxConcurrent: Maximum number of inner observable sequences being subscribed to concurrently. + - returns: The observable sequence that merges the elements of the observable sequences. + */ + public func merge() -> Driver { + let source = _source + .map { $0.asDriver() } + .merge() + return Driver(source) + } + + /** + Merges elements from all inner observable sequences into a single observable sequence, limiting the number of concurrent subscriptions to inner sequences. + + - returns: The observable sequence that merges the elements of the inner sequences. + */ + public func merge(maxConcurrent maxConcurrent: Int) + -> Driver { + let source = _source + .map { $0.asDriver() } + .merge(maxConcurrent: maxConcurrent) + return Driver(source) + } +} + +// throttle +extension Driver { + + /** + Ignores elements from an observable sequence which are followed by another element within a specified relative time duration, using the specified scheduler to run throttling timers. + + `throttle` and `debounce` are synonyms. + + - parameter dueTime: Throttling duration for each element. + - parameter scheduler: Scheduler to run the throttle timers and send events on. + - returns: The throttled sequence. + */ + public func throttle(dueTime: S.TimeInterval, _ scheduler: S) + -> Driver { + let source = _source + .throttle(dueTime, scheduler) + + return Driver(source) + } + + /** + Ignores elements from an observable sequence which are followed by another element within a specified relative time duration, using the specified scheduler to run throttling timers. + + `throttle` and `debounce` are synonyms. + + - parameter dueTime: Throttling duration for each element. + - parameter scheduler: Scheduler to run the throttle timers and send events on. + - returns: The throttled sequence. + */ + public func debounce(dueTime: S.TimeInterval, _ scheduler: S) + -> Driver { + let source = _source + .debounce(dueTime, scheduler) + + return Driver(source) + } +} + +// scan +extension Driver { + /** + Applies an accumulator function over an observable sequence and returns each intermediate result. The specified seed value is used as the initial accumulator value. + + For aggregation behavior with no intermediate results, see `reduce`. + + - parameter seed: The initial accumulator value. + - parameter accumulator: An accumulator function to be invoked on each element. + - returns: An observable sequence containing the accumulated values. + */ + public func scan(seed: A, accumulator: (A, E) -> A) + -> Driver { + let source = _source + .scan(seed, accumulator: accumulator) + return Driver(source) + } +} \ No newline at end of file diff --git a/RxCocoa/Common/CocoaUnits/Driver+Subscription.swift b/RxCocoa/Common/CocoaUnits/Driver+Subscription.swift new file mode 100644 index 00000000..a75d42d6 --- /dev/null +++ b/RxCocoa/Common/CocoaUnits/Driver+Subscription.swift @@ -0,0 +1,80 @@ +// +// Driver+Extensions.swift +// Rx +// +// Created by Krunoslav Zaher on 9/19/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE +import RxSwift +#endif + +extension Driver { + /** + Creates new subscription and sends elements to observer. + + In this form it's equivalent to `subscribe` method, but it communicates intent better. + + - parameter observer: Observer that receives events. + - returns: Disposable object that can be used to unsubscribe the observer from the subject. + */ + public func drive(observer: O) -> Disposable { + return self.asObservable().subscribe(observer) + } + + /** + Subscribes to observable sequence using custom binder function. + + - parameter with: Function used to bind elements from `self`. + - returns: Object representing subscription. + */ + public func drive(transformation: Observable -> R) -> R { + return transformation(self.asObservable()) + } + + /** + Subscribes to observable sequence using custom binder function and final parameter passed to binder function + after `self` is passed. + + public func drive(with: Self -> R1 -> R2, curriedArgument: R1) -> R2 { + return with(self)(curriedArgument) + } + + - parameter with: Function used to bind elements from `self`. + - parameter curriedArgument: Final argument passed to `binder` to finish binding process. + - returns: Object representing subscription. + */ + public func drive(with: Observable -> R1 -> R2, curriedArgument: R1) -> R2 { + return with(self.asObservable())(curriedArgument) + } + + /** + Subscribes an element handler, a completion handler and disposed handler to an observable sequence. + + Error callback is not exposed because `Driver` can't error out. + + - parameter onNext: Action to invoke for each element in the observable sequence. + - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence. + gracefully completed, errored, or if the generation is cancelled by disposing subscription) + - parameter onDisposed: Action to invoke upon any type of termination of sequence (if the sequence has + gracefully completed, errored, or if the generation is cancelled by disposing subscription) + - returns: Subscription object used to unsubscribe from the observable sequence. + */ + public func drive(onNext onNext: ((E) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil) -> Disposable { + return self.asObservable().subscribe(onNext: onNext, onCompleted: onCompleted, onDisposed: onDisposed) + } + + /** + Subscribes an element handler to an observable sequence. + + - parameter onNext: Action to invoke for each element in the observable sequence. + - returns: Subscription object used to unsubscribe from the observable sequence. + */ + public func driveNext(onNext: E -> Void) -> Disposable { + return self.asObservable().subscribeNext(onNext) + } +} + + diff --git a/RxCocoa/Common/CocoaUnits/Driver.swift b/RxCocoa/Common/CocoaUnits/Driver.swift new file mode 100644 index 00000000..74e14a3e --- /dev/null +++ b/RxCocoa/Common/CocoaUnits/Driver.swift @@ -0,0 +1,152 @@ +// +// Driver.swift +// Rx +// +// Created by Krunoslav Zaher on 8/27/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE +import RxSwift +#endif + +/** +A type that can be converted to `Driver`. +*/ +public protocol DriverConvertibleType : ObservableConvertibleType { + + /** + Converts self to `Driver`. + */ + func asDriver() -> Driver +} + +/** + Unit that represents observable sequence with following properties: + + - it never fails + - it delivers events on `MainScheduler.sharedInstance` + - `shareReplay(1)` behavior + - all observers share sequence computation resources + - it's stateful, upon subscription (calling subscribe) last element is immediatelly replayed if it was produced + - computation of elements is reference counted with respect to the number of observers + - if there are no subscribers, it will release sequence computation resources + - it uses lockless versions of optimized operators (main dispatch queue is used as shared lock) + + `Driver` can be considered a builder pattern for observable sequences that drive the application. + + To find out more about units and how to use them, please go to `Documentation/Units.md`. +*/ +public struct Driver : DriverConvertibleType { + public typealias E = Element + + let _source: Observable + + init(_ source: Observable) { + self._source = source.shareReplay(1) + } + + #if EXPANDABLE_DRIVER + public static func createUnsafe(source: O) -> Driver { + return Driver(source.asObservable()) + } + #endif + + public func asObservable() -> Observable { + return _source.subscribeOn(MainScheduler.sharedInstance) + } + + public func asDriver() -> Driver { + return self + } +} + +public struct Drive { + +#if !RX_NO_MODULE + + /** + Returns an empty observable sequence, using the specified scheduler to send out the single `Completed` message. + + - returns: An observable sequence with no elements. + */ + public static func empty() -> Driver { + return Driver(RxSwift.empty()) + } + + /** + Returns a non-terminating observable sequence, which can be used to denote an infinite duration. + + - returns: An observable sequence whose observers will never get called. + */ + public static func never() -> Driver { + return Driver(RxSwift.never()) + } + + /** + Returns an observable sequence that contains a single element. + + - parameter element: Single element in the resulting observable sequence. + - returns: An observable sequence containing the single specified element. + */ + public static func just(element: E) -> Driver { + return Driver(RxSwift.just(element)) + } + +#else + + /** + Returns an empty observable sequence, using the specified scheduler to send out the single `Completed` message. + + - returns: An observable sequence with no elements. + */ + public static func empty() -> Driver { + return Driver(_empty()) + } + + /** + Returns a non-terminating observable sequence, which can be used to denote an infinite duration. + + - returns: An observable sequence whose observers will never get called. + */ + public static func never() -> Driver { + return Driver(_never()) + } + + /** + Returns an observable sequence that contains a single element. + + - parameter element: Single element in the resulting observable sequence. + - returns: An observable sequence containing the single specified element. + */ + public static func just(element: E) -> Driver { + return Driver(_just(element)) + } + +#endif + + public static func sequenceOf(elements: E ...) -> Driver { + let source = elements.asObservable() + return Driver(source) + } + +} + +// name clashes :( + +#if RX_NO_MODULE + +func _empty() -> Observable { + return empty() +} + +func _never() -> Observable { + return never() +} + +func _just(element: E) -> Observable { + return just(element) +} + +#endif diff --git a/RxCocoa/Common/CocoaUnits/ObservableConvertibleType+Driver.swift b/RxCocoa/Common/CocoaUnits/ObservableConvertibleType+Driver.swift new file mode 100644 index 00000000..d6ea653c --- /dev/null +++ b/RxCocoa/Common/CocoaUnits/ObservableConvertibleType+Driver.swift @@ -0,0 +1,60 @@ +// +// ObservableConvertibleType+Driver.swift +// Rx +// +// Created by Krunoslav Zaher on 9/19/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE +import RxSwift +#endif + +extension ObservableConvertibleType { + /** + Converts anything convertible to `Observable` to `Driver` unit. + + - parameter onErrorJustReturn: Element to return in case of error and after that complete the sequence. + - returns: Driving observable sequence. + */ + public func asDriver(onErrorJustReturn onErrorJustReturn: E) -> Driver { + let source = self + .asObservable() + .catchErrorJustReturn(onErrorJustReturn) + .observeOn(MainScheduler.sharedInstance) + return Driver(source) + } + + /** + Converts anything convertible to `Observable` to `Driver` unit. + + - parameter onErrorDriveWith: Driver that continues to drive the sequence in case of error. + - returns: Driving observable sequence. + */ + public func asDriver(onErrorDriveWith onErrorDriveWith: Driver) -> Driver { + let source = self + .asObservable() + .catchError { _ in + onErrorDriveWith.asObservable() + } + .observeOn(MainScheduler.sharedInstance) + return Driver(source) + } + + /** + Converts anything convertible to `Observable` to `Driver` unit. + + - parameter onErrorRecover: Calculates driver that continues to drive the sequence in case of error. + - returns: Driving observable sequence. + */ + public func asDriver(onErrorRecover onErrorRecover: (error: ErrorType) -> Driver) -> Driver { + let source = self + .asObservable() + .catchError { error in + onErrorRecover(error: error).asObservable() + } + .observeOn(MainScheduler.sharedInstance) + return Driver(source) + } +} \ No newline at end of file diff --git a/RxCocoa/Common/Observable+CocoaExtensions.swift b/RxCocoa/Common/Observable+Bind.swift similarity index 80% rename from RxCocoa/Common/Observable+CocoaExtensions.swift rename to RxCocoa/Common/Observable+Bind.swift index 0dce226c..18542552 100644 --- a/RxCocoa/Common/Observable+CocoaExtensions.swift +++ b/RxCocoa/Common/Observable+Bind.swift @@ -1,5 +1,5 @@ // -// Observable+Extensions.swift +// Observable+Bind.swift // Rx // // Created by Krunoslav Zaher on 8/29/15. @@ -51,4 +51,14 @@ extension ObservableType { return binder(self)(curriedArgument) } + + /** + Subscribes an element handler to an observable sequence. + + - parameter onNext: Action to invoke for each element in the observable sequence. + - returns: Subscription object used to unsubscribe from the observable sequence. + */ + public func bindNext(onNext: E -> Void) -> Disposable { + return subscribeNext(onNext) + } } \ No newline at end of file diff --git a/RxCocoa/Common/RxCocoa.swift b/RxCocoa/Common/RxCocoa.swift index fc54151e..80707902 100644 --- a/RxCocoa/Common/RxCocoa.swift +++ b/RxCocoa/Common/RxCocoa.swift @@ -65,11 +65,11 @@ func bindingErrorToInterface(error: ErrorType) { } func rxAbstractMethodWithMessage(message: String) -> T { - return rxFatalErrorAndDontReturn(message) + rxFatalError(message) } func rxAbstractMethod() -> T { - return rxFatalErrorAndDontReturn("Abstract method") + rxFatalError("Abstract method") } // workaround for Swift compiler bug, cheers compiler team :) @@ -85,7 +85,6 @@ func castOrFatalError(value: AnyObject!, message: String) -> T { let maybeResult: T? = value as? T guard let result = maybeResult else { rxFatalError(message) - return maybeResult! } return result @@ -95,7 +94,6 @@ func castOrFatalError(value: AnyObject!) -> T { let maybeResult: T? = value as? T guard let result = maybeResult else { rxFatalError("Failure converting from \(value) to \(T.self)") - return maybeResult! } return result @@ -109,14 +107,9 @@ let delegateNotSet = "Delegate not set" // } -func rxFatalErrorAndDontReturn(lastMessage: String) -> T { - rxFatalError(lastMessage) - return (nil as T!)! -} - #if !RX_NO_MODULE -func rxFatalError(lastMessage: String) { +@noreturn func rxFatalError(lastMessage: String) { // The temptation to comment this line is great, but please don't, it's for your own good. The choice is yours. fatalError(lastMessage) } diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index 01ac11d0..3c8c5283 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -67,62 +67,6 @@ C83367241AD029AE00C668A7 /* HtmlParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83367111AD029AE00C668A7 /* HtmlParsing.swift */; }; C83367251AD029AE00C668A7 /* ImageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83367121AD029AE00C668A7 /* ImageService.swift */; }; C83367311AD029AE00C668A7 /* Wireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83367211AD029AE00C668A7 /* Wireframe.swift */; }; - C84B3A2D1BA4345A001B7D88 /* _RX.m in Sources */ = {isa = PBXBuildFile; fileRef = C84B39DF1BA4345A001B7D88 /* _RX.m */; }; - C84B3A2E1BA4345A001B7D88 /* _RXDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = C84B39E11BA4345A001B7D88 /* _RXDelegateProxy.m */; }; - C84B3A2F1BA4345A001B7D88 /* _RXKVOObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = C84B39E31BA4345A001B7D88 /* _RXKVOObserver.m */; }; - C84B3A301BA4345A001B7D88 /* _RXSwizzling.m in Sources */ = {isa = PBXBuildFile; fileRef = C84B39E51BA4345A001B7D88 /* _RXSwizzling.m */; }; - C84B3A311BA4345A001B7D88 /* CLLocationManager+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B39E61BA4345A001B7D88 /* CLLocationManager+Rx.swift */; }; - C84B3A321BA4345A001B7D88 /* ControlEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B39E81BA4345A001B7D88 /* ControlEvent.swift */; }; - C84B3A331BA4345A001B7D88 /* ControlProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B39E91BA4345A001B7D88 /* ControlProperty.swift */; }; - C84B3A341BA4345A001B7D88 /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B39EA1BA4345A001B7D88 /* DelegateProxy.swift */; }; - C84B3A351BA4345A001B7D88 /* DelegateProxyType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B39EB1BA4345A001B7D88 /* DelegateProxyType.swift */; }; - C84B3A361BA4345A001B7D88 /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B39EC1BA4345A001B7D88 /* Logging.swift */; }; - C84B3A371BA4345A001B7D88 /* Observable+CocoaExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B39ED1BA4345A001B7D88 /* Observable+CocoaExtensions.swift */; }; - C84B3A381BA4345A001B7D88 /* ControlTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B39F01BA4345A001B7D88 /* ControlTarget.swift */; }; - C84B3A391BA4345A001B7D88 /* Deallocating.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B39F11BA4345A001B7D88 /* Deallocating.swift */; }; - C84B3A3A1BA4345A001B7D88 /* DeinitAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B39F21BA4345A001B7D88 /* DeinitAction.swift */; }; - C84B3A3B1BA4345A001B7D88 /* KVOObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B39F31BA4345A001B7D88 /* KVOObservable.swift */; }; - C84B3A3C1BA4345A001B7D88 /* KVOObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B39F41BA4345A001B7D88 /* KVOObserver.swift */; }; - C84B3A3D1BA4345A001B7D88 /* NSNotificationCenter+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B39F51BA4345A001B7D88 /* NSNotificationCenter+Rx.swift */; }; - C84B3A3E1BA4345A001B7D88 /* NSObject+Rx+CoreGraphics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B39F61BA4345A001B7D88 /* NSObject+Rx+CoreGraphics.swift */; }; - C84B3A3F1BA4345A001B7D88 /* NSObject+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B39F71BA4345A001B7D88 /* NSObject+Rx.swift */; }; - C84B3A401BA4345A001B7D88 /* NSURLSession+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B39F81BA4345A001B7D88 /* NSURLSession+Rx.swift */; }; - C84B3A411BA4345A001B7D88 /* RxCLLocationManagerDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B39FA1BA4345A001B7D88 /* RxCLLocationManagerDelegateProxy.swift */; }; - C84B3A421BA4345A001B7D88 /* RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B39FB1BA4345A001B7D88 /* RxCocoa.swift */; }; - C84B3A431BA4345A001B7D88 /* RxTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B39FC1BA4345A001B7D88 /* RxTarget.swift */; }; - C84B3A441BA4345A001B7D88 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C84B39FD1BA4345A001B7D88 /* Info.plist */; }; - C84B3A451BA4345A001B7D88 /* RxCollectionViewReactiveArrayDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A001BA4345A001B7D88 /* RxCollectionViewReactiveArrayDataSource.swift */; }; - C84B3A461BA4345A001B7D88 /* RxTableViewReactiveArrayDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A011BA4345A001B7D88 /* RxTableViewReactiveArrayDataSource.swift */; }; - C84B3A471BA4345A001B7D88 /* ItemEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A031BA4345A001B7D88 /* ItemEvents.swift */; }; - C84B3A481BA4345A001B7D88 /* RxCollectionViewDataSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A051BA4345A001B7D88 /* RxCollectionViewDataSourceType.swift */; }; - C84B3A491BA4345A001B7D88 /* RxTableViewDataSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A061BA4345A001B7D88 /* RxTableViewDataSourceType.swift */; }; - C84B3A4A1BA4345A001B7D88 /* RxActionSheetDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A081BA4345A001B7D88 /* RxActionSheetDelegateProxy.swift */; }; - C84B3A4B1BA4345A001B7D88 /* RxAlertViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A091BA4345A001B7D88 /* RxAlertViewDelegateProxy.swift */; }; - C84B3A4C1BA4345A001B7D88 /* RxCollectionViewDataSourceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A0A1BA4345A001B7D88 /* RxCollectionViewDataSourceProxy.swift */; }; - C84B3A4D1BA4345A001B7D88 /* RxCollectionViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A0B1BA4345A001B7D88 /* RxCollectionViewDelegateProxy.swift */; }; - C84B3A4E1BA4345A001B7D88 /* RxScrollViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A0C1BA4345A001B7D88 /* RxScrollViewDelegateProxy.swift */; }; - C84B3A4F1BA4345A001B7D88 /* RxSearchBarDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A0D1BA4345A001B7D88 /* RxSearchBarDelegateProxy.swift */; }; - C84B3A501BA4345A001B7D88 /* RxTableViewDataSourceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A0E1BA4345A001B7D88 /* RxTableViewDataSourceProxy.swift */; }; - C84B3A511BA4345A001B7D88 /* RxTableViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A0F1BA4345A001B7D88 /* RxTableViewDelegateProxy.swift */; }; - C84B3A521BA4345A001B7D88 /* RxTextViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A101BA4345A001B7D88 /* RxTextViewDelegateProxy.swift */; }; - C84B3A531BA4345A001B7D88 /* UIActionSheet+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A111BA4345A001B7D88 /* UIActionSheet+Rx.swift */; }; - C84B3A541BA4345A001B7D88 /* UIAlertView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A121BA4345A001B7D88 /* UIAlertView+Rx.swift */; }; - C84B3A551BA4345A001B7D88 /* UIBarButtonItem+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A131BA4345A001B7D88 /* UIBarButtonItem+Rx.swift */; }; - C84B3A561BA4345A001B7D88 /* UIButton+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A141BA4345A001B7D88 /* UIButton+Rx.swift */; }; - C84B3A571BA4345A001B7D88 /* UICollectionView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A151BA4345A001B7D88 /* UICollectionView+Rx.swift */; }; - C84B3A581BA4345A001B7D88 /* UIControl+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A161BA4345A001B7D88 /* UIControl+Rx.swift */; }; - C84B3A591BA4345A001B7D88 /* UIDatePicker+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A171BA4345A001B7D88 /* UIDatePicker+Rx.swift */; }; - C84B3A5A1BA4345A001B7D88 /* UIGestureRecognizer+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A181BA4345A001B7D88 /* UIGestureRecognizer+Rx.swift */; }; - C84B3A5B1BA4345A001B7D88 /* UIImageView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A191BA4345A001B7D88 /* UIImageView+Rx.swift */; }; - C84B3A5C1BA4345A001B7D88 /* UILabel+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A1A1BA4345A001B7D88 /* UILabel+Rx.swift */; }; - C84B3A5D1BA4345A001B7D88 /* UIScrollView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A1B1BA4345A001B7D88 /* UIScrollView+Rx.swift */; }; - C84B3A5E1BA4345A001B7D88 /* UISearchBar+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A1C1BA4345A001B7D88 /* UISearchBar+Rx.swift */; }; - C84B3A5F1BA4345A001B7D88 /* UISegmentedControl+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A1D1BA4345A001B7D88 /* UISegmentedControl+Rx.swift */; }; - C84B3A601BA4345A001B7D88 /* UISlider+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A1E1BA4345A001B7D88 /* UISlider+Rx.swift */; }; - C84B3A611BA4345A001B7D88 /* UISwitch+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A1F1BA4345A001B7D88 /* UISwitch+Rx.swift */; }; - C84B3A621BA4345A001B7D88 /* UITableView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A201BA4345A001B7D88 /* UITableView+Rx.swift */; }; - C84B3A631BA4345A001B7D88 /* UITextField+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A211BA4345A001B7D88 /* UITextField+Rx.swift */; }; - C84B3A641BA4345A001B7D88 /* UITextView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B3A221BA4345A001B7D88 /* UITextView+Rx.swift */; }; C84B91381B8A282000C9CCCF /* RxTableViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88C78631B3EB0A00061C5AB /* RxTableViewSectionedAnimatedDataSource.swift */; }; C84B91391B8A282000C9CCCF /* RxTableViewSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88C78641B3EB0A00061C5AB /* RxTableViewSectionedDataSource.swift */; }; C84B913A1B8A282000C9CCCF /* RxTableViewSectionedReloadDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88C78651B3EB0A00061C5AB /* RxTableViewSectionedReloadDataSource.swift */; }; @@ -132,115 +76,6 @@ C859B9A41B45C5D900D012D7 /* PartialUpdatesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C859B9A31B45C5D900D012D7 /* PartialUpdatesViewController.swift */; }; C859B9AC1B45CF9100D012D7 /* NumberCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C859B9AB1B45CF9100D012D7 /* NumberCell.swift */; }; C859B9AE1B45CFAB00D012D7 /* NumberSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C859B9AD1B45CFAB00D012D7 /* NumberSectionView.swift */; }; - C86409901BA5909000D3C4E8 /* Cancelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864091D1BA5909000D3C4E8 /* Cancelable.swift */; }; - C86409911BA5909000D3C4E8 /* AsyncLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864091F1BA5909000D3C4E8 /* AsyncLock.swift */; }; - C86409921BA5909000D3C4E8 /* Lock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409201BA5909000D3C4E8 /* Lock.swift */; }; - C86409931BA5909000D3C4E8 /* ConnectableObservableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409211BA5909000D3C4E8 /* ConnectableObservableType.swift */; }; - C86409941BA5909000D3C4E8 /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409231BA5909000D3C4E8 /* Bag.swift */; }; - C86409951BA5909000D3C4E8 /* InfiniteSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409241BA5909000D3C4E8 /* InfiniteSequence.swift */; }; - C86409961BA5909000D3C4E8 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409251BA5909000D3C4E8 /* Queue.swift */; }; - C86409971BA5909000D3C4E8 /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409261BA5909000D3C4E8 /* Disposable.swift */; }; - C86409981BA5909000D3C4E8 /* AnonymousDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409281BA5909000D3C4E8 /* AnonymousDisposable.swift */; }; - C86409991BA5909000D3C4E8 /* BinaryDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409291BA5909000D3C4E8 /* BinaryDisposable.swift */; }; - C864099A1BA5909000D3C4E8 /* CompositeDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864092A1BA5909000D3C4E8 /* CompositeDisposable.swift */; }; - C864099B1BA5909000D3C4E8 /* DisposeBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864092B1BA5909000D3C4E8 /* DisposeBag.swift */; }; - C864099C1BA5909000D3C4E8 /* DisposeBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864092C1BA5909000D3C4E8 /* DisposeBase.swift */; }; - C864099D1BA5909000D3C4E8 /* NAryDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864092D1BA5909000D3C4E8 /* NAryDisposable.swift */; }; - C864099E1BA5909000D3C4E8 /* NAryDisposable.tt in Resources */ = {isa = PBXBuildFile; fileRef = C864092E1BA5909000D3C4E8 /* NAryDisposable.tt */; }; - C864099F1BA5909000D3C4E8 /* NopDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864092F1BA5909000D3C4E8 /* NopDisposable.swift */; }; - C86409A01BA5909000D3C4E8 /* ScheduledDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409301BA5909000D3C4E8 /* ScheduledDisposable.swift */; }; - C86409A11BA5909000D3C4E8 /* ScopedDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409311BA5909000D3C4E8 /* ScopedDisposable.swift */; }; - C86409A21BA5909000D3C4E8 /* SerialDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409321BA5909000D3C4E8 /* SerialDisposable.swift */; }; - C86409A31BA5909000D3C4E8 /* SingleAssignmentDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409331BA5909000D3C4E8 /* SingleAssignmentDisposable.swift */; }; - C86409A41BA5909000D3C4E8 /* StableCompositeDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409341BA5909000D3C4E8 /* StableCompositeDisposable.swift */; }; - C86409A51BA5909000D3C4E8 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409351BA5909000D3C4E8 /* Error.swift */; }; - C86409A61BA5909000D3C4E8 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409361BA5909000D3C4E8 /* Event.swift */; }; - C86409A71BA5909000D3C4E8 /* ImmediateSchedulerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409371BA5909000D3C4E8 /* ImmediateSchedulerType.swift */; }; - C86409A81BA5909000D3C4E8 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C86409381BA5909000D3C4E8 /* Info.plist */; }; - C86409A91BA5909000D3C4E8 /* Observable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409391BA5909000D3C4E8 /* Observable+Extensions.swift */; }; - C86409AA1BA5909000D3C4E8 /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864093A1BA5909000D3C4E8 /* Observable.swift */; }; - C86409AB1BA5909000D3C4E8 /* Amb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864093D1BA5909000D3C4E8 /* Amb.swift */; }; - C86409AC1BA5909000D3C4E8 /* AnonymousObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864093E1BA5909000D3C4E8 /* AnonymousObservable.swift */; }; - C86409AD1BA5909000D3C4E8 /* AsObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864093F1BA5909000D3C4E8 /* AsObservable.swift */; }; - C86409AE1BA5909000D3C4E8 /* Buffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409401BA5909000D3C4E8 /* Buffer.swift */; }; - C86409AF1BA5909000D3C4E8 /* Catch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409411BA5909000D3C4E8 /* Catch.swift */; }; - C86409B01BA5909000D3C4E8 /* CombineLatest+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409421BA5909000D3C4E8 /* CombineLatest+arity.swift */; }; - C86409B11BA5909000D3C4E8 /* CombineLatest+arity.tt in Resources */ = {isa = PBXBuildFile; fileRef = C86409431BA5909000D3C4E8 /* CombineLatest+arity.tt */; }; - C86409B21BA5909000D3C4E8 /* CombineLatest+CollectionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409441BA5909000D3C4E8 /* CombineLatest+CollectionType.swift */; }; - C86409B31BA5909000D3C4E8 /* CombineLatest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409451BA5909000D3C4E8 /* CombineLatest.swift */; }; - C86409B41BA5909000D3C4E8 /* Concat.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409461BA5909000D3C4E8 /* Concat.swift */; }; - C86409B51BA5909000D3C4E8 /* ConnectableObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409471BA5909000D3C4E8 /* ConnectableObservable.swift */; }; - C86409B61BA5909000D3C4E8 /* Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409481BA5909000D3C4E8 /* Debug.swift */; }; - C86409B71BA5909000D3C4E8 /* Deferred.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409491BA5909000D3C4E8 /* Deferred.swift */; }; - C86409B81BA5909000D3C4E8 /* DelaySubscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864094A1BA5909000D3C4E8 /* DelaySubscription.swift */; }; - C86409B91BA5909000D3C4E8 /* DistinctUntilChanged.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864094B1BA5909000D3C4E8 /* DistinctUntilChanged.swift */; }; - C86409BA1BA5909000D3C4E8 /* Do.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864094C1BA5909000D3C4E8 /* Do.swift */; }; - C86409BB1BA5909000D3C4E8 /* Empty.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864094D1BA5909000D3C4E8 /* Empty.swift */; }; - C86409BC1BA5909000D3C4E8 /* FailWith.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864094E1BA5909000D3C4E8 /* FailWith.swift */; }; - C86409BD1BA5909000D3C4E8 /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864094F1BA5909000D3C4E8 /* Filter.swift */; }; - C86409BE1BA5909000D3C4E8 /* FlatMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409501BA5909000D3C4E8 /* FlatMap.swift */; }; - C86409BF1BA5909000D3C4E8 /* Generate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409511BA5909000D3C4E8 /* Generate.swift */; }; - C86409C01BA5909000D3C4E8 /* Just.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409521BA5909000D3C4E8 /* Just.swift */; }; - C86409C11BA5909000D3C4E8 /* Map.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409531BA5909000D3C4E8 /* Map.swift */; }; - C86409C21BA5909000D3C4E8 /* Merge.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409541BA5909000D3C4E8 /* Merge.swift */; }; - C86409C31BA5909000D3C4E8 /* Multicast.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409551BA5909000D3C4E8 /* Multicast.swift */; }; - C86409C41BA5909000D3C4E8 /* Never.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409561BA5909000D3C4E8 /* Never.swift */; }; - C86409C51BA5909000D3C4E8 /* ObserveOn.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409571BA5909000D3C4E8 /* ObserveOn.swift */; }; - C86409C61BA5909000D3C4E8 /* ObserveOnSerialDispatchQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409581BA5909000D3C4E8 /* ObserveOnSerialDispatchQueue.swift */; }; - C86409C71BA5909000D3C4E8 /* Producer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409591BA5909000D3C4E8 /* Producer.swift */; }; - C86409C81BA5909000D3C4E8 /* Reduce.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864095A1BA5909000D3C4E8 /* Reduce.swift */; }; - C86409C91BA5909000D3C4E8 /* RefCount.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864095B1BA5909000D3C4E8 /* RefCount.swift */; }; - C86409CA1BA5909000D3C4E8 /* Sample.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864095C1BA5909000D3C4E8 /* Sample.swift */; }; - C86409CB1BA5909000D3C4E8 /* Scan.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864095D1BA5909000D3C4E8 /* Scan.swift */; }; - C86409CC1BA5909000D3C4E8 /* Sink.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864095E1BA5909000D3C4E8 /* Sink.swift */; }; - C86409CD1BA5909000D3C4E8 /* Skip.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864095F1BA5909000D3C4E8 /* Skip.swift */; }; - C86409CE1BA5909000D3C4E8 /* StartWith.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409601BA5909000D3C4E8 /* StartWith.swift */; }; - C86409CF1BA5909000D3C4E8 /* SubscribeOn.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409611BA5909000D3C4E8 /* SubscribeOn.swift */; }; - C86409D01BA5909000D3C4E8 /* Switch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409621BA5909000D3C4E8 /* Switch.swift */; }; - C86409D11BA5909000D3C4E8 /* Take.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409631BA5909000D3C4E8 /* Take.swift */; }; - C86409D21BA5909000D3C4E8 /* TakeUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409641BA5909000D3C4E8 /* TakeUntil.swift */; }; - C86409D31BA5909000D3C4E8 /* TakeWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409651BA5909000D3C4E8 /* TakeWhile.swift */; }; - C86409D41BA5909000D3C4E8 /* Throttle.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409661BA5909000D3C4E8 /* Throttle.swift */; }; - C86409D51BA5909000D3C4E8 /* Timer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409671BA5909000D3C4E8 /* Timer.swift */; }; - C86409D61BA5909000D3C4E8 /* Zip+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409681BA5909000D3C4E8 /* Zip+arity.swift */; }; - C86409D71BA5909000D3C4E8 /* Zip+arity.tt in Resources */ = {isa = PBXBuildFile; fileRef = C86409691BA5909000D3C4E8 /* Zip+arity.tt */; }; - C86409D81BA5909000D3C4E8 /* Zip+CollectionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864096A1BA5909000D3C4E8 /* Zip+CollectionType.swift */; }; - C86409D91BA5909000D3C4E8 /* Zip.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864096B1BA5909000D3C4E8 /* Zip.swift */; }; - C86409DA1BA5909000D3C4E8 /* Observable+Aggregate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864096C1BA5909000D3C4E8 /* Observable+Aggregate.swift */; }; - C86409DB1BA5909000D3C4E8 /* Observable+Binding.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864096D1BA5909000D3C4E8 /* Observable+Binding.swift */; }; - C86409DC1BA5909000D3C4E8 /* Observable+Concurrency.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864096E1BA5909000D3C4E8 /* Observable+Concurrency.swift */; }; - C86409DD1BA5909000D3C4E8 /* Observable+Creation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864096F1BA5909000D3C4E8 /* Observable+Creation.swift */; }; - C86409DE1BA5909000D3C4E8 /* Observable+Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409701BA5909000D3C4E8 /* Observable+Debug.swift */; }; - C86409DF1BA5909000D3C4E8 /* Observable+Multiple.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409711BA5909000D3C4E8 /* Observable+Multiple.swift */; }; - C86409E01BA5909000D3C4E8 /* Observable+Single.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409721BA5909000D3C4E8 /* Observable+Single.swift */; }; - C86409E11BA5909000D3C4E8 /* Observable+StandardSequenceOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409731BA5909000D3C4E8 /* Observable+StandardSequenceOperators.swift */; }; - C86409E21BA5909000D3C4E8 /* Observable+Time.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409741BA5909000D3C4E8 /* Observable+Time.swift */; }; - C86409E31BA5909000D3C4E8 /* ObservableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409751BA5909000D3C4E8 /* ObservableType.swift */; }; - C86409E41BA5909000D3C4E8 /* ObserverOf.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409761BA5909000D3C4E8 /* ObserverOf.swift */; }; - C86409E51BA5909000D3C4E8 /* AnonymousObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409781BA5909000D3C4E8 /* AnonymousObserver.swift */; }; - C86409E61BA5909000D3C4E8 /* ObserverBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409791BA5909000D3C4E8 /* ObserverBase.swift */; }; - C86409E71BA5909000D3C4E8 /* TailRecursiveSink.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864097A1BA5909000D3C4E8 /* TailRecursiveSink.swift */; }; - C86409E81BA5909000D3C4E8 /* ObserverType+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864097B1BA5909000D3C4E8 /* ObserverType+Extensions.swift */; }; - C86409E91BA5909000D3C4E8 /* ObserverType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864097C1BA5909000D3C4E8 /* ObserverType.swift */; }; - C86409EA1BA5909000D3C4E8 /* Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864097D1BA5909000D3C4E8 /* Rx.swift */; }; - C86409EB1BA5909000D3C4E8 /* RxBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864097E1BA5909000D3C4E8 /* RxBox.swift */; }; - C86409EC1BA5909000D3C4E8 /* ConcurrentDispatchQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409801BA5909000D3C4E8 /* ConcurrentDispatchQueueScheduler.swift */; }; - C86409ED1BA5909000D3C4E8 /* CurrentThreadScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409811BA5909000D3C4E8 /* CurrentThreadScheduler.swift */; }; - C86409EE1BA5909000D3C4E8 /* DispatchQueueSchedulerPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409821BA5909000D3C4E8 /* DispatchQueueSchedulerPriority.swift */; }; - C86409EF1BA5909000D3C4E8 /* MainScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409831BA5909000D3C4E8 /* MainScheduler.swift */; }; - C86409F01BA5909000D3C4E8 /* OperationQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409841BA5909000D3C4E8 /* OperationQueueScheduler.swift */; }; - C86409F11BA5909000D3C4E8 /* RecursiveScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409851BA5909000D3C4E8 /* RecursiveScheduler.swift */; }; - C86409F21BA5909000D3C4E8 /* ScheduledItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409861BA5909000D3C4E8 /* ScheduledItem.swift */; }; - C86409F31BA5909000D3C4E8 /* SchedulerServices+Emulation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409871BA5909000D3C4E8 /* SchedulerServices+Emulation.swift */; }; - C86409F41BA5909000D3C4E8 /* SerialDispatchQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409881BA5909000D3C4E8 /* SerialDispatchQueueScheduler.swift */; }; - C86409F51BA5909000D3C4E8 /* SchedulerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409891BA5909000D3C4E8 /* SchedulerType.swift */; }; - C86409F61BA5909000D3C4E8 /* BehaviorSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864098B1BA5909000D3C4E8 /* BehaviorSubject.swift */; }; - C86409F71BA5909000D3C4E8 /* PublishSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864098C1BA5909000D3C4E8 /* PublishSubject.swift */; }; - C86409F81BA5909000D3C4E8 /* ReplaySubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864098D1BA5909000D3C4E8 /* ReplaySubject.swift */; }; - C86409F91BA5909000D3C4E8 /* SubjectType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864098E1BA5909000D3C4E8 /* SubjectType.swift */; }; - C86409FA1BA5909000D3C4E8 /* Variable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864098F1BA5909000D3C4E8 /* Variable.swift */; }; - C86409FF1BA5A87200D3C4E8 /* Range.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409FE1BA5A87200D3C4E8 /* Range.swift */; }; - C8640A011BA5AB5A00D3C4E8 /* Repeat.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8640A001BA5AB5A00D3C4E8 /* Repeat.swift */; }; C86E2F3E1AE5A0CA00C31024 /* SearchResultViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86E2F321AE5A0CA00C31024 /* SearchResultViewModel.swift */; }; C86E2F3F1AE5A0CA00C31024 /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86E2F331AE5A0CA00C31024 /* SearchViewModel.swift */; }; C86E2F451AE5A0CA00C31024 /* WikipediaAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86E2F3B1AE5A0CA00C31024 /* WikipediaAPI.swift */; }; @@ -265,9 +100,184 @@ C890A6581AEBD26B00AFF7E6 /* GitHubSignupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C890A6571AEBD26B00AFF7E6 /* GitHubSignupViewController.swift */; }; C890A65A1AEBD28A00AFF7E6 /* GitHubAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C890A6591AEBD28A00AFF7E6 /* GitHubAPI.swift */; }; C890A65D1AEC084100AFF7E6 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C890A65C1AEC084100AFF7E6 /* ViewController.swift */; }; + C894649E1BC6C2B00055219D /* Cancelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464281BC6C2B00055219D /* Cancelable.swift */; }; + C894649F1BC6C2B00055219D /* AsyncLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894642A1BC6C2B00055219D /* AsyncLock.swift */; }; + C89464A01BC6C2B00055219D /* Lock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894642B1BC6C2B00055219D /* Lock.swift */; }; + C89464A11BC6C2B00055219D /* ConnectableObservableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894642C1BC6C2B00055219D /* ConnectableObservableType.swift */; }; + C89464A21BC6C2B00055219D /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894642E1BC6C2B00055219D /* Bag.swift */; }; + C89464A31BC6C2B00055219D /* InfiniteSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894642F1BC6C2B00055219D /* InfiniteSequence.swift */; }; + C89464A41BC6C2B00055219D /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464301BC6C2B00055219D /* Queue.swift */; }; + C89464A51BC6C2B00055219D /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464311BC6C2B00055219D /* Disposable.swift */; }; + C89464A61BC6C2B00055219D /* AnonymousDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464331BC6C2B00055219D /* AnonymousDisposable.swift */; }; + C89464A71BC6C2B00055219D /* BinaryDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464341BC6C2B00055219D /* BinaryDisposable.swift */; }; + C89464A81BC6C2B00055219D /* CompositeDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464351BC6C2B00055219D /* CompositeDisposable.swift */; }; + C89464A91BC6C2B00055219D /* DisposeBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464361BC6C2B00055219D /* DisposeBag.swift */; }; + C89464AA1BC6C2B00055219D /* DisposeBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464371BC6C2B00055219D /* DisposeBase.swift */; }; + C89464AB1BC6C2B00055219D /* NAryDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464381BC6C2B00055219D /* NAryDisposable.swift */; }; + C89464AC1BC6C2B00055219D /* NAryDisposable.tt in Resources */ = {isa = PBXBuildFile; fileRef = C89464391BC6C2B00055219D /* NAryDisposable.tt */; }; + C89464AD1BC6C2B00055219D /* NopDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894643A1BC6C2B00055219D /* NopDisposable.swift */; }; + C89464AE1BC6C2B00055219D /* ScheduledDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894643B1BC6C2B00055219D /* ScheduledDisposable.swift */; }; + C89464AF1BC6C2B00055219D /* ScopedDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894643C1BC6C2B00055219D /* ScopedDisposable.swift */; }; + C89464B01BC6C2B00055219D /* SerialDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894643D1BC6C2B00055219D /* SerialDisposable.swift */; }; + C89464B11BC6C2B00055219D /* SingleAssignmentDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894643E1BC6C2B00055219D /* SingleAssignmentDisposable.swift */; }; + C89464B21BC6C2B00055219D /* StableCompositeDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894643F1BC6C2B00055219D /* StableCompositeDisposable.swift */; }; + C89464B31BC6C2B00055219D /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464401BC6C2B00055219D /* Error.swift */; }; + C89464B41BC6C2B00055219D /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464411BC6C2B00055219D /* Event.swift */; }; + C89464B51BC6C2B00055219D /* ImmediateSchedulerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464421BC6C2B00055219D /* ImmediateSchedulerType.swift */; }; + C89464B61BC6C2B00055219D /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C89464431BC6C2B00055219D /* Info.plist */; }; + C89464B71BC6C2B00055219D /* Observable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464441BC6C2B00055219D /* Observable+Extensions.swift */; }; + C89464B81BC6C2B00055219D /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464451BC6C2B00055219D /* Observable.swift */; }; + C89464B91BC6C2B00055219D /* ObservableConvertibleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464461BC6C2B00055219D /* ObservableConvertibleType.swift */; }; + C89464BA1BC6C2B00055219D /* Amb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464491BC6C2B00055219D /* Amb.swift */; }; + C89464BB1BC6C2B00055219D /* AnonymousObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894644A1BC6C2B00055219D /* AnonymousObservable.swift */; }; + C89464BC1BC6C2B00055219D /* AsObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894644B1BC6C2B00055219D /* AsObservable.swift */; }; + C89464BD1BC6C2B00055219D /* Buffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894644C1BC6C2B00055219D /* Buffer.swift */; }; + C89464BE1BC6C2B00055219D /* Catch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894644D1BC6C2B00055219D /* Catch.swift */; }; + C89464BF1BC6C2B00055219D /* CombineLatest+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894644E1BC6C2B00055219D /* CombineLatest+arity.swift */; }; + C89464C01BC6C2B00055219D /* CombineLatest+arity.tt in Resources */ = {isa = PBXBuildFile; fileRef = C894644F1BC6C2B00055219D /* CombineLatest+arity.tt */; }; + C89464C11BC6C2B00055219D /* CombineLatest+CollectionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464501BC6C2B00055219D /* CombineLatest+CollectionType.swift */; }; + C89464C21BC6C2B00055219D /* CombineLatest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464511BC6C2B00055219D /* CombineLatest.swift */; }; + C89464C31BC6C2B00055219D /* Concat.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464521BC6C2B00055219D /* Concat.swift */; }; + C89464C41BC6C2B00055219D /* ConnectableObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464531BC6C2B00055219D /* ConnectableObservable.swift */; }; + C89464C51BC6C2B00055219D /* Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464541BC6C2B00055219D /* Debug.swift */; }; + C89464C61BC6C2B00055219D /* Deferred.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464551BC6C2B00055219D /* Deferred.swift */; }; + C89464C71BC6C2B00055219D /* DelaySubscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464561BC6C2B00055219D /* DelaySubscription.swift */; }; + C89464C81BC6C2B00055219D /* DistinctUntilChanged.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464571BC6C2B00055219D /* DistinctUntilChanged.swift */; }; + C89464C91BC6C2B00055219D /* Do.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464581BC6C2B00055219D /* Do.swift */; }; + C89464CA1BC6C2B00055219D /* Empty.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464591BC6C2B00055219D /* Empty.swift */; }; + C89464CB1BC6C2B00055219D /* FailWith.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894645A1BC6C2B00055219D /* FailWith.swift */; }; + C89464CC1BC6C2B00055219D /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894645B1BC6C2B00055219D /* Filter.swift */; }; + C89464CD1BC6C2B00055219D /* FlatMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894645C1BC6C2B00055219D /* FlatMap.swift */; }; + C89464CE1BC6C2B00055219D /* Generate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894645D1BC6C2B00055219D /* Generate.swift */; }; + C89464CF1BC6C2B00055219D /* Just.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894645E1BC6C2B00055219D /* Just.swift */; }; + C89464D01BC6C2B00055219D /* Map.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894645F1BC6C2B00055219D /* Map.swift */; }; + C89464D11BC6C2B00055219D /* Merge.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464601BC6C2B00055219D /* Merge.swift */; }; + C89464D21BC6C2B00055219D /* Multicast.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464611BC6C2B00055219D /* Multicast.swift */; }; + C89464D31BC6C2B00055219D /* Never.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464621BC6C2B00055219D /* Never.swift */; }; + C89464D41BC6C2B00055219D /* ObserveOn.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464631BC6C2B00055219D /* ObserveOn.swift */; }; + C89464D51BC6C2B00055219D /* ObserveOnSerialDispatchQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464641BC6C2B00055219D /* ObserveOnSerialDispatchQueue.swift */; }; + C89464D61BC6C2B00055219D /* Producer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464651BC6C2B00055219D /* Producer.swift */; }; + C89464D71BC6C2B00055219D /* Range.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464661BC6C2B00055219D /* Range.swift */; }; + C89464D81BC6C2B00055219D /* Reduce.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464671BC6C2B00055219D /* Reduce.swift */; }; + C89464D91BC6C2B00055219D /* RefCount.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464681BC6C2B00055219D /* RefCount.swift */; }; + C89464DA1BC6C2B00055219D /* Repeat.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464691BC6C2B00055219D /* Repeat.swift */; }; + C89464DB1BC6C2B00055219D /* Sample.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894646A1BC6C2B00055219D /* Sample.swift */; }; + C89464DC1BC6C2B00055219D /* Scan.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894646B1BC6C2B00055219D /* Scan.swift */; }; + C89464DD1BC6C2B00055219D /* Sink.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894646C1BC6C2B00055219D /* Sink.swift */; }; + C89464DE1BC6C2B00055219D /* Skip.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894646D1BC6C2B00055219D /* Skip.swift */; }; + C89464DF1BC6C2B00055219D /* StartWith.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894646E1BC6C2B00055219D /* StartWith.swift */; }; + C89464E01BC6C2B00055219D /* SubscribeOn.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894646F1BC6C2B00055219D /* SubscribeOn.swift */; }; + C89464E11BC6C2B00055219D /* Switch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464701BC6C2B00055219D /* Switch.swift */; }; + C89464E21BC6C2B00055219D /* Take.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464711BC6C2B00055219D /* Take.swift */; }; + C89464E31BC6C2B00055219D /* TakeUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464721BC6C2B00055219D /* TakeUntil.swift */; }; + C89464E41BC6C2B00055219D /* TakeWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464731BC6C2B00055219D /* TakeWhile.swift */; }; + C89464E51BC6C2B00055219D /* Throttle.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464741BC6C2B00055219D /* Throttle.swift */; }; + C89464E61BC6C2B00055219D /* Timer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464751BC6C2B00055219D /* Timer.swift */; }; + C89464E71BC6C2B00055219D /* Zip+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464761BC6C2B00055219D /* Zip+arity.swift */; }; + C89464E81BC6C2B00055219D /* Zip+arity.tt in Resources */ = {isa = PBXBuildFile; fileRef = C89464771BC6C2B00055219D /* Zip+arity.tt */; }; + C89464E91BC6C2B00055219D /* Zip+CollectionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464781BC6C2B00055219D /* Zip+CollectionType.swift */; }; + C89464EA1BC6C2B00055219D /* Zip.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464791BC6C2B00055219D /* Zip.swift */; }; + C89464EB1BC6C2B00055219D /* Observable+Aggregate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894647A1BC6C2B00055219D /* Observable+Aggregate.swift */; }; + C89464EC1BC6C2B00055219D /* Observable+Binding.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894647B1BC6C2B00055219D /* Observable+Binding.swift */; }; + C89464ED1BC6C2B00055219D /* Observable+Concurrency.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894647C1BC6C2B00055219D /* Observable+Concurrency.swift */; }; + C89464EE1BC6C2B00055219D /* Observable+Creation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894647D1BC6C2B00055219D /* Observable+Creation.swift */; }; + C89464EF1BC6C2B00055219D /* Observable+Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894647E1BC6C2B00055219D /* Observable+Debug.swift */; }; + C89464F01BC6C2B00055219D /* Observable+Multiple.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894647F1BC6C2B00055219D /* Observable+Multiple.swift */; }; + C89464F11BC6C2B00055219D /* Observable+Single.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464801BC6C2B00055219D /* Observable+Single.swift */; }; + C89464F21BC6C2B00055219D /* Observable+StandardSequenceOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464811BC6C2B00055219D /* Observable+StandardSequenceOperators.swift */; }; + C89464F31BC6C2B00055219D /* Observable+Time.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464821BC6C2B00055219D /* Observable+Time.swift */; }; + C89464F41BC6C2B00055219D /* ObservableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464831BC6C2B00055219D /* ObservableType.swift */; }; + C89464F51BC6C2B00055219D /* ObserverOf.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464841BC6C2B00055219D /* ObserverOf.swift */; }; + C89464F61BC6C2B00055219D /* AnonymousObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464861BC6C2B00055219D /* AnonymousObserver.swift */; }; + C89464F71BC6C2B00055219D /* ObserverBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464871BC6C2B00055219D /* ObserverBase.swift */; }; + C89464F81BC6C2B00055219D /* TailRecursiveSink.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464881BC6C2B00055219D /* TailRecursiveSink.swift */; }; + C89464F91BC6C2B00055219D /* ObserverType+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464891BC6C2B00055219D /* ObserverType+Extensions.swift */; }; + C89464FA1BC6C2B00055219D /* ObserverType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894648A1BC6C2B00055219D /* ObserverType.swift */; }; + C89464FB1BC6C2B00055219D /* Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894648B1BC6C2B00055219D /* Rx.swift */; }; + C89464FC1BC6C2B00055219D /* RxBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894648C1BC6C2B00055219D /* RxBox.swift */; }; + C89464FD1BC6C2B00055219D /* ConcurrentDispatchQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894648E1BC6C2B00055219D /* ConcurrentDispatchQueueScheduler.swift */; }; + C89464FE1BC6C2B00055219D /* CurrentThreadScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894648F1BC6C2B00055219D /* CurrentThreadScheduler.swift */; }; + C89464FF1BC6C2B00055219D /* DispatchQueueSchedulerPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464901BC6C2B00055219D /* DispatchQueueSchedulerPriority.swift */; }; + C89465001BC6C2B00055219D /* MainScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464911BC6C2B00055219D /* MainScheduler.swift */; }; + C89465011BC6C2B00055219D /* OperationQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464921BC6C2B00055219D /* OperationQueueScheduler.swift */; }; + C89465021BC6C2B00055219D /* RecursiveScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464931BC6C2B00055219D /* RecursiveScheduler.swift */; }; + C89465031BC6C2B00055219D /* ScheduledItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464941BC6C2B00055219D /* ScheduledItem.swift */; }; + C89465041BC6C2B00055219D /* SchedulerServices+Emulation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464951BC6C2B00055219D /* SchedulerServices+Emulation.swift */; }; + C89465051BC6C2B00055219D /* SerialDispatchQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464961BC6C2B00055219D /* SerialDispatchQueueScheduler.swift */; }; + C89465061BC6C2B00055219D /* SchedulerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464971BC6C2B00055219D /* SchedulerType.swift */; }; + C89465071BC6C2B00055219D /* BehaviorSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464991BC6C2B00055219D /* BehaviorSubject.swift */; }; + C89465081BC6C2B00055219D /* PublishSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894649A1BC6C2B00055219D /* PublishSubject.swift */; }; + C89465091BC6C2B00055219D /* ReplaySubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894649B1BC6C2B00055219D /* ReplaySubject.swift */; }; + C894650A1BC6C2B00055219D /* SubjectType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894649C1BC6C2B00055219D /* SubjectType.swift */; }; + C894650B1BC6C2B00055219D /* Variable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894649D1BC6C2B00055219D /* Variable.swift */; }; + C89465611BC6C2BC0055219D /* _RX.m in Sources */ = {isa = PBXBuildFile; fileRef = C894650F1BC6C2BC0055219D /* _RX.m */; }; + C89465621BC6C2BC0055219D /* _RXDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = C89465111BC6C2BC0055219D /* _RXDelegateProxy.m */; }; + C89465631BC6C2BC0055219D /* _RXKVOObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = C89465131BC6C2BC0055219D /* _RXKVOObserver.m */; }; + C89465641BC6C2BC0055219D /* _RXSwizzling.m in Sources */ = {isa = PBXBuildFile; fileRef = C89465151BC6C2BC0055219D /* _RXSwizzling.m */; }; + C89465651BC6C2BC0055219D /* CLLocationManager+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465161BC6C2BC0055219D /* CLLocationManager+Rx.swift */; }; + C89465661BC6C2BC0055219D /* ControlEvent+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465181BC6C2BC0055219D /* ControlEvent+Driver.swift */; }; + C89465671BC6C2BC0055219D /* ControlEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465191BC6C2BC0055219D /* ControlEvent.swift */; }; + C89465681BC6C2BC0055219D /* ControlProperty+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894651A1BC6C2BC0055219D /* ControlProperty+Driver.swift */; }; + C89465691BC6C2BC0055219D /* ControlProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894651B1BC6C2BC0055219D /* ControlProperty.swift */; }; + C894656A1BC6C2BC0055219D /* Driver+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894651C1BC6C2BC0055219D /* Driver+Operators.swift */; }; + C894656B1BC6C2BC0055219D /* Driver+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894651D1BC6C2BC0055219D /* Driver+Subscription.swift */; }; + C894656C1BC6C2BC0055219D /* Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894651E1BC6C2BC0055219D /* Driver.swift */; }; + C894656D1BC6C2BC0055219D /* ObservableConvertibleType+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894651F1BC6C2BC0055219D /* ObservableConvertibleType+Driver.swift */; }; + C894656E1BC6C2BC0055219D /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465201BC6C2BC0055219D /* DelegateProxy.swift */; }; + C894656F1BC6C2BC0055219D /* DelegateProxyType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465211BC6C2BC0055219D /* DelegateProxyType.swift */; }; + C89465701BC6C2BC0055219D /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465221BC6C2BC0055219D /* Logging.swift */; }; + C89465711BC6C2BC0055219D /* Observable+Bind.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465231BC6C2BC0055219D /* Observable+Bind.swift */; }; + C89465721BC6C2BC0055219D /* ControlTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465261BC6C2BC0055219D /* ControlTarget.swift */; }; + C89465731BC6C2BC0055219D /* Deallocating.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465271BC6C2BC0055219D /* Deallocating.swift */; }; + C89465741BC6C2BC0055219D /* DeinitAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465281BC6C2BC0055219D /* DeinitAction.swift */; }; + C89465751BC6C2BC0055219D /* KVOObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465291BC6C2BC0055219D /* KVOObservable.swift */; }; + C89465761BC6C2BC0055219D /* KVOObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894652A1BC6C2BC0055219D /* KVOObserver.swift */; }; + C89465771BC6C2BC0055219D /* NSNotificationCenter+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894652B1BC6C2BC0055219D /* NSNotificationCenter+Rx.swift */; }; + C89465781BC6C2BC0055219D /* NSObject+Rx+CoreGraphics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894652C1BC6C2BC0055219D /* NSObject+Rx+CoreGraphics.swift */; }; + C89465791BC6C2BC0055219D /* NSObject+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894652D1BC6C2BC0055219D /* NSObject+Rx.swift */; }; + C894657A1BC6C2BC0055219D /* NSURLSession+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894652E1BC6C2BC0055219D /* NSURLSession+Rx.swift */; }; + C894657B1BC6C2BC0055219D /* RxCLLocationManagerDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465301BC6C2BC0055219D /* RxCLLocationManagerDelegateProxy.swift */; }; + C894657C1BC6C2BC0055219D /* RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465311BC6C2BC0055219D /* RxCocoa.swift */; }; + C894657D1BC6C2BC0055219D /* RxTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465321BC6C2BC0055219D /* RxTarget.swift */; }; + C894657E1BC6C2BC0055219D /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C89465331BC6C2BC0055219D /* Info.plist */; }; + C894657F1BC6C2BC0055219D /* RxCollectionViewReactiveArrayDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465361BC6C2BC0055219D /* RxCollectionViewReactiveArrayDataSource.swift */; }; + C89465801BC6C2BC0055219D /* RxTableViewReactiveArrayDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465371BC6C2BC0055219D /* RxTableViewReactiveArrayDataSource.swift */; }; + C89465811BC6C2BC0055219D /* ItemEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465391BC6C2BC0055219D /* ItemEvents.swift */; }; + C89465821BC6C2BC0055219D /* RxCollectionViewDataSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894653B1BC6C2BC0055219D /* RxCollectionViewDataSourceType.swift */; }; + C89465831BC6C2BC0055219D /* RxTableViewDataSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894653C1BC6C2BC0055219D /* RxTableViewDataSourceType.swift */; }; + C89465841BC6C2BC0055219D /* RxActionSheetDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894653E1BC6C2BC0055219D /* RxActionSheetDelegateProxy.swift */; }; + C89465851BC6C2BC0055219D /* RxAlertViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894653F1BC6C2BC0055219D /* RxAlertViewDelegateProxy.swift */; }; + C89465861BC6C2BC0055219D /* RxCollectionViewDataSourceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465401BC6C2BC0055219D /* RxCollectionViewDataSourceProxy.swift */; }; + C89465871BC6C2BC0055219D /* RxCollectionViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465411BC6C2BC0055219D /* RxCollectionViewDelegateProxy.swift */; }; + C89465881BC6C2BC0055219D /* RxScrollViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465421BC6C2BC0055219D /* RxScrollViewDelegateProxy.swift */; }; + C89465891BC6C2BC0055219D /* RxSearchBarDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465431BC6C2BC0055219D /* RxSearchBarDelegateProxy.swift */; }; + C894658A1BC6C2BC0055219D /* RxTableViewDataSourceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465441BC6C2BC0055219D /* RxTableViewDataSourceProxy.swift */; }; + C894658B1BC6C2BC0055219D /* RxTableViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465451BC6C2BC0055219D /* RxTableViewDelegateProxy.swift */; }; + C894658C1BC6C2BC0055219D /* RxTextViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465461BC6C2BC0055219D /* RxTextViewDelegateProxy.swift */; }; + C894658D1BC6C2BC0055219D /* UIActionSheet+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465471BC6C2BC0055219D /* UIActionSheet+Rx.swift */; }; + C894658E1BC6C2BC0055219D /* UIAlertView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465481BC6C2BC0055219D /* UIAlertView+Rx.swift */; }; + C894658F1BC6C2BC0055219D /* UIBarButtonItem+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465491BC6C2BC0055219D /* UIBarButtonItem+Rx.swift */; }; + C89465901BC6C2BC0055219D /* UIButton+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894654A1BC6C2BC0055219D /* UIButton+Rx.swift */; }; + C89465911BC6C2BC0055219D /* UICollectionView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894654B1BC6C2BC0055219D /* UICollectionView+Rx.swift */; }; + C89465921BC6C2BC0055219D /* UIControl+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894654C1BC6C2BC0055219D /* UIControl+Rx.swift */; }; + C89465931BC6C2BC0055219D /* UIDatePicker+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894654D1BC6C2BC0055219D /* UIDatePicker+Rx.swift */; }; + C89465941BC6C2BC0055219D /* UIGestureRecognizer+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894654E1BC6C2BC0055219D /* UIGestureRecognizer+Rx.swift */; }; + C89465951BC6C2BC0055219D /* UIImageView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894654F1BC6C2BC0055219D /* UIImageView+Rx.swift */; }; + C89465961BC6C2BC0055219D /* UILabel+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465501BC6C2BC0055219D /* UILabel+Rx.swift */; }; + C89465971BC6C2BC0055219D /* UIScrollView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465511BC6C2BC0055219D /* UIScrollView+Rx.swift */; }; + C89465981BC6C2BC0055219D /* UISearchBar+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465521BC6C2BC0055219D /* UISearchBar+Rx.swift */; }; + C89465991BC6C2BC0055219D /* UISegmentedControl+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465531BC6C2BC0055219D /* UISegmentedControl+Rx.swift */; }; + C894659A1BC6C2BC0055219D /* UISlider+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465541BC6C2BC0055219D /* UISlider+Rx.swift */; }; + C894659B1BC6C2BC0055219D /* UIStepper+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465551BC6C2BC0055219D /* UIStepper+Rx.swift */; }; + C894659C1BC6C2BC0055219D /* UISwitch+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465561BC6C2BC0055219D /* UISwitch+Rx.swift */; }; + C894659D1BC6C2BC0055219D /* UITableView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465571BC6C2BC0055219D /* UITableView+Rx.swift */; }; + C894659E1BC6C2BC0055219D /* UITextField+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465581BC6C2BC0055219D /* UITextField+Rx.swift */; }; + C894659F1BC6C2BC0055219D /* UITextView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465591BC6C2BC0055219D /* UITextView+Rx.swift */; }; C89634081B95BE50002AE38C /* RxBlocking.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468EF1B8A8BD000BF917B /* RxBlocking.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; C89634091B95BE50002AE38C /* RxCocoa.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468ED1B8A8BCC00BF917B /* RxCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; C896340A1B95BE51002AE38C /* RxSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468EB1B8A8BC900BF917B /* RxSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + C89CDB711BCC45E5002063D9 /* ShareReplay1.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89CDB611BCC45DC002063D9 /* ShareReplay1.swift */; }; + C89CDB721BCC45EE002063D9 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89CDB621BCC45DC002063D9 /* SkipUntil.swift */; }; C8A2A2C81B4049E300F11F09 /* PseudoRandomGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8A2A2C71B4049E300F11F09 /* PseudoRandomGenerator.swift */; }; C8A2A2CB1B404A1200F11F09 /* Randomizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8A2A2CA1B404A1200F11F09 /* Randomizer.swift */; }; C8A468EC1B8A8BC900BF917B /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468EB1B8A8BC900BF917B /* RxSwift.framework */; }; @@ -292,8 +302,6 @@ C8DF92EA1B0B38C0009BCF9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; }; C8DF92EB1B0B38C0009BCF9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; }; C8DF92F61B0B43A4009BCF9A /* IntroductionExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DF92F51B0B43A4009BCF9A /* IntroductionExampleViewController.swift */; }; - D245D9F41BC6CA0900CAB388 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D245D9E61BC6C60800CAB388 /* SkipUntil.swift */; }; - D2FC15C41BCBAA13007361FF /* SkipWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FC15B61BCBAA01007361FF /* SkipWhile.swift */; }; EC91FB951BBA144400973245 /* GitHubSearchRepositoriesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC91FB941BBA144400973245 /* GitHubSearchRepositoriesViewController.swift */; }; /* End PBXBuildFile section */ @@ -437,182 +445,12 @@ C83367111AD029AE00C668A7 /* HtmlParsing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HtmlParsing.swift; sourceTree = ""; }; C83367121AD029AE00C668A7 /* ImageService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageService.swift; sourceTree = ""; }; C83367211AD029AE00C668A7 /* Wireframe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Wireframe.swift; sourceTree = ""; }; - C84B39DE1BA4345A001B7D88 /* _RX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _RX.h; sourceTree = ""; }; - C84B39DF1BA4345A001B7D88 /* _RX.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _RX.m; sourceTree = ""; }; - C84B39E01BA4345A001B7D88 /* _RXDelegateProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _RXDelegateProxy.h; sourceTree = ""; }; - C84B39E11BA4345A001B7D88 /* _RXDelegateProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _RXDelegateProxy.m; sourceTree = ""; }; - C84B39E21BA4345A001B7D88 /* _RXKVOObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _RXKVOObserver.h; sourceTree = ""; }; - C84B39E31BA4345A001B7D88 /* _RXKVOObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _RXKVOObserver.m; sourceTree = ""; }; - C84B39E41BA4345A001B7D88 /* _RXSwizzling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _RXSwizzling.h; sourceTree = ""; }; - C84B39E51BA4345A001B7D88 /* _RXSwizzling.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _RXSwizzling.m; sourceTree = ""; }; - C84B39E61BA4345A001B7D88 /* CLLocationManager+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CLLocationManager+Rx.swift"; sourceTree = ""; }; - C84B39E81BA4345A001B7D88 /* ControlEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlEvent.swift; sourceTree = ""; }; - C84B39E91BA4345A001B7D88 /* ControlProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlProperty.swift; sourceTree = ""; }; - C84B39EA1BA4345A001B7D88 /* DelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxy.swift; sourceTree = ""; }; - C84B39EB1BA4345A001B7D88 /* DelegateProxyType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxyType.swift; sourceTree = ""; }; - C84B39EC1BA4345A001B7D88 /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = ""; }; - C84B39ED1BA4345A001B7D88 /* Observable+CocoaExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+CocoaExtensions.swift"; sourceTree = ""; }; - C84B39F01BA4345A001B7D88 /* ControlTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlTarget.swift; sourceTree = ""; }; - C84B39F11BA4345A001B7D88 /* Deallocating.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Deallocating.swift; sourceTree = ""; }; - C84B39F21BA4345A001B7D88 /* DeinitAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeinitAction.swift; sourceTree = ""; }; - C84B39F31BA4345A001B7D88 /* KVOObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVOObservable.swift; sourceTree = ""; }; - C84B39F41BA4345A001B7D88 /* KVOObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVOObserver.swift; sourceTree = ""; }; - C84B39F51BA4345A001B7D88 /* NSNotificationCenter+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSNotificationCenter+Rx.swift"; sourceTree = ""; }; - C84B39F61BA4345A001B7D88 /* NSObject+Rx+CoreGraphics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Rx+CoreGraphics.swift"; sourceTree = ""; }; - C84B39F71BA4345A001B7D88 /* NSObject+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Rx.swift"; sourceTree = ""; }; - C84B39F81BA4345A001B7D88 /* NSURLSession+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSURLSession+Rx.swift"; sourceTree = ""; }; - C84B39FA1BA4345A001B7D88 /* RxCLLocationManagerDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCLLocationManagerDelegateProxy.swift; sourceTree = ""; }; - C84B39FB1BA4345A001B7D88 /* RxCocoa.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCocoa.swift; sourceTree = ""; }; - C84B39FC1BA4345A001B7D88 /* RxTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTarget.swift; sourceTree = ""; }; - C84B39FD1BA4345A001B7D88 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - C84B3A001BA4345A001B7D88 /* RxCollectionViewReactiveArrayDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewReactiveArrayDataSource.swift; sourceTree = ""; }; - C84B3A011BA4345A001B7D88 /* RxTableViewReactiveArrayDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewReactiveArrayDataSource.swift; sourceTree = ""; }; - C84B3A031BA4345A001B7D88 /* ItemEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemEvents.swift; sourceTree = ""; }; - C84B3A051BA4345A001B7D88 /* RxCollectionViewDataSourceType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewDataSourceType.swift; sourceTree = ""; }; - C84B3A061BA4345A001B7D88 /* RxTableViewDataSourceType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewDataSourceType.swift; sourceTree = ""; }; - C84B3A081BA4345A001B7D88 /* RxActionSheetDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxActionSheetDelegateProxy.swift; sourceTree = ""; }; - C84B3A091BA4345A001B7D88 /* RxAlertViewDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxAlertViewDelegateProxy.swift; sourceTree = ""; }; - C84B3A0A1BA4345A001B7D88 /* RxCollectionViewDataSourceProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewDataSourceProxy.swift; sourceTree = ""; }; - C84B3A0B1BA4345A001B7D88 /* RxCollectionViewDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewDelegateProxy.swift; sourceTree = ""; }; - C84B3A0C1BA4345A001B7D88 /* RxScrollViewDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxScrollViewDelegateProxy.swift; sourceTree = ""; }; - C84B3A0D1BA4345A001B7D88 /* RxSearchBarDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxSearchBarDelegateProxy.swift; sourceTree = ""; }; - C84B3A0E1BA4345A001B7D88 /* RxTableViewDataSourceProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewDataSourceProxy.swift; sourceTree = ""; }; - C84B3A0F1BA4345A001B7D88 /* RxTableViewDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewDelegateProxy.swift; sourceTree = ""; }; - C84B3A101BA4345A001B7D88 /* RxTextViewDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTextViewDelegateProxy.swift; sourceTree = ""; }; - C84B3A111BA4345A001B7D88 /* UIActionSheet+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIActionSheet+Rx.swift"; sourceTree = ""; }; - C84B3A121BA4345A001B7D88 /* UIAlertView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIAlertView+Rx.swift"; sourceTree = ""; }; - C84B3A131BA4345A001B7D88 /* UIBarButtonItem+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIBarButtonItem+Rx.swift"; sourceTree = ""; }; - C84B3A141BA4345A001B7D88 /* UIButton+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIButton+Rx.swift"; sourceTree = ""; }; - C84B3A151BA4345A001B7D88 /* UICollectionView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UICollectionView+Rx.swift"; sourceTree = ""; }; - C84B3A161BA4345A001B7D88 /* UIControl+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIControl+Rx.swift"; sourceTree = ""; }; - C84B3A171BA4345A001B7D88 /* UIDatePicker+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIDatePicker+Rx.swift"; sourceTree = ""; }; - C84B3A181BA4345A001B7D88 /* UIGestureRecognizer+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Rx.swift"; sourceTree = ""; }; - C84B3A191BA4345A001B7D88 /* UIImageView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImageView+Rx.swift"; sourceTree = ""; }; - C84B3A1A1BA4345A001B7D88 /* UILabel+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UILabel+Rx.swift"; sourceTree = ""; }; - C84B3A1B1BA4345A001B7D88 /* UIScrollView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIScrollView+Rx.swift"; sourceTree = ""; }; - C84B3A1C1BA4345A001B7D88 /* UISearchBar+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UISearchBar+Rx.swift"; sourceTree = ""; }; - C84B3A1D1BA4345A001B7D88 /* UISegmentedControl+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UISegmentedControl+Rx.swift"; sourceTree = ""; }; - C84B3A1E1BA4345A001B7D88 /* UISlider+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UISlider+Rx.swift"; sourceTree = ""; }; - C84B3A1F1BA4345A001B7D88 /* UISwitch+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UISwitch+Rx.swift"; sourceTree = ""; }; - C84B3A201BA4345A001B7D88 /* UITableView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITableView+Rx.swift"; sourceTree = ""; }; - C84B3A211BA4345A001B7D88 /* UITextField+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextField+Rx.swift"; sourceTree = ""; }; - C84B3A221BA4345A001B7D88 /* UITextView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextView+Rx.swift"; sourceTree = ""; }; - C84B3A291BA4345A001B7D88 /* RxCocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RxCocoa.h; sourceTree = ""; }; C859B9A31B45C5D900D012D7 /* PartialUpdatesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PartialUpdatesViewController.swift; sourceTree = ""; }; C859B9A51B45C80700D012D7 /* RxCollectionViewSectionedReloadDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewSectionedReloadDataSource.swift; sourceTree = ""; }; C859B9A71B45C83700D012D7 /* RxCollectionViewSectionedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewSectionedDataSource.swift; sourceTree = ""; }; C859B9A91B45CB0900D012D7 /* RxCollectionViewSectionedAnimatedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewSectionedAnimatedDataSource.swift; sourceTree = ""; }; C859B9AB1B45CF9100D012D7 /* NumberCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberCell.swift; sourceTree = ""; }; C859B9AD1B45CFAB00D012D7 /* NumberSectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberSectionView.swift; sourceTree = ""; }; - C864091D1BA5909000D3C4E8 /* Cancelable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cancelable.swift; sourceTree = ""; }; - C864091F1BA5909000D3C4E8 /* AsyncLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncLock.swift; sourceTree = ""; }; - C86409201BA5909000D3C4E8 /* Lock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Lock.swift; sourceTree = ""; }; - C86409211BA5909000D3C4E8 /* ConnectableObservableType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectableObservableType.swift; sourceTree = ""; }; - C86409231BA5909000D3C4E8 /* Bag.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bag.swift; sourceTree = ""; }; - C86409241BA5909000D3C4E8 /* InfiniteSequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InfiniteSequence.swift; sourceTree = ""; }; - C86409251BA5909000D3C4E8 /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = ""; }; - C86409261BA5909000D3C4E8 /* Disposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Disposable.swift; sourceTree = ""; }; - C86409281BA5909000D3C4E8 /* AnonymousDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnonymousDisposable.swift; sourceTree = ""; }; - C86409291BA5909000D3C4E8 /* BinaryDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinaryDisposable.swift; sourceTree = ""; }; - C864092A1BA5909000D3C4E8 /* CompositeDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompositeDisposable.swift; sourceTree = ""; }; - C864092B1BA5909000D3C4E8 /* DisposeBag.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisposeBag.swift; sourceTree = ""; }; - C864092C1BA5909000D3C4E8 /* DisposeBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisposeBase.swift; sourceTree = ""; }; - C864092D1BA5909000D3C4E8 /* NAryDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NAryDisposable.swift; sourceTree = ""; }; - C864092E1BA5909000D3C4E8 /* NAryDisposable.tt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = NAryDisposable.tt; sourceTree = ""; }; - C864092F1BA5909000D3C4E8 /* NopDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NopDisposable.swift; sourceTree = ""; }; - C86409301BA5909000D3C4E8 /* ScheduledDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduledDisposable.swift; sourceTree = ""; }; - C86409311BA5909000D3C4E8 /* ScopedDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScopedDisposable.swift; sourceTree = ""; }; - C86409321BA5909000D3C4E8 /* SerialDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SerialDisposable.swift; sourceTree = ""; }; - C86409331BA5909000D3C4E8 /* SingleAssignmentDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SingleAssignmentDisposable.swift; sourceTree = ""; }; - C86409341BA5909000D3C4E8 /* StableCompositeDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StableCompositeDisposable.swift; sourceTree = ""; }; - C86409351BA5909000D3C4E8 /* Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = ""; }; - C86409361BA5909000D3C4E8 /* Event.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = ""; }; - C86409371BA5909000D3C4E8 /* ImmediateSchedulerType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImmediateSchedulerType.swift; sourceTree = ""; }; - C86409381BA5909000D3C4E8 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - C86409391BA5909000D3C4E8 /* Observable+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Extensions.swift"; sourceTree = ""; }; - C864093A1BA5909000D3C4E8 /* Observable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = ""; }; - C864093D1BA5909000D3C4E8 /* Amb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Amb.swift; sourceTree = ""; }; - C864093E1BA5909000D3C4E8 /* AnonymousObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnonymousObservable.swift; sourceTree = ""; }; - C864093F1BA5909000D3C4E8 /* AsObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsObservable.swift; sourceTree = ""; }; - C86409401BA5909000D3C4E8 /* Buffer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Buffer.swift; sourceTree = ""; }; - C86409411BA5909000D3C4E8 /* Catch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Catch.swift; sourceTree = ""; }; - C86409421BA5909000D3C4E8 /* CombineLatest+arity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CombineLatest+arity.swift"; sourceTree = ""; }; - C86409431BA5909000D3C4E8 /* CombineLatest+arity.tt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "CombineLatest+arity.tt"; sourceTree = ""; }; - C86409441BA5909000D3C4E8 /* CombineLatest+CollectionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CombineLatest+CollectionType.swift"; sourceTree = ""; }; - C86409451BA5909000D3C4E8 /* CombineLatest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombineLatest.swift; sourceTree = ""; }; - C86409461BA5909000D3C4E8 /* Concat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Concat.swift; sourceTree = ""; }; - C86409471BA5909000D3C4E8 /* ConnectableObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectableObservable.swift; sourceTree = ""; }; - C86409481BA5909000D3C4E8 /* Debug.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Debug.swift; sourceTree = ""; }; - C86409491BA5909000D3C4E8 /* Deferred.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Deferred.swift; sourceTree = ""; }; - C864094A1BA5909000D3C4E8 /* DelaySubscription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelaySubscription.swift; sourceTree = ""; }; - C864094B1BA5909000D3C4E8 /* DistinctUntilChanged.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DistinctUntilChanged.swift; sourceTree = ""; }; - C864094C1BA5909000D3C4E8 /* Do.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Do.swift; sourceTree = ""; }; - C864094D1BA5909000D3C4E8 /* Empty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Empty.swift; sourceTree = ""; }; - C864094E1BA5909000D3C4E8 /* FailWith.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FailWith.swift; sourceTree = ""; }; - C864094F1BA5909000D3C4E8 /* Filter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Filter.swift; sourceTree = ""; }; - C86409501BA5909000D3C4E8 /* FlatMap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlatMap.swift; sourceTree = ""; }; - C86409511BA5909000D3C4E8 /* Generate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Generate.swift; sourceTree = ""; }; - C86409521BA5909000D3C4E8 /* Just.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Just.swift; sourceTree = ""; }; - C86409531BA5909000D3C4E8 /* Map.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Map.swift; sourceTree = ""; }; - C86409541BA5909000D3C4E8 /* Merge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Merge.swift; sourceTree = ""; }; - C86409551BA5909000D3C4E8 /* Multicast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Multicast.swift; sourceTree = ""; }; - C86409561BA5909000D3C4E8 /* Never.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Never.swift; sourceTree = ""; }; - C86409571BA5909000D3C4E8 /* ObserveOn.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserveOn.swift; sourceTree = ""; }; - C86409581BA5909000D3C4E8 /* ObserveOnSerialDispatchQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserveOnSerialDispatchQueue.swift; sourceTree = ""; }; - C86409591BA5909000D3C4E8 /* Producer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Producer.swift; sourceTree = ""; }; - C864095A1BA5909000D3C4E8 /* Reduce.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reduce.swift; sourceTree = ""; }; - C864095B1BA5909000D3C4E8 /* RefCount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefCount.swift; sourceTree = ""; }; - C864095C1BA5909000D3C4E8 /* Sample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sample.swift; sourceTree = ""; }; - C864095D1BA5909000D3C4E8 /* Scan.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Scan.swift; sourceTree = ""; }; - C864095E1BA5909000D3C4E8 /* Sink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sink.swift; sourceTree = ""; }; - C864095F1BA5909000D3C4E8 /* Skip.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Skip.swift; sourceTree = ""; }; - C86409601BA5909000D3C4E8 /* StartWith.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StartWith.swift; sourceTree = ""; }; - C86409611BA5909000D3C4E8 /* SubscribeOn.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscribeOn.swift; sourceTree = ""; }; - C86409621BA5909000D3C4E8 /* Switch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Switch.swift; sourceTree = ""; }; - C86409631BA5909000D3C4E8 /* Take.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Take.swift; sourceTree = ""; }; - C86409641BA5909000D3C4E8 /* TakeUntil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TakeUntil.swift; sourceTree = ""; }; - C86409651BA5909000D3C4E8 /* TakeWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TakeWhile.swift; sourceTree = ""; }; - C86409661BA5909000D3C4E8 /* Throttle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Throttle.swift; sourceTree = ""; }; - C86409671BA5909000D3C4E8 /* Timer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Timer.swift; sourceTree = ""; }; - C86409681BA5909000D3C4E8 /* Zip+arity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Zip+arity.swift"; sourceTree = ""; }; - C86409691BA5909000D3C4E8 /* Zip+arity.tt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Zip+arity.tt"; sourceTree = ""; }; - C864096A1BA5909000D3C4E8 /* Zip+CollectionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Zip+CollectionType.swift"; sourceTree = ""; }; - C864096B1BA5909000D3C4E8 /* Zip.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Zip.swift; sourceTree = ""; }; - C864096C1BA5909000D3C4E8 /* Observable+Aggregate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Aggregate.swift"; sourceTree = ""; }; - C864096D1BA5909000D3C4E8 /* Observable+Binding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Binding.swift"; sourceTree = ""; }; - C864096E1BA5909000D3C4E8 /* Observable+Concurrency.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Concurrency.swift"; sourceTree = ""; }; - C864096F1BA5909000D3C4E8 /* Observable+Creation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Creation.swift"; sourceTree = ""; }; - C86409701BA5909000D3C4E8 /* Observable+Debug.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Debug.swift"; sourceTree = ""; }; - C86409711BA5909000D3C4E8 /* Observable+Multiple.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Multiple.swift"; sourceTree = ""; }; - C86409721BA5909000D3C4E8 /* Observable+Single.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Single.swift"; sourceTree = ""; }; - C86409731BA5909000D3C4E8 /* Observable+StandardSequenceOperators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+StandardSequenceOperators.swift"; sourceTree = ""; }; - C86409741BA5909000D3C4E8 /* Observable+Time.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Time.swift"; sourceTree = ""; }; - C86409751BA5909000D3C4E8 /* ObservableType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableType.swift; sourceTree = ""; }; - C86409761BA5909000D3C4E8 /* ObserverOf.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserverOf.swift; sourceTree = ""; }; - C86409781BA5909000D3C4E8 /* AnonymousObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnonymousObserver.swift; sourceTree = ""; }; - C86409791BA5909000D3C4E8 /* ObserverBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserverBase.swift; sourceTree = ""; }; - C864097A1BA5909000D3C4E8 /* TailRecursiveSink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TailRecursiveSink.swift; sourceTree = ""; }; - C864097B1BA5909000D3C4E8 /* ObserverType+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObserverType+Extensions.swift"; sourceTree = ""; }; - C864097C1BA5909000D3C4E8 /* ObserverType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserverType.swift; sourceTree = ""; }; - C864097D1BA5909000D3C4E8 /* Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rx.swift; sourceTree = ""; }; - C864097E1BA5909000D3C4E8 /* RxBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxBox.swift; sourceTree = ""; }; - C86409801BA5909000D3C4E8 /* ConcurrentDispatchQueueScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConcurrentDispatchQueueScheduler.swift; sourceTree = ""; }; - C86409811BA5909000D3C4E8 /* CurrentThreadScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurrentThreadScheduler.swift; sourceTree = ""; }; - C86409821BA5909000D3C4E8 /* DispatchQueueSchedulerPriority.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DispatchQueueSchedulerPriority.swift; sourceTree = ""; }; - C86409831BA5909000D3C4E8 /* MainScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainScheduler.swift; sourceTree = ""; }; - C86409841BA5909000D3C4E8 /* OperationQueueScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationQueueScheduler.swift; sourceTree = ""; }; - C86409851BA5909000D3C4E8 /* RecursiveScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecursiveScheduler.swift; sourceTree = ""; }; - C86409861BA5909000D3C4E8 /* ScheduledItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduledItem.swift; sourceTree = ""; }; - C86409871BA5909000D3C4E8 /* SchedulerServices+Emulation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SchedulerServices+Emulation.swift"; sourceTree = ""; }; - C86409881BA5909000D3C4E8 /* SerialDispatchQueueScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SerialDispatchQueueScheduler.swift; sourceTree = ""; }; - C86409891BA5909000D3C4E8 /* SchedulerType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchedulerType.swift; sourceTree = ""; }; - C864098B1BA5909000D3C4E8 /* BehaviorSubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BehaviorSubject.swift; sourceTree = ""; }; - C864098C1BA5909000D3C4E8 /* PublishSubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PublishSubject.swift; sourceTree = ""; }; - C864098D1BA5909000D3C4E8 /* ReplaySubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReplaySubject.swift; sourceTree = ""; }; - C864098E1BA5909000D3C4E8 /* SubjectType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubjectType.swift; sourceTree = ""; }; - C864098F1BA5909000D3C4E8 /* Variable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Variable.swift; sourceTree = ""; }; - C86409FE1BA5A87200D3C4E8 /* Range.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Range.swift; sourceTree = ""; }; - C8640A001BA5AB5A00D3C4E8 /* Repeat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Repeat.swift; sourceTree = ""; }; C86E2F321AE5A0CA00C31024 /* SearchResultViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SearchResultViewModel.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C86E2F331AE5A0CA00C31024 /* SearchViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SearchViewModel.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C86E2F3B1AE5A0CA00C31024 /* WikipediaAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WikipediaAPI.swift; sourceTree = ""; }; @@ -630,6 +468,186 @@ C890A6571AEBD26B00AFF7E6 /* GitHubSignupViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = GitHubSignupViewController.swift; path = Views/GitHubSignupViewController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C890A6591AEBD28A00AFF7E6 /* GitHubAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = GitHubAPI.swift; path = GitHubAPI/GitHubAPI.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C890A65C1AEC084100AFF7E6 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + C89464281BC6C2B00055219D /* Cancelable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cancelable.swift; sourceTree = ""; }; + C894642A1BC6C2B00055219D /* AsyncLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncLock.swift; sourceTree = ""; }; + C894642B1BC6C2B00055219D /* Lock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Lock.swift; sourceTree = ""; }; + C894642C1BC6C2B00055219D /* ConnectableObservableType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectableObservableType.swift; sourceTree = ""; }; + C894642E1BC6C2B00055219D /* Bag.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bag.swift; sourceTree = ""; }; + C894642F1BC6C2B00055219D /* InfiniteSequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InfiniteSequence.swift; sourceTree = ""; }; + C89464301BC6C2B00055219D /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = ""; }; + C89464311BC6C2B00055219D /* Disposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Disposable.swift; sourceTree = ""; }; + C89464331BC6C2B00055219D /* AnonymousDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnonymousDisposable.swift; sourceTree = ""; }; + C89464341BC6C2B00055219D /* BinaryDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinaryDisposable.swift; sourceTree = ""; }; + C89464351BC6C2B00055219D /* CompositeDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompositeDisposable.swift; sourceTree = ""; }; + C89464361BC6C2B00055219D /* DisposeBag.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisposeBag.swift; sourceTree = ""; }; + C89464371BC6C2B00055219D /* DisposeBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisposeBase.swift; sourceTree = ""; }; + C89464381BC6C2B00055219D /* NAryDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NAryDisposable.swift; sourceTree = ""; }; + C89464391BC6C2B00055219D /* NAryDisposable.tt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = NAryDisposable.tt; sourceTree = ""; }; + C894643A1BC6C2B00055219D /* NopDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NopDisposable.swift; sourceTree = ""; }; + C894643B1BC6C2B00055219D /* ScheduledDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduledDisposable.swift; sourceTree = ""; }; + C894643C1BC6C2B00055219D /* ScopedDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScopedDisposable.swift; sourceTree = ""; }; + C894643D1BC6C2B00055219D /* SerialDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SerialDisposable.swift; sourceTree = ""; }; + C894643E1BC6C2B00055219D /* SingleAssignmentDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SingleAssignmentDisposable.swift; sourceTree = ""; }; + C894643F1BC6C2B00055219D /* StableCompositeDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StableCompositeDisposable.swift; sourceTree = ""; }; + C89464401BC6C2B00055219D /* Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = ""; }; + C89464411BC6C2B00055219D /* Event.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = ""; }; + C89464421BC6C2B00055219D /* ImmediateSchedulerType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImmediateSchedulerType.swift; sourceTree = ""; }; + C89464431BC6C2B00055219D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C89464441BC6C2B00055219D /* Observable+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Extensions.swift"; sourceTree = ""; }; + C89464451BC6C2B00055219D /* Observable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = ""; }; + C89464461BC6C2B00055219D /* ObservableConvertibleType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableConvertibleType.swift; sourceTree = ""; }; + C89464491BC6C2B00055219D /* Amb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Amb.swift; sourceTree = ""; }; + C894644A1BC6C2B00055219D /* AnonymousObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnonymousObservable.swift; sourceTree = ""; }; + C894644B1BC6C2B00055219D /* AsObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsObservable.swift; sourceTree = ""; }; + C894644C1BC6C2B00055219D /* Buffer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Buffer.swift; sourceTree = ""; }; + C894644D1BC6C2B00055219D /* Catch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Catch.swift; sourceTree = ""; }; + C894644E1BC6C2B00055219D /* CombineLatest+arity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CombineLatest+arity.swift"; sourceTree = ""; }; + C894644F1BC6C2B00055219D /* CombineLatest+arity.tt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "CombineLatest+arity.tt"; sourceTree = ""; }; + C89464501BC6C2B00055219D /* CombineLatest+CollectionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CombineLatest+CollectionType.swift"; sourceTree = ""; }; + C89464511BC6C2B00055219D /* CombineLatest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombineLatest.swift; sourceTree = ""; }; + C89464521BC6C2B00055219D /* Concat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Concat.swift; sourceTree = ""; }; + C89464531BC6C2B00055219D /* ConnectableObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectableObservable.swift; sourceTree = ""; }; + C89464541BC6C2B00055219D /* Debug.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Debug.swift; sourceTree = ""; }; + C89464551BC6C2B00055219D /* Deferred.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Deferred.swift; sourceTree = ""; }; + C89464561BC6C2B00055219D /* DelaySubscription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelaySubscription.swift; sourceTree = ""; }; + C89464571BC6C2B00055219D /* DistinctUntilChanged.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DistinctUntilChanged.swift; sourceTree = ""; }; + C89464581BC6C2B00055219D /* Do.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Do.swift; sourceTree = ""; }; + C89464591BC6C2B00055219D /* Empty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Empty.swift; sourceTree = ""; }; + C894645A1BC6C2B00055219D /* FailWith.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FailWith.swift; sourceTree = ""; }; + C894645B1BC6C2B00055219D /* Filter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Filter.swift; sourceTree = ""; }; + C894645C1BC6C2B00055219D /* FlatMap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlatMap.swift; sourceTree = ""; }; + C894645D1BC6C2B00055219D /* Generate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Generate.swift; sourceTree = ""; }; + C894645E1BC6C2B00055219D /* Just.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Just.swift; sourceTree = ""; }; + C894645F1BC6C2B00055219D /* Map.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Map.swift; sourceTree = ""; }; + C89464601BC6C2B00055219D /* Merge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Merge.swift; sourceTree = ""; }; + C89464611BC6C2B00055219D /* Multicast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Multicast.swift; sourceTree = ""; }; + C89464621BC6C2B00055219D /* Never.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Never.swift; sourceTree = ""; }; + C89464631BC6C2B00055219D /* ObserveOn.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserveOn.swift; sourceTree = ""; }; + C89464641BC6C2B00055219D /* ObserveOnSerialDispatchQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserveOnSerialDispatchQueue.swift; sourceTree = ""; }; + C89464651BC6C2B00055219D /* Producer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Producer.swift; sourceTree = ""; }; + C89464661BC6C2B00055219D /* Range.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Range.swift; sourceTree = ""; }; + C89464671BC6C2B00055219D /* Reduce.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reduce.swift; sourceTree = ""; }; + C89464681BC6C2B00055219D /* RefCount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefCount.swift; sourceTree = ""; }; + C89464691BC6C2B00055219D /* Repeat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Repeat.swift; sourceTree = ""; }; + C894646A1BC6C2B00055219D /* Sample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sample.swift; sourceTree = ""; }; + C894646B1BC6C2B00055219D /* Scan.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Scan.swift; sourceTree = ""; }; + C894646C1BC6C2B00055219D /* Sink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sink.swift; sourceTree = ""; }; + C894646D1BC6C2B00055219D /* Skip.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Skip.swift; sourceTree = ""; }; + C894646E1BC6C2B00055219D /* StartWith.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StartWith.swift; sourceTree = ""; }; + C894646F1BC6C2B00055219D /* SubscribeOn.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscribeOn.swift; sourceTree = ""; }; + C89464701BC6C2B00055219D /* Switch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Switch.swift; sourceTree = ""; }; + C89464711BC6C2B00055219D /* Take.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Take.swift; sourceTree = ""; }; + C89464721BC6C2B00055219D /* TakeUntil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TakeUntil.swift; sourceTree = ""; }; + C89464731BC6C2B00055219D /* TakeWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TakeWhile.swift; sourceTree = ""; }; + C89464741BC6C2B00055219D /* Throttle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Throttle.swift; sourceTree = ""; }; + C89464751BC6C2B00055219D /* Timer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Timer.swift; sourceTree = ""; }; + C89464761BC6C2B00055219D /* Zip+arity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Zip+arity.swift"; sourceTree = ""; }; + C89464771BC6C2B00055219D /* Zip+arity.tt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Zip+arity.tt"; sourceTree = ""; }; + C89464781BC6C2B00055219D /* Zip+CollectionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Zip+CollectionType.swift"; sourceTree = ""; }; + C89464791BC6C2B00055219D /* Zip.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Zip.swift; sourceTree = ""; }; + C894647A1BC6C2B00055219D /* Observable+Aggregate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Aggregate.swift"; sourceTree = ""; }; + C894647B1BC6C2B00055219D /* Observable+Binding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Binding.swift"; sourceTree = ""; }; + C894647C1BC6C2B00055219D /* Observable+Concurrency.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Concurrency.swift"; sourceTree = ""; }; + C894647D1BC6C2B00055219D /* Observable+Creation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Creation.swift"; sourceTree = ""; }; + C894647E1BC6C2B00055219D /* Observable+Debug.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Debug.swift"; sourceTree = ""; }; + C894647F1BC6C2B00055219D /* Observable+Multiple.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Multiple.swift"; sourceTree = ""; }; + C89464801BC6C2B00055219D /* Observable+Single.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Single.swift"; sourceTree = ""; }; + C89464811BC6C2B00055219D /* Observable+StandardSequenceOperators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+StandardSequenceOperators.swift"; sourceTree = ""; }; + C89464821BC6C2B00055219D /* Observable+Time.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Time.swift"; sourceTree = ""; }; + C89464831BC6C2B00055219D /* ObservableType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableType.swift; sourceTree = ""; }; + C89464841BC6C2B00055219D /* ObserverOf.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserverOf.swift; sourceTree = ""; }; + C89464861BC6C2B00055219D /* AnonymousObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnonymousObserver.swift; sourceTree = ""; }; + C89464871BC6C2B00055219D /* ObserverBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserverBase.swift; sourceTree = ""; }; + C89464881BC6C2B00055219D /* TailRecursiveSink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TailRecursiveSink.swift; sourceTree = ""; }; + C89464891BC6C2B00055219D /* ObserverType+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObserverType+Extensions.swift"; sourceTree = ""; }; + C894648A1BC6C2B00055219D /* ObserverType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserverType.swift; sourceTree = ""; }; + C894648B1BC6C2B00055219D /* Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rx.swift; sourceTree = ""; }; + C894648C1BC6C2B00055219D /* RxBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxBox.swift; sourceTree = ""; }; + C894648E1BC6C2B00055219D /* ConcurrentDispatchQueueScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConcurrentDispatchQueueScheduler.swift; sourceTree = ""; }; + C894648F1BC6C2B00055219D /* CurrentThreadScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurrentThreadScheduler.swift; sourceTree = ""; }; + C89464901BC6C2B00055219D /* DispatchQueueSchedulerPriority.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DispatchQueueSchedulerPriority.swift; sourceTree = ""; }; + C89464911BC6C2B00055219D /* MainScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainScheduler.swift; sourceTree = ""; }; + C89464921BC6C2B00055219D /* OperationQueueScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationQueueScheduler.swift; sourceTree = ""; }; + C89464931BC6C2B00055219D /* RecursiveScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecursiveScheduler.swift; sourceTree = ""; }; + C89464941BC6C2B00055219D /* ScheduledItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduledItem.swift; sourceTree = ""; }; + C89464951BC6C2B00055219D /* SchedulerServices+Emulation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SchedulerServices+Emulation.swift"; sourceTree = ""; }; + C89464961BC6C2B00055219D /* SerialDispatchQueueScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SerialDispatchQueueScheduler.swift; sourceTree = ""; }; + C89464971BC6C2B00055219D /* SchedulerType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchedulerType.swift; sourceTree = ""; }; + C89464991BC6C2B00055219D /* BehaviorSubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BehaviorSubject.swift; sourceTree = ""; }; + C894649A1BC6C2B00055219D /* PublishSubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PublishSubject.swift; sourceTree = ""; }; + C894649B1BC6C2B00055219D /* ReplaySubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReplaySubject.swift; sourceTree = ""; }; + C894649C1BC6C2B00055219D /* SubjectType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubjectType.swift; sourceTree = ""; }; + C894649D1BC6C2B00055219D /* Variable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Variable.swift; sourceTree = ""; }; + C894650E1BC6C2BC0055219D /* _RX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _RX.h; sourceTree = ""; }; + C894650F1BC6C2BC0055219D /* _RX.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _RX.m; sourceTree = ""; }; + C89465101BC6C2BC0055219D /* _RXDelegateProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _RXDelegateProxy.h; sourceTree = ""; }; + C89465111BC6C2BC0055219D /* _RXDelegateProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _RXDelegateProxy.m; sourceTree = ""; }; + C89465121BC6C2BC0055219D /* _RXKVOObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _RXKVOObserver.h; sourceTree = ""; }; + C89465131BC6C2BC0055219D /* _RXKVOObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _RXKVOObserver.m; sourceTree = ""; }; + C89465141BC6C2BC0055219D /* _RXSwizzling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _RXSwizzling.h; sourceTree = ""; }; + C89465151BC6C2BC0055219D /* _RXSwizzling.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _RXSwizzling.m; sourceTree = ""; }; + C89465161BC6C2BC0055219D /* CLLocationManager+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CLLocationManager+Rx.swift"; sourceTree = ""; }; + C89465181BC6C2BC0055219D /* ControlEvent+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControlEvent+Driver.swift"; sourceTree = ""; }; + C89465191BC6C2BC0055219D /* ControlEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlEvent.swift; sourceTree = ""; }; + C894651A1BC6C2BC0055219D /* ControlProperty+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControlProperty+Driver.swift"; sourceTree = ""; }; + C894651B1BC6C2BC0055219D /* ControlProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlProperty.swift; sourceTree = ""; }; + C894651C1BC6C2BC0055219D /* Driver+Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Driver+Operators.swift"; sourceTree = ""; }; + C894651D1BC6C2BC0055219D /* Driver+Subscription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Driver+Subscription.swift"; sourceTree = ""; }; + C894651E1BC6C2BC0055219D /* Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Driver.swift; sourceTree = ""; }; + C894651F1BC6C2BC0055219D /* ObservableConvertibleType+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObservableConvertibleType+Driver.swift"; sourceTree = ""; }; + C89465201BC6C2BC0055219D /* DelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxy.swift; sourceTree = ""; }; + C89465211BC6C2BC0055219D /* DelegateProxyType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxyType.swift; sourceTree = ""; }; + C89465221BC6C2BC0055219D /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = ""; }; + C89465231BC6C2BC0055219D /* Observable+Bind.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Bind.swift"; sourceTree = ""; }; + C89465261BC6C2BC0055219D /* ControlTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlTarget.swift; sourceTree = ""; }; + C89465271BC6C2BC0055219D /* Deallocating.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Deallocating.swift; sourceTree = ""; }; + C89465281BC6C2BC0055219D /* DeinitAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeinitAction.swift; sourceTree = ""; }; + C89465291BC6C2BC0055219D /* KVOObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVOObservable.swift; sourceTree = ""; }; + C894652A1BC6C2BC0055219D /* KVOObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVOObserver.swift; sourceTree = ""; }; + C894652B1BC6C2BC0055219D /* NSNotificationCenter+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSNotificationCenter+Rx.swift"; sourceTree = ""; }; + C894652C1BC6C2BC0055219D /* NSObject+Rx+CoreGraphics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Rx+CoreGraphics.swift"; sourceTree = ""; }; + C894652D1BC6C2BC0055219D /* NSObject+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Rx.swift"; sourceTree = ""; }; + C894652E1BC6C2BC0055219D /* NSURLSession+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSURLSession+Rx.swift"; sourceTree = ""; }; + C89465301BC6C2BC0055219D /* RxCLLocationManagerDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCLLocationManagerDelegateProxy.swift; sourceTree = ""; }; + C89465311BC6C2BC0055219D /* RxCocoa.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCocoa.swift; sourceTree = ""; }; + C89465321BC6C2BC0055219D /* RxTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTarget.swift; sourceTree = ""; }; + C89465331BC6C2BC0055219D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C89465361BC6C2BC0055219D /* RxCollectionViewReactiveArrayDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewReactiveArrayDataSource.swift; sourceTree = ""; }; + C89465371BC6C2BC0055219D /* RxTableViewReactiveArrayDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewReactiveArrayDataSource.swift; sourceTree = ""; }; + C89465391BC6C2BC0055219D /* ItemEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemEvents.swift; sourceTree = ""; }; + C894653B1BC6C2BC0055219D /* RxCollectionViewDataSourceType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewDataSourceType.swift; sourceTree = ""; }; + C894653C1BC6C2BC0055219D /* RxTableViewDataSourceType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewDataSourceType.swift; sourceTree = ""; }; + C894653E1BC6C2BC0055219D /* RxActionSheetDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxActionSheetDelegateProxy.swift; sourceTree = ""; }; + C894653F1BC6C2BC0055219D /* RxAlertViewDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxAlertViewDelegateProxy.swift; sourceTree = ""; }; + C89465401BC6C2BC0055219D /* RxCollectionViewDataSourceProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewDataSourceProxy.swift; sourceTree = ""; }; + C89465411BC6C2BC0055219D /* RxCollectionViewDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewDelegateProxy.swift; sourceTree = ""; }; + C89465421BC6C2BC0055219D /* RxScrollViewDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxScrollViewDelegateProxy.swift; sourceTree = ""; }; + C89465431BC6C2BC0055219D /* RxSearchBarDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxSearchBarDelegateProxy.swift; sourceTree = ""; }; + C89465441BC6C2BC0055219D /* RxTableViewDataSourceProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewDataSourceProxy.swift; sourceTree = ""; }; + C89465451BC6C2BC0055219D /* RxTableViewDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewDelegateProxy.swift; sourceTree = ""; }; + C89465461BC6C2BC0055219D /* RxTextViewDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTextViewDelegateProxy.swift; sourceTree = ""; }; + C89465471BC6C2BC0055219D /* UIActionSheet+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIActionSheet+Rx.swift"; sourceTree = ""; }; + C89465481BC6C2BC0055219D /* UIAlertView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIAlertView+Rx.swift"; sourceTree = ""; }; + C89465491BC6C2BC0055219D /* UIBarButtonItem+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIBarButtonItem+Rx.swift"; sourceTree = ""; }; + C894654A1BC6C2BC0055219D /* UIButton+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIButton+Rx.swift"; sourceTree = ""; }; + C894654B1BC6C2BC0055219D /* UICollectionView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UICollectionView+Rx.swift"; sourceTree = ""; }; + C894654C1BC6C2BC0055219D /* UIControl+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIControl+Rx.swift"; sourceTree = ""; }; + C894654D1BC6C2BC0055219D /* UIDatePicker+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIDatePicker+Rx.swift"; sourceTree = ""; }; + C894654E1BC6C2BC0055219D /* UIGestureRecognizer+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Rx.swift"; sourceTree = ""; }; + C894654F1BC6C2BC0055219D /* UIImageView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImageView+Rx.swift"; sourceTree = ""; }; + C89465501BC6C2BC0055219D /* UILabel+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UILabel+Rx.swift"; sourceTree = ""; }; + C89465511BC6C2BC0055219D /* UIScrollView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIScrollView+Rx.swift"; sourceTree = ""; }; + C89465521BC6C2BC0055219D /* UISearchBar+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UISearchBar+Rx.swift"; sourceTree = ""; }; + C89465531BC6C2BC0055219D /* UISegmentedControl+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UISegmentedControl+Rx.swift"; sourceTree = ""; }; + C89465541BC6C2BC0055219D /* UISlider+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UISlider+Rx.swift"; sourceTree = ""; }; + C89465551BC6C2BC0055219D /* UIStepper+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStepper+Rx.swift"; sourceTree = ""; }; + C89465561BC6C2BC0055219D /* UISwitch+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UISwitch+Rx.swift"; sourceTree = ""; }; + C89465571BC6C2BC0055219D /* UITableView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITableView+Rx.swift"; sourceTree = ""; }; + C89465581BC6C2BC0055219D /* UITextField+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextField+Rx.swift"; sourceTree = ""; }; + C89465591BC6C2BC0055219D /* UITextView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextView+Rx.swift"; sourceTree = ""; }; + C89465601BC6C2BC0055219D /* RxCocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RxCocoa.h; sourceTree = ""; }; + C89CDB611BCC45DC002063D9 /* ShareReplay1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareReplay1.swift; sourceTree = ""; }; + C89CDB621BCC45DC002063D9 /* SkipUntil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipUntil.swift; sourceTree = ""; }; C8A2A2C71B4049E300F11F09 /* PseudoRandomGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PseudoRandomGenerator.swift; sourceTree = ""; }; C8A2A2CA1B404A1200F11F09 /* Randomizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Randomizer.swift; sourceTree = ""; }; C8A468EB1B8A8BC900BF917B /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -652,8 +670,6 @@ C8DF92F01B0B3E67009BCF9A /* Info-OSX.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-OSX.plist"; sourceTree = ""; }; C8DF92F21B0B3E71009BCF9A /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = ""; }; C8DF92F51B0B43A4009BCF9A /* IntroductionExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = IntroductionExampleViewController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D245D9E61BC6C60800CAB388 /* SkipUntil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipUntil.swift; sourceTree = ""; }; - D2FC15B61BCBAA01007361FF /* SkipWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipWhile.swift; sourceTree = ""; }; EC91FB941BBA144400973245 /* GitHubSearchRepositoriesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubSearchRepositoriesViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -827,161 +843,12 @@ C836EB911B8A7A3700AB941D /* NoModule */ = { isa = PBXGroup; children = ( - C864091C1BA5909000D3C4E8 /* RxSwift */, - C84B39DC1BA4345A001B7D88 /* RxCocoa */, + C894650C1BC6C2BC0055219D /* RxCocoa */, + C89464271BC6C2B00055219D /* RxSwift */, ); name = NoModule; sourceTree = ""; }; - C84B39DC1BA4345A001B7D88 /* RxCocoa */ = { - isa = PBXGroup; - children = ( - C84B39DD1BA4345A001B7D88 /* Common */, - C84B39FD1BA4345A001B7D88 /* Info.plist */, - C84B39FE1BA4345A001B7D88 /* iOS */, - C84B3A291BA4345A001B7D88 /* RxCocoa.h */, - ); - name = RxCocoa; - path = ../RxCocoa; - sourceTree = ""; - }; - C84B39DD1BA4345A001B7D88 /* Common */ = { - isa = PBXGroup; - children = ( - C84B39DE1BA4345A001B7D88 /* _RX.h */, - C84B39DF1BA4345A001B7D88 /* _RX.m */, - C84B39E01BA4345A001B7D88 /* _RXDelegateProxy.h */, - C84B39E11BA4345A001B7D88 /* _RXDelegateProxy.m */, - C84B39E21BA4345A001B7D88 /* _RXKVOObserver.h */, - C84B39E31BA4345A001B7D88 /* _RXKVOObserver.m */, - C84B39E41BA4345A001B7D88 /* _RXSwizzling.h */, - C84B39E51BA4345A001B7D88 /* _RXSwizzling.m */, - C84B39E61BA4345A001B7D88 /* CLLocationManager+Rx.swift */, - C84B39E71BA4345A001B7D88 /* CocoaUnits */, - C84B39EA1BA4345A001B7D88 /* DelegateProxy.swift */, - C84B39EB1BA4345A001B7D88 /* DelegateProxyType.swift */, - C84B39EC1BA4345A001B7D88 /* Logging.swift */, - C84B39ED1BA4345A001B7D88 /* Observable+CocoaExtensions.swift */, - C84B39EE1BA4345A001B7D88 /* Observables */, - C84B39F91BA4345A001B7D88 /* Proxies */, - C84B39FB1BA4345A001B7D88 /* RxCocoa.swift */, - C84B39FC1BA4345A001B7D88 /* RxTarget.swift */, - ); - path = Common; - sourceTree = ""; - }; - C84B39E71BA4345A001B7D88 /* CocoaUnits */ = { - isa = PBXGroup; - children = ( - C84B39E81BA4345A001B7D88 /* ControlEvent.swift */, - C84B39E91BA4345A001B7D88 /* ControlProperty.swift */, - ); - path = CocoaUnits; - sourceTree = ""; - }; - C84B39EE1BA4345A001B7D88 /* Observables */ = { - isa = PBXGroup; - children = ( - C84B39EF1BA4345A001B7D88 /* Implementations */, - C84B39F51BA4345A001B7D88 /* NSNotificationCenter+Rx.swift */, - C84B39F61BA4345A001B7D88 /* NSObject+Rx+CoreGraphics.swift */, - C84B39F71BA4345A001B7D88 /* NSObject+Rx.swift */, - C84B39F81BA4345A001B7D88 /* NSURLSession+Rx.swift */, - ); - path = Observables; - sourceTree = ""; - }; - C84B39EF1BA4345A001B7D88 /* Implementations */ = { - isa = PBXGroup; - children = ( - C84B39F01BA4345A001B7D88 /* ControlTarget.swift */, - C84B39F11BA4345A001B7D88 /* Deallocating.swift */, - C84B39F21BA4345A001B7D88 /* DeinitAction.swift */, - C84B39F31BA4345A001B7D88 /* KVOObservable.swift */, - C84B39F41BA4345A001B7D88 /* KVOObserver.swift */, - ); - path = Implementations; - sourceTree = ""; - }; - C84B39F91BA4345A001B7D88 /* Proxies */ = { - isa = PBXGroup; - children = ( - C84B39FA1BA4345A001B7D88 /* RxCLLocationManagerDelegateProxy.swift */, - ); - path = Proxies; - sourceTree = ""; - }; - C84B39FE1BA4345A001B7D88 /* iOS */ = { - isa = PBXGroup; - children = ( - C84B39FF1BA4345A001B7D88 /* DataSources */, - C84B3A021BA4345A001B7D88 /* Events */, - C84B3A041BA4345A001B7D88 /* Protocols */, - C84B3A071BA4345A001B7D88 /* Proxies */, - C84B3A111BA4345A001B7D88 /* UIActionSheet+Rx.swift */, - C84B3A121BA4345A001B7D88 /* UIAlertView+Rx.swift */, - C84B3A131BA4345A001B7D88 /* UIBarButtonItem+Rx.swift */, - C84B3A141BA4345A001B7D88 /* UIButton+Rx.swift */, - C84B3A151BA4345A001B7D88 /* UICollectionView+Rx.swift */, - C84B3A161BA4345A001B7D88 /* UIControl+Rx.swift */, - C84B3A171BA4345A001B7D88 /* UIDatePicker+Rx.swift */, - C84B3A181BA4345A001B7D88 /* UIGestureRecognizer+Rx.swift */, - C84B3A191BA4345A001B7D88 /* UIImageView+Rx.swift */, - C84B3A1A1BA4345A001B7D88 /* UILabel+Rx.swift */, - C84B3A1B1BA4345A001B7D88 /* UIScrollView+Rx.swift */, - C84B3A1C1BA4345A001B7D88 /* UISearchBar+Rx.swift */, - C84B3A1D1BA4345A001B7D88 /* UISegmentedControl+Rx.swift */, - C84B3A1E1BA4345A001B7D88 /* UISlider+Rx.swift */, - C84B3A1F1BA4345A001B7D88 /* UISwitch+Rx.swift */, - C84B3A201BA4345A001B7D88 /* UITableView+Rx.swift */, - C84B3A211BA4345A001B7D88 /* UITextField+Rx.swift */, - C84B3A221BA4345A001B7D88 /* UITextView+Rx.swift */, - ); - path = iOS; - sourceTree = ""; - }; - C84B39FF1BA4345A001B7D88 /* DataSources */ = { - isa = PBXGroup; - children = ( - C84B3A001BA4345A001B7D88 /* RxCollectionViewReactiveArrayDataSource.swift */, - C84B3A011BA4345A001B7D88 /* RxTableViewReactiveArrayDataSource.swift */, - ); - path = DataSources; - sourceTree = ""; - }; - C84B3A021BA4345A001B7D88 /* Events */ = { - isa = PBXGroup; - children = ( - C84B3A031BA4345A001B7D88 /* ItemEvents.swift */, - ); - path = Events; - sourceTree = ""; - }; - C84B3A041BA4345A001B7D88 /* Protocols */ = { - isa = PBXGroup; - children = ( - C84B3A051BA4345A001B7D88 /* RxCollectionViewDataSourceType.swift */, - C84B3A061BA4345A001B7D88 /* RxTableViewDataSourceType.swift */, - ); - path = Protocols; - sourceTree = ""; - }; - C84B3A071BA4345A001B7D88 /* Proxies */ = { - isa = PBXGroup; - children = ( - C84B3A081BA4345A001B7D88 /* RxActionSheetDelegateProxy.swift */, - C84B3A091BA4345A001B7D88 /* RxAlertViewDelegateProxy.swift */, - C84B3A0A1BA4345A001B7D88 /* RxCollectionViewDataSourceProxy.swift */, - C84B3A0B1BA4345A001B7D88 /* RxCollectionViewDelegateProxy.swift */, - C84B3A0C1BA4345A001B7D88 /* RxScrollViewDelegateProxy.swift */, - C84B3A0D1BA4345A001B7D88 /* RxSearchBarDelegateProxy.swift */, - C84B3A0E1BA4345A001B7D88 /* RxTableViewDataSourceProxy.swift */, - C84B3A0F1BA4345A001B7D88 /* RxTableViewDelegateProxy.swift */, - C84B3A101BA4345A001B7D88 /* RxTextViewDelegateProxy.swift */, - ); - path = Proxies; - sourceTree = ""; - }; C859B9A21B45C5D900D012D7 /* 07 PartialUpdates */ = { isa = PBXGroup; children = ( @@ -993,189 +860,6 @@ path = PartialUpdates; sourceTree = ""; }; - C864091C1BA5909000D3C4E8 /* RxSwift */ = { - isa = PBXGroup; - children = ( - C864091D1BA5909000D3C4E8 /* Cancelable.swift */, - C864091E1BA5909000D3C4E8 /* Concurrency */, - C86409211BA5909000D3C4E8 /* ConnectableObservableType.swift */, - C86409221BA5909000D3C4E8 /* DataStructures */, - C86409261BA5909000D3C4E8 /* Disposable.swift */, - C86409271BA5909000D3C4E8 /* Disposables */, - C86409351BA5909000D3C4E8 /* Error.swift */, - C86409361BA5909000D3C4E8 /* Event.swift */, - C86409371BA5909000D3C4E8 /* ImmediateSchedulerType.swift */, - C86409381BA5909000D3C4E8 /* Info.plist */, - C86409391BA5909000D3C4E8 /* Observable+Extensions.swift */, - C864093A1BA5909000D3C4E8 /* Observable.swift */, - C864093B1BA5909000D3C4E8 /* Observables */, - C86409751BA5909000D3C4E8 /* ObservableType.swift */, - C86409761BA5909000D3C4E8 /* ObserverOf.swift */, - C86409771BA5909000D3C4E8 /* Observers */, - C864097B1BA5909000D3C4E8 /* ObserverType+Extensions.swift */, - C864097C1BA5909000D3C4E8 /* ObserverType.swift */, - C864097D1BA5909000D3C4E8 /* Rx.swift */, - C864097E1BA5909000D3C4E8 /* RxBox.swift */, - C864097F1BA5909000D3C4E8 /* Schedulers */, - C86409891BA5909000D3C4E8 /* SchedulerType.swift */, - C864098A1BA5909000D3C4E8 /* Subjects */, - ); - name = RxSwift; - path = ../RxSwift; - sourceTree = ""; - }; - C864091E1BA5909000D3C4E8 /* Concurrency */ = { - isa = PBXGroup; - children = ( - C864091F1BA5909000D3C4E8 /* AsyncLock.swift */, - C86409201BA5909000D3C4E8 /* Lock.swift */, - ); - path = Concurrency; - sourceTree = ""; - }; - C86409221BA5909000D3C4E8 /* DataStructures */ = { - isa = PBXGroup; - children = ( - C86409231BA5909000D3C4E8 /* Bag.swift */, - C86409241BA5909000D3C4E8 /* InfiniteSequence.swift */, - C86409251BA5909000D3C4E8 /* Queue.swift */, - ); - path = DataStructures; - sourceTree = ""; - }; - C86409271BA5909000D3C4E8 /* Disposables */ = { - isa = PBXGroup; - children = ( - C86409281BA5909000D3C4E8 /* AnonymousDisposable.swift */, - C86409291BA5909000D3C4E8 /* BinaryDisposable.swift */, - C864092A1BA5909000D3C4E8 /* CompositeDisposable.swift */, - C864092B1BA5909000D3C4E8 /* DisposeBag.swift */, - C864092C1BA5909000D3C4E8 /* DisposeBase.swift */, - C864092D1BA5909000D3C4E8 /* NAryDisposable.swift */, - C864092E1BA5909000D3C4E8 /* NAryDisposable.tt */, - C864092F1BA5909000D3C4E8 /* NopDisposable.swift */, - C86409301BA5909000D3C4E8 /* ScheduledDisposable.swift */, - C86409311BA5909000D3C4E8 /* ScopedDisposable.swift */, - C86409321BA5909000D3C4E8 /* SerialDisposable.swift */, - C86409331BA5909000D3C4E8 /* SingleAssignmentDisposable.swift */, - C86409341BA5909000D3C4E8 /* StableCompositeDisposable.swift */, - ); - path = Disposables; - sourceTree = ""; - }; - C864093B1BA5909000D3C4E8 /* Observables */ = { - isa = PBXGroup; - children = ( - C864093C1BA5909000D3C4E8 /* Implementations */, - C864096C1BA5909000D3C4E8 /* Observable+Aggregate.swift */, - C864096D1BA5909000D3C4E8 /* Observable+Binding.swift */, - C864096E1BA5909000D3C4E8 /* Observable+Concurrency.swift */, - C864096F1BA5909000D3C4E8 /* Observable+Creation.swift */, - C86409701BA5909000D3C4E8 /* Observable+Debug.swift */, - C86409711BA5909000D3C4E8 /* Observable+Multiple.swift */, - C86409721BA5909000D3C4E8 /* Observable+Single.swift */, - C86409731BA5909000D3C4E8 /* Observable+StandardSequenceOperators.swift */, - C86409741BA5909000D3C4E8 /* Observable+Time.swift */, - ); - path = Observables; - sourceTree = ""; - }; - C864093C1BA5909000D3C4E8 /* Implementations */ = { - isa = PBXGroup; - children = ( - D2FC15B61BCBAA01007361FF /* SkipWhile.swift */, - D245D9E61BC6C60800CAB388 /* SkipUntil.swift */, - C864093D1BA5909000D3C4E8 /* Amb.swift */, - C864093E1BA5909000D3C4E8 /* AnonymousObservable.swift */, - C864093F1BA5909000D3C4E8 /* AsObservable.swift */, - C86409401BA5909000D3C4E8 /* Buffer.swift */, - C86409411BA5909000D3C4E8 /* Catch.swift */, - C86409451BA5909000D3C4E8 /* CombineLatest.swift */, - C86409421BA5909000D3C4E8 /* CombineLatest+arity.swift */, - C86409431BA5909000D3C4E8 /* CombineLatest+arity.tt */, - C86409441BA5909000D3C4E8 /* CombineLatest+CollectionType.swift */, - C86409461BA5909000D3C4E8 /* Concat.swift */, - C86409471BA5909000D3C4E8 /* ConnectableObservable.swift */, - C86409481BA5909000D3C4E8 /* Debug.swift */, - C86409491BA5909000D3C4E8 /* Deferred.swift */, - C864094A1BA5909000D3C4E8 /* DelaySubscription.swift */, - C864094B1BA5909000D3C4E8 /* DistinctUntilChanged.swift */, - C864094C1BA5909000D3C4E8 /* Do.swift */, - C864094D1BA5909000D3C4E8 /* Empty.swift */, - C864094E1BA5909000D3C4E8 /* FailWith.swift */, - C864094F1BA5909000D3C4E8 /* Filter.swift */, - C86409501BA5909000D3C4E8 /* FlatMap.swift */, - C86409511BA5909000D3C4E8 /* Generate.swift */, - C86409521BA5909000D3C4E8 /* Just.swift */, - C86409531BA5909000D3C4E8 /* Map.swift */, - C86409541BA5909000D3C4E8 /* Merge.swift */, - C86409551BA5909000D3C4E8 /* Multicast.swift */, - C86409561BA5909000D3C4E8 /* Never.swift */, - C86409571BA5909000D3C4E8 /* ObserveOn.swift */, - C86409581BA5909000D3C4E8 /* ObserveOnSerialDispatchQueue.swift */, - C86409591BA5909000D3C4E8 /* Producer.swift */, - C86409FE1BA5A87200D3C4E8 /* Range.swift */, - C864095A1BA5909000D3C4E8 /* Reduce.swift */, - C864095B1BA5909000D3C4E8 /* RefCount.swift */, - C8640A001BA5AB5A00D3C4E8 /* Repeat.swift */, - C864095C1BA5909000D3C4E8 /* Sample.swift */, - C864095D1BA5909000D3C4E8 /* Scan.swift */, - C864095E1BA5909000D3C4E8 /* Sink.swift */, - C864095F1BA5909000D3C4E8 /* Skip.swift */, - C86409601BA5909000D3C4E8 /* StartWith.swift */, - C86409611BA5909000D3C4E8 /* SubscribeOn.swift */, - C86409621BA5909000D3C4E8 /* Switch.swift */, - C86409631BA5909000D3C4E8 /* Take.swift */, - C86409641BA5909000D3C4E8 /* TakeUntil.swift */, - C86409651BA5909000D3C4E8 /* TakeWhile.swift */, - C86409661BA5909000D3C4E8 /* Throttle.swift */, - C86409671BA5909000D3C4E8 /* Timer.swift */, - C864096B1BA5909000D3C4E8 /* Zip.swift */, - C86409681BA5909000D3C4E8 /* Zip+arity.swift */, - C86409691BA5909000D3C4E8 /* Zip+arity.tt */, - C864096A1BA5909000D3C4E8 /* Zip+CollectionType.swift */, - ); - path = Implementations; - sourceTree = ""; - }; - C86409771BA5909000D3C4E8 /* Observers */ = { - isa = PBXGroup; - children = ( - C86409781BA5909000D3C4E8 /* AnonymousObserver.swift */, - C86409791BA5909000D3C4E8 /* ObserverBase.swift */, - C864097A1BA5909000D3C4E8 /* TailRecursiveSink.swift */, - ); - path = Observers; - sourceTree = ""; - }; - C864097F1BA5909000D3C4E8 /* Schedulers */ = { - isa = PBXGroup; - children = ( - C86409801BA5909000D3C4E8 /* ConcurrentDispatchQueueScheduler.swift */, - C86409811BA5909000D3C4E8 /* CurrentThreadScheduler.swift */, - C86409821BA5909000D3C4E8 /* DispatchQueueSchedulerPriority.swift */, - C86409831BA5909000D3C4E8 /* MainScheduler.swift */, - C86409841BA5909000D3C4E8 /* OperationQueueScheduler.swift */, - C86409851BA5909000D3C4E8 /* RecursiveScheduler.swift */, - C86409861BA5909000D3C4E8 /* ScheduledItem.swift */, - C86409871BA5909000D3C4E8 /* SchedulerServices+Emulation.swift */, - C86409881BA5909000D3C4E8 /* SerialDispatchQueueScheduler.swift */, - ); - path = Schedulers; - sourceTree = ""; - }; - C864098A1BA5909000D3C4E8 /* Subjects */ = { - isa = PBXGroup; - children = ( - C864098B1BA5909000D3C4E8 /* BehaviorSubject.swift */, - C864098C1BA5909000D3C4E8 /* PublishSubject.swift */, - C864098D1BA5909000D3C4E8 /* ReplaySubject.swift */, - C864098E1BA5909000D3C4E8 /* SubjectType.swift */, - C864098F1BA5909000D3C4E8 /* Variable.swift */, - ); - path = Subjects; - sourceTree = ""; - }; C86E2F2E1AE5A0CA00C31024 /* Examples */ = { isa = PBXGroup; children = ( @@ -1272,6 +956,346 @@ path = DataSources; sourceTree = ""; }; + C89464271BC6C2B00055219D /* RxSwift */ = { + isa = PBXGroup; + children = ( + C89464281BC6C2B00055219D /* Cancelable.swift */, + C89464291BC6C2B00055219D /* Concurrency */, + C894642C1BC6C2B00055219D /* ConnectableObservableType.swift */, + C894642D1BC6C2B00055219D /* DataStructures */, + C89464311BC6C2B00055219D /* Disposable.swift */, + C89464321BC6C2B00055219D /* Disposables */, + C89464401BC6C2B00055219D /* Error.swift */, + C89464411BC6C2B00055219D /* Event.swift */, + C89464421BC6C2B00055219D /* ImmediateSchedulerType.swift */, + C89464431BC6C2B00055219D /* Info.plist */, + C89464441BC6C2B00055219D /* Observable+Extensions.swift */, + C89464451BC6C2B00055219D /* Observable.swift */, + C89464461BC6C2B00055219D /* ObservableConvertibleType.swift */, + C89464471BC6C2B00055219D /* Observables */, + C89464831BC6C2B00055219D /* ObservableType.swift */, + C89464841BC6C2B00055219D /* ObserverOf.swift */, + C89464851BC6C2B00055219D /* Observers */, + C89464891BC6C2B00055219D /* ObserverType+Extensions.swift */, + C894648A1BC6C2B00055219D /* ObserverType.swift */, + C894648B1BC6C2B00055219D /* Rx.swift */, + C894648C1BC6C2B00055219D /* RxBox.swift */, + C894648D1BC6C2B00055219D /* Schedulers */, + C89464971BC6C2B00055219D /* SchedulerType.swift */, + C89464981BC6C2B00055219D /* Subjects */, + ); + name = RxSwift; + path = ../RxSwift; + sourceTree = ""; + }; + C89464291BC6C2B00055219D /* Concurrency */ = { + isa = PBXGroup; + children = ( + C894642A1BC6C2B00055219D /* AsyncLock.swift */, + C894642B1BC6C2B00055219D /* Lock.swift */, + ); + path = Concurrency; + sourceTree = ""; + }; + C894642D1BC6C2B00055219D /* DataStructures */ = { + isa = PBXGroup; + children = ( + C894642E1BC6C2B00055219D /* Bag.swift */, + C894642F1BC6C2B00055219D /* InfiniteSequence.swift */, + C89464301BC6C2B00055219D /* Queue.swift */, + ); + path = DataStructures; + sourceTree = ""; + }; + C89464321BC6C2B00055219D /* Disposables */ = { + isa = PBXGroup; + children = ( + C89464331BC6C2B00055219D /* AnonymousDisposable.swift */, + C89464341BC6C2B00055219D /* BinaryDisposable.swift */, + C89464351BC6C2B00055219D /* CompositeDisposable.swift */, + C89464361BC6C2B00055219D /* DisposeBag.swift */, + C89464371BC6C2B00055219D /* DisposeBase.swift */, + C89464381BC6C2B00055219D /* NAryDisposable.swift */, + C89464391BC6C2B00055219D /* NAryDisposable.tt */, + C894643A1BC6C2B00055219D /* NopDisposable.swift */, + C894643B1BC6C2B00055219D /* ScheduledDisposable.swift */, + C894643C1BC6C2B00055219D /* ScopedDisposable.swift */, + C894643D1BC6C2B00055219D /* SerialDisposable.swift */, + C894643E1BC6C2B00055219D /* SingleAssignmentDisposable.swift */, + C894643F1BC6C2B00055219D /* StableCompositeDisposable.swift */, + ); + path = Disposables; + sourceTree = ""; + }; + C89464471BC6C2B00055219D /* Observables */ = { + isa = PBXGroup; + children = ( + C89464481BC6C2B00055219D /* Implementations */, + C894647A1BC6C2B00055219D /* Observable+Aggregate.swift */, + C894647B1BC6C2B00055219D /* Observable+Binding.swift */, + C894647C1BC6C2B00055219D /* Observable+Concurrency.swift */, + C894647D1BC6C2B00055219D /* Observable+Creation.swift */, + C894647E1BC6C2B00055219D /* Observable+Debug.swift */, + C894647F1BC6C2B00055219D /* Observable+Multiple.swift */, + C89464801BC6C2B00055219D /* Observable+Single.swift */, + C89464811BC6C2B00055219D /* Observable+StandardSequenceOperators.swift */, + C89464821BC6C2B00055219D /* Observable+Time.swift */, + ); + path = Observables; + sourceTree = ""; + }; + C89464481BC6C2B00055219D /* Implementations */ = { + isa = PBXGroup; + children = ( + C89464491BC6C2B00055219D /* Amb.swift */, + C894644A1BC6C2B00055219D /* AnonymousObservable.swift */, + C894644B1BC6C2B00055219D /* AsObservable.swift */, + C894644C1BC6C2B00055219D /* Buffer.swift */, + C894644D1BC6C2B00055219D /* Catch.swift */, + C89464511BC6C2B00055219D /* CombineLatest.swift */, + C894644E1BC6C2B00055219D /* CombineLatest+arity.swift */, + C894644F1BC6C2B00055219D /* CombineLatest+arity.tt */, + C89464501BC6C2B00055219D /* CombineLatest+CollectionType.swift */, + C89464521BC6C2B00055219D /* Concat.swift */, + C89464531BC6C2B00055219D /* ConnectableObservable.swift */, + C89464541BC6C2B00055219D /* Debug.swift */, + C89464551BC6C2B00055219D /* Deferred.swift */, + C89464561BC6C2B00055219D /* DelaySubscription.swift */, + C89464571BC6C2B00055219D /* DistinctUntilChanged.swift */, + C89464581BC6C2B00055219D /* Do.swift */, + C89464591BC6C2B00055219D /* Empty.swift */, + C894645A1BC6C2B00055219D /* FailWith.swift */, + C894645B1BC6C2B00055219D /* Filter.swift */, + C894645C1BC6C2B00055219D /* FlatMap.swift */, + C894645D1BC6C2B00055219D /* Generate.swift */, + C894645E1BC6C2B00055219D /* Just.swift */, + C894645F1BC6C2B00055219D /* Map.swift */, + C89464601BC6C2B00055219D /* Merge.swift */, + C89464611BC6C2B00055219D /* Multicast.swift */, + C89464621BC6C2B00055219D /* Never.swift */, + C89464631BC6C2B00055219D /* ObserveOn.swift */, + C89464641BC6C2B00055219D /* ObserveOnSerialDispatchQueue.swift */, + C89464651BC6C2B00055219D /* Producer.swift */, + C89464661BC6C2B00055219D /* Range.swift */, + C89464671BC6C2B00055219D /* Reduce.swift */, + C89464681BC6C2B00055219D /* RefCount.swift */, + C89464691BC6C2B00055219D /* Repeat.swift */, + C894646A1BC6C2B00055219D /* Sample.swift */, + C894646B1BC6C2B00055219D /* Scan.swift */, + C89CDB611BCC45DC002063D9 /* ShareReplay1.swift */, + C894646C1BC6C2B00055219D /* Sink.swift */, + C894646D1BC6C2B00055219D /* Skip.swift */, + C89CDB621BCC45DC002063D9 /* SkipUntil.swift */, + C894646E1BC6C2B00055219D /* StartWith.swift */, + C894646F1BC6C2B00055219D /* SubscribeOn.swift */, + C89464701BC6C2B00055219D /* Switch.swift */, + C89464711BC6C2B00055219D /* Take.swift */, + C89464721BC6C2B00055219D /* TakeUntil.swift */, + C89464731BC6C2B00055219D /* TakeWhile.swift */, + C89464741BC6C2B00055219D /* Throttle.swift */, + C89464751BC6C2B00055219D /* Timer.swift */, + C89464791BC6C2B00055219D /* Zip.swift */, + C89464761BC6C2B00055219D /* Zip+arity.swift */, + C89464771BC6C2B00055219D /* Zip+arity.tt */, + C89464781BC6C2B00055219D /* Zip+CollectionType.swift */, + ); + path = Implementations; + sourceTree = ""; + }; + C89464851BC6C2B00055219D /* Observers */ = { + isa = PBXGroup; + children = ( + C89464861BC6C2B00055219D /* AnonymousObserver.swift */, + C89464871BC6C2B00055219D /* ObserverBase.swift */, + C89464881BC6C2B00055219D /* TailRecursiveSink.swift */, + ); + path = Observers; + sourceTree = ""; + }; + C894648D1BC6C2B00055219D /* Schedulers */ = { + isa = PBXGroup; + children = ( + C894648E1BC6C2B00055219D /* ConcurrentDispatchQueueScheduler.swift */, + C894648F1BC6C2B00055219D /* CurrentThreadScheduler.swift */, + C89464901BC6C2B00055219D /* DispatchQueueSchedulerPriority.swift */, + C89464911BC6C2B00055219D /* MainScheduler.swift */, + C89464921BC6C2B00055219D /* OperationQueueScheduler.swift */, + C89464931BC6C2B00055219D /* RecursiveScheduler.swift */, + C89464941BC6C2B00055219D /* ScheduledItem.swift */, + C89464951BC6C2B00055219D /* SchedulerServices+Emulation.swift */, + C89464961BC6C2B00055219D /* SerialDispatchQueueScheduler.swift */, + ); + path = Schedulers; + sourceTree = ""; + }; + C89464981BC6C2B00055219D /* Subjects */ = { + isa = PBXGroup; + children = ( + C89464991BC6C2B00055219D /* BehaviorSubject.swift */, + C894649A1BC6C2B00055219D /* PublishSubject.swift */, + C894649B1BC6C2B00055219D /* ReplaySubject.swift */, + C894649C1BC6C2B00055219D /* SubjectType.swift */, + C894649D1BC6C2B00055219D /* Variable.swift */, + ); + path = Subjects; + sourceTree = ""; + }; + C894650C1BC6C2BC0055219D /* RxCocoa */ = { + isa = PBXGroup; + children = ( + C894650D1BC6C2BC0055219D /* Common */, + C89465331BC6C2BC0055219D /* Info.plist */, + C89465341BC6C2BC0055219D /* iOS */, + C89465601BC6C2BC0055219D /* RxCocoa.h */, + ); + name = RxCocoa; + path = ../RxCocoa; + sourceTree = ""; + }; + C894650D1BC6C2BC0055219D /* Common */ = { + isa = PBXGroup; + children = ( + C894650E1BC6C2BC0055219D /* _RX.h */, + C894650F1BC6C2BC0055219D /* _RX.m */, + C89465101BC6C2BC0055219D /* _RXDelegateProxy.h */, + C89465111BC6C2BC0055219D /* _RXDelegateProxy.m */, + C89465121BC6C2BC0055219D /* _RXKVOObserver.h */, + C89465131BC6C2BC0055219D /* _RXKVOObserver.m */, + C89465141BC6C2BC0055219D /* _RXSwizzling.h */, + C89465151BC6C2BC0055219D /* _RXSwizzling.m */, + C89465161BC6C2BC0055219D /* CLLocationManager+Rx.swift */, + C89465171BC6C2BC0055219D /* CocoaUnits */, + C89465201BC6C2BC0055219D /* DelegateProxy.swift */, + C89465211BC6C2BC0055219D /* DelegateProxyType.swift */, + C89465221BC6C2BC0055219D /* Logging.swift */, + C89465231BC6C2BC0055219D /* Observable+Bind.swift */, + C89465241BC6C2BC0055219D /* Observables */, + C894652F1BC6C2BC0055219D /* Proxies */, + C89465311BC6C2BC0055219D /* RxCocoa.swift */, + C89465321BC6C2BC0055219D /* RxTarget.swift */, + ); + path = Common; + sourceTree = ""; + }; + C89465171BC6C2BC0055219D /* CocoaUnits */ = { + isa = PBXGroup; + children = ( + C89465181BC6C2BC0055219D /* ControlEvent+Driver.swift */, + C89465191BC6C2BC0055219D /* ControlEvent.swift */, + C894651A1BC6C2BC0055219D /* ControlProperty+Driver.swift */, + C894651B1BC6C2BC0055219D /* ControlProperty.swift */, + C894651C1BC6C2BC0055219D /* Driver+Operators.swift */, + C894651D1BC6C2BC0055219D /* Driver+Subscription.swift */, + C894651E1BC6C2BC0055219D /* Driver.swift */, + C894651F1BC6C2BC0055219D /* ObservableConvertibleType+Driver.swift */, + ); + path = CocoaUnits; + sourceTree = ""; + }; + C89465241BC6C2BC0055219D /* Observables */ = { + isa = PBXGroup; + children = ( + C89465251BC6C2BC0055219D /* Implementations */, + C894652B1BC6C2BC0055219D /* NSNotificationCenter+Rx.swift */, + C894652C1BC6C2BC0055219D /* NSObject+Rx+CoreGraphics.swift */, + C894652D1BC6C2BC0055219D /* NSObject+Rx.swift */, + C894652E1BC6C2BC0055219D /* NSURLSession+Rx.swift */, + ); + path = Observables; + sourceTree = ""; + }; + C89465251BC6C2BC0055219D /* Implementations */ = { + isa = PBXGroup; + children = ( + C89465261BC6C2BC0055219D /* ControlTarget.swift */, + C89465271BC6C2BC0055219D /* Deallocating.swift */, + C89465281BC6C2BC0055219D /* DeinitAction.swift */, + C89465291BC6C2BC0055219D /* KVOObservable.swift */, + C894652A1BC6C2BC0055219D /* KVOObserver.swift */, + ); + path = Implementations; + sourceTree = ""; + }; + C894652F1BC6C2BC0055219D /* Proxies */ = { + isa = PBXGroup; + children = ( + C89465301BC6C2BC0055219D /* RxCLLocationManagerDelegateProxy.swift */, + ); + path = Proxies; + sourceTree = ""; + }; + C89465341BC6C2BC0055219D /* iOS */ = { + isa = PBXGroup; + children = ( + C89465351BC6C2BC0055219D /* DataSources */, + C89465381BC6C2BC0055219D /* Events */, + C894653A1BC6C2BC0055219D /* Protocols */, + C894653D1BC6C2BC0055219D /* Proxies */, + C89465471BC6C2BC0055219D /* UIActionSheet+Rx.swift */, + C89465481BC6C2BC0055219D /* UIAlertView+Rx.swift */, + C89465491BC6C2BC0055219D /* UIBarButtonItem+Rx.swift */, + C894654A1BC6C2BC0055219D /* UIButton+Rx.swift */, + C894654B1BC6C2BC0055219D /* UICollectionView+Rx.swift */, + C894654C1BC6C2BC0055219D /* UIControl+Rx.swift */, + C894654D1BC6C2BC0055219D /* UIDatePicker+Rx.swift */, + C894654E1BC6C2BC0055219D /* UIGestureRecognizer+Rx.swift */, + C894654F1BC6C2BC0055219D /* UIImageView+Rx.swift */, + C89465501BC6C2BC0055219D /* UILabel+Rx.swift */, + C89465511BC6C2BC0055219D /* UIScrollView+Rx.swift */, + C89465521BC6C2BC0055219D /* UISearchBar+Rx.swift */, + C89465531BC6C2BC0055219D /* UISegmentedControl+Rx.swift */, + C89465541BC6C2BC0055219D /* UISlider+Rx.swift */, + C89465551BC6C2BC0055219D /* UIStepper+Rx.swift */, + C89465561BC6C2BC0055219D /* UISwitch+Rx.swift */, + C89465571BC6C2BC0055219D /* UITableView+Rx.swift */, + C89465581BC6C2BC0055219D /* UITextField+Rx.swift */, + C89465591BC6C2BC0055219D /* UITextView+Rx.swift */, + ); + path = iOS; + sourceTree = ""; + }; + C89465351BC6C2BC0055219D /* DataSources */ = { + isa = PBXGroup; + children = ( + C89465361BC6C2BC0055219D /* RxCollectionViewReactiveArrayDataSource.swift */, + C89465371BC6C2BC0055219D /* RxTableViewReactiveArrayDataSource.swift */, + ); + path = DataSources; + sourceTree = ""; + }; + C89465381BC6C2BC0055219D /* Events */ = { + isa = PBXGroup; + children = ( + C89465391BC6C2BC0055219D /* ItemEvents.swift */, + ); + path = Events; + sourceTree = ""; + }; + C894653A1BC6C2BC0055219D /* Protocols */ = { + isa = PBXGroup; + children = ( + C894653B1BC6C2BC0055219D /* RxCollectionViewDataSourceType.swift */, + C894653C1BC6C2BC0055219D /* RxTableViewDataSourceType.swift */, + ); + path = Protocols; + sourceTree = ""; + }; + C894653D1BC6C2BC0055219D /* Proxies */ = { + isa = PBXGroup; + children = ( + C894653E1BC6C2BC0055219D /* RxActionSheetDelegateProxy.swift */, + C894653F1BC6C2BC0055219D /* RxAlertViewDelegateProxy.swift */, + C89465401BC6C2BC0055219D /* RxCollectionViewDataSourceProxy.swift */, + C89465411BC6C2BC0055219D /* RxCollectionViewDelegateProxy.swift */, + C89465421BC6C2BC0055219D /* RxScrollViewDelegateProxy.swift */, + C89465431BC6C2BC0055219D /* RxSearchBarDelegateProxy.swift */, + C89465441BC6C2BC0055219D /* RxTableViewDataSourceProxy.swift */, + C89465451BC6C2BC0055219D /* RxTableViewDelegateProxy.swift */, + C89465461BC6C2BC0055219D /* RxTextViewDelegateProxy.swift */, + ); + path = Proxies; + sourceTree = ""; + }; C8DF92C71B0B2F84009BCF9A /* iOS */ = { isa = PBXGroup; children = ( @@ -1500,16 +1524,16 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - C86409A81BA5909000D3C4E8 /* Info.plist in Resources */, C8297E5D1B6CF905000589EA /* WikipediaSearchCell.xib in Resources */, - C86409B11BA5909000D3C4E8 /* CombineLatest+arity.tt in Resources */, C8297E5E1B6CF905000589EA /* LaunchScreen.xib in Resources */, - C864099E1BA5909000D3C4E8 /* NAryDisposable.tt in Resources */, + C89464C01BC6C2B00055219D /* CombineLatest+arity.tt in Resources */, C8297E5F1B6CF905000589EA /* WikipediaImageCell.xib in Resources */, + C89464E81BC6C2B00055219D /* Zip+arity.tt in Resources */, + C89464AC1BC6C2B00055219D /* NAryDisposable.tt in Resources */, C8297E601B6CF905000589EA /* Images.xcassets in Resources */, C8297E611B6CF905000589EA /* Main.storyboard in Resources */, - C84B3A441BA4345A001B7D88 /* Info.plist in Resources */, - C86409D71BA5909000D3C4E8 /* Zip+arity.tt in Resources */, + C894657E1BC6C2BC0055219D /* Info.plist in Resources */, + C89464B61BC6C2B00055219D /* Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1541,211 +1565,219 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C894656D1BC6C2BC0055219D /* ObservableConvertibleType+Driver.swift in Sources */, + C89465971BC6C2BC0055219D /* UIScrollView+Rx.swift in Sources */, C8297E2F1B6CF905000589EA /* RxTableViewSectionedAnimatedDataSource.swift in Sources */, + C89464FF1BC6C2B00055219D /* DispatchQueueSchedulerPriority.swift in Sources */, + C89464D41BC6C2B00055219D /* ObserveOn.swift in Sources */, + C894659B1BC6C2BC0055219D /* UIStepper+Rx.swift in Sources */, C8A7501F1B94E77C00D8D046 /* RxDataSourceStarterKit.swift in Sources */, - C84B3A451BA4345A001B7D88 /* RxCollectionViewReactiveArrayDataSource.swift in Sources */, - C84B3A361BA4345A001B7D88 /* Logging.swift in Sources */, + C89464F61BC6C2B00055219D /* AnonymousObserver.swift in Sources */, + C89464C81BC6C2B00055219D /* DistinctUntilChanged.swift in Sources */, + C89464CF1BC6C2B00055219D /* Just.swift in Sources */, C8297E301B6CF905000589EA /* RandomUserAPI.swift in Sources */, - C86409CC1BA5909000D3C4E8 /* Sink.swift in Sources */, + C89465921BC6C2BC0055219D /* UIControl+Rx.swift in Sources */, + C894650B1BC6C2B00055219D /* Variable.swift in Sources */, C8297E311B6CF905000589EA /* SearchResultViewModel.swift in Sources */, + C89464D91BC6C2B00055219D /* RefCount.swift in Sources */, + C89465731BC6C2BC0055219D /* Deallocating.swift in Sources */, + C89464A51BC6C2B00055219D /* Disposable.swift in Sources */, + C89464F91BC6C2B00055219D /* ObserverType+Extensions.swift in Sources */, + C89464DC1BC6C2B00055219D /* Scan.swift in Sources */, + C89464B21BC6C2B00055219D /* StableCompositeDisposable.swift in Sources */, + C89464AE1BC6C2B00055219D /* ScheduledDisposable.swift in Sources */, + C89464B51BC6C2B00055219D /* ImmediateSchedulerType.swift in Sources */, C8297E321B6CF905000589EA /* HtmlParsing.swift in Sources */, - C86409A71BA5909000D3C4E8 /* ImmediateSchedulerType.swift in Sources */, - C84B3A4C1BA4345A001B7D88 /* RxCollectionViewDataSourceProxy.swift in Sources */, - C86409F61BA5909000D3C4E8 /* BehaviorSubject.swift in Sources */, C8297E331B6CF905000589EA /* NumberCell.swift in Sources */, - C84B3A321BA4345A001B7D88 /* ControlEvent.swift in Sources */, - C86409A31BA5909000D3C4E8 /* SingleAssignmentDisposable.swift in Sources */, - C86409D41BA5909000D3C4E8 /* Throttle.swift in Sources */, - C86409951BA5909000D3C4E8 /* InfiniteSequence.swift in Sources */, - C86409A61BA5909000D3C4E8 /* Event.swift in Sources */, - C86409961BA5909000D3C4E8 /* Queue.swift in Sources */, - C84B3A501BA4345A001B7D88 /* RxTableViewDataSourceProxy.swift in Sources */, + C894658D1BC6C2BC0055219D /* UIActionSheet+Rx.swift in Sources */, + C89464EA1BC6C2B00055219D /* Zip.swift in Sources */, + C89464E51BC6C2B00055219D /* Throttle.swift in Sources */, + C89465831BC6C2BC0055219D /* RxTableViewDataSourceType.swift in Sources */, + C89464ED1BC6C2B00055219D /* Observable+Concurrency.swift in Sources */, + C89464DA1BC6C2B00055219D /* Repeat.swift in Sources */, + C89465651BC6C2BC0055219D /* CLLocationManager+Rx.swift in Sources */, 07A5C3DC1B70B703001EFE5C /* CalculatorViewController.swift in Sources */, - C86409BC1BA5909000D3C4E8 /* FailWith.swift in Sources */, - C86409B91BA5909000D3C4E8 /* DistinctUntilChanged.swift in Sources */, - C86409EE1BA5909000D3C4E8 /* DispatchQueueSchedulerPriority.swift in Sources */, - C86409F31BA5909000D3C4E8 /* SchedulerServices+Emulation.swift in Sources */, - C86409991BA5909000D3C4E8 /* BinaryDisposable.swift in Sources */, - C86409BF1BA5909000D3C4E8 /* Generate.swift in Sources */, - C86409B71BA5909000D3C4E8 /* Deferred.swift in Sources */, - C86409BE1BA5909000D3C4E8 /* FlatMap.swift in Sources */, - C86409E21BA5909000D3C4E8 /* Observable+Time.swift in Sources */, - C86409E91BA5909000D3C4E8 /* ObserverType.swift in Sources */, - C84B3A601BA4345A001B7D88 /* UISlider+Rx.swift in Sources */, - C86409EC1BA5909000D3C4E8 /* ConcurrentDispatchQueueScheduler.swift in Sources */, - C84B3A561BA4345A001B7D88 /* UIButton+Rx.swift in Sources */, - C86409EA1BA5909000D3C4E8 /* Rx.swift in Sources */, - C84B3A5D1BA4345A001B7D88 /* UIScrollView+Rx.swift in Sources */, - C86409E81BA5909000D3C4E8 /* ObserverType+Extensions.swift in Sources */, - C84B3A5C1BA4345A001B7D88 /* UILabel+Rx.swift in Sources */, + C89465961BC6C2BC0055219D /* UILabel+Rx.swift in Sources */, + C894659C1BC6C2BC0055219D /* UISwitch+Rx.swift in Sources */, + C89464F81BC6C2B00055219D /* TailRecursiveSink.swift in Sources */, + C89464BF1BC6C2B00055219D /* CombineLatest+arity.swift in Sources */, + C89465751BC6C2BC0055219D /* KVOObservable.swift in Sources */, + C89464CB1BC6C2B00055219D /* FailWith.swift in Sources */, C8297E341B6CF905000589EA /* UIImageView+Extensions.swift in Sources */, - C84B3A481BA4345A001B7D88 /* RxCollectionViewDataSourceType.swift in Sources */, + C89465051BC6C2B00055219D /* SerialDispatchQueueScheduler.swift in Sources */, + C89464B81BC6C2B00055219D /* Observable.swift in Sources */, + C89465001BC6C2B00055219D /* MainScheduler.swift in Sources */, C8297E351B6CF905000589EA /* NumberSectionView.swift in Sources */, - C84B3A611BA4345A001B7D88 /* UISwitch+Rx.swift in Sources */, - C86409CE1BA5909000D3C4E8 /* StartWith.swift in Sources */, - C86409D91BA5909000D3C4E8 /* Zip.swift in Sources */, - C84B3A4D1BA4345A001B7D88 /* RxCollectionViewDelegateProxy.swift in Sources */, - C86409EB1BA5909000D3C4E8 /* RxBox.swift in Sources */, + C894659E1BC6C2BC0055219D /* UITextField+Rx.swift in Sources */, + C89464D11BC6C2B00055219D /* Merge.swift in Sources */, + C89464A71BC6C2B00055219D /* BinaryDisposable.swift in Sources */, + C89465741BC6C2BC0055219D /* DeinitAction.swift in Sources */, C8297E361B6CF905000589EA /* RootViewController.swift in Sources */, - C84B3A511BA4345A001B7D88 /* RxTableViewDelegateProxy.swift in Sources */, + C894656C1BC6C2BC0055219D /* Driver.swift in Sources */, + C89464BB1BC6C2B00055219D /* AnonymousObservable.swift in Sources */, + C89465991BC6C2BC0055219D /* UISegmentedControl+Rx.swift in Sources */, C8297E371B6CF905000589EA /* RxCollectionViewSectionedDataSource.swift in Sources */, - C84B3A591BA4345A001B7D88 /* UIDatePicker+Rx.swift in Sources */, - C86409E11BA5909000D3C4E8 /* Observable+StandardSequenceOperators.swift in Sources */, - C86409B61BA5909000D3C4E8 /* Debug.swift in Sources */, + C89464CD1BC6C2B00055219D /* FlatMap.swift in Sources */, C8297E381B6CF905000589EA /* Changeset.swift in Sources */, C8297E391B6CF905000589EA /* CollectionViewImageCell.swift in Sources */, - C84B3A491BA4345A001B7D88 /* RxTableViewDataSourceType.swift in Sources */, - C864099D1BA5909000D3C4E8 /* NAryDisposable.swift in Sources */, - C86409C51BA5909000D3C4E8 /* ObserveOn.swift in Sources */, - C84B3A331BA4345A001B7D88 /* ControlProperty.swift in Sources */, - D245D9F41BC6CA0900CAB388 /* SkipUntil.swift in Sources */, - C86409FA1BA5909000D3C4E8 /* Variable.swift in Sources */, + C894649E1BC6C2B00055219D /* Cancelable.swift in Sources */, + C89464E01BC6C2B00055219D /* SubscribeOn.swift in Sources */, + C89465031BC6C2B00055219D /* ScheduledItem.swift in Sources */, + C89465801BC6C2BC0055219D /* RxTableViewReactiveArrayDataSource.swift in Sources */, + C89464AF1BC6C2B00055219D /* ScopedDisposable.swift in Sources */, + C89464FA1BC6C2B00055219D /* ObserverType.swift in Sources */, + C89464F01BC6C2B00055219D /* Observable+Multiple.swift in Sources */, + C89464DF1BC6C2B00055219D /* StartWith.swift in Sources */, + C89465881BC6C2BC0055219D /* RxScrollViewDelegateProxy.swift in Sources */, + C894656B1BC6C2BC0055219D /* Driver+Subscription.swift in Sources */, + C89464B71BC6C2B00055219D /* Observable+Extensions.swift in Sources */, + C89464A01BC6C2B00055219D /* Lock.swift in Sources */, + C89464C91BC6C2B00055219D /* Do.swift in Sources */, + C89464A41BC6C2B00055219D /* Queue.swift in Sources */, + C89464B91BC6C2B00055219D /* ObservableConvertibleType.swift in Sources */, + C894657C1BC6C2BC0055219D /* RxCocoa.swift in Sources */, + C894658F1BC6C2BC0055219D /* UIBarButtonItem+Rx.swift in Sources */, + C89464D61BC6C2B00055219D /* Producer.swift in Sources */, + C894658A1BC6C2BC0055219D /* RxTableViewDataSourceProxy.swift in Sources */, + C894656F1BC6C2BC0055219D /* DelegateProxyType.swift in Sources */, + C89465721BC6C2BC0055219D /* ControlTarget.swift in Sources */, + C89464EC1BC6C2B00055219D /* Observable+Binding.swift in Sources */, C8297E3A1B6CF905000589EA /* WikipediaSearchViewController.swift in Sources */, - C84B3A5A1BA4345A001B7D88 /* UIGestureRecognizer+Rx.swift in Sources */, - D2FC15C41BCBAA13007361FF /* SkipWhile.swift in Sources */, - C86409AC1BA5909000D3C4E8 /* AnonymousObservable.swift in Sources */, - C84B3A401BA4345A001B7D88 /* NSURLSession+Rx.swift in Sources */, + C89464F21BC6C2B00055219D /* Observable+StandardSequenceOperators.swift in Sources */, + C89464CC1BC6C2B00055219D /* Filter.swift in Sources */, + C89465021BC6C2B00055219D /* RecursiveScheduler.swift in Sources */, + C89464C11BC6C2B00055219D /* CombineLatest+CollectionType.swift in Sources */, + C89465671BC6C2BC0055219D /* ControlEvent.swift in Sources */, C8297E3B1B6CF905000589EA /* String+extensions.swift in Sources */, - C86409DF1BA5909000D3C4E8 /* Observable+Multiple.swift in Sources */, - C86409D11BA5909000D3C4E8 /* Take.swift in Sources */, - C86409BD1BA5909000D3C4E8 /* Filter.swift in Sources */, - C86409F11BA5909000D3C4E8 /* RecursiveScheduler.swift in Sources */, - C84B3A391BA4345A001B7D88 /* Deallocating.swift in Sources */, - C86409C41BA5909000D3C4E8 /* Never.swift in Sources */, + C89464A61BC6C2B00055219D /* AnonymousDisposable.swift in Sources */, + C894657D1BC6C2BC0055219D /* RxTarget.swift in Sources */, + C89464EE1BC6C2B00055219D /* Observable+Creation.swift in Sources */, + C894659A1BC6C2BC0055219D /* UISlider+Rx.swift in Sources */, + C89465891BC6C2BC0055219D /* RxSearchBarDelegateProxy.swift in Sources */, + C89464C21BC6C2B00055219D /* CombineLatest.swift in Sources */, + C89464E71BC6C2B00055219D /* Zip+arity.swift in Sources */, + C89464DE1BC6C2B00055219D /* Skip.swift in Sources */, + C89464DB1BC6C2B00055219D /* Sample.swift in Sources */, + C89465011BC6C2B00055219D /* OperationQueueScheduler.swift in Sources */, + C89465791BC6C2BC0055219D /* NSObject+Rx.swift in Sources */, + C89464F31BC6C2B00055219D /* Observable+Time.swift in Sources */, + C89464F11BC6C2B00055219D /* Observable+Single.swift in Sources */, + C89464BA1BC6C2B00055219D /* Amb.swift in Sources */, C8297E3C1B6CF905000589EA /* SectionModel.swift in Sources */, - C84B3A2E1BA4345A001B7D88 /* _RXDelegateProxy.m in Sources */, - C86409C91BA5909000D3C4E8 /* RefCount.swift in Sources */, + C894650A1BC6C2B00055219D /* SubjectType.swift in Sources */, + C89464B11BC6C2B00055219D /* SingleAssignmentDisposable.swift in Sources */, + C89464AA1BC6C2B00055219D /* DisposeBase.swift in Sources */, + C89465871BC6C2BC0055219D /* RxCollectionViewDelegateProxy.swift in Sources */, C8297E3D1B6CF905000589EA /* SearchViewModel.swift in Sources */, - C86409B51BA5909000D3C4E8 /* ConnectableObservable.swift in Sources */, - C84B3A3A1BA4345A001B7D88 /* DeinitAction.swift in Sources */, - C84B3A621BA4345A001B7D88 /* UITableView+Rx.swift in Sources */, - C86409FF1BA5A87200D3C4E8 /* Range.swift in Sources */, - C86409E61BA5909000D3C4E8 /* ObserverBase.swift in Sources */, - C84B3A631BA4345A001B7D88 /* UITextField+Rx.swift in Sources */, - C84B3A571BA4345A001B7D88 /* UICollectionView+Rx.swift in Sources */, - C84B3A431BA4345A001B7D88 /* RxTarget.swift in Sources */, - C84B3A3E1BA4345A001B7D88 /* NSObject+Rx+CoreGraphics.swift in Sources */, - C86409F01BA5909000D3C4E8 /* OperationQueueScheduler.swift in Sources */, + C89464E61BC6C2B00055219D /* Timer.swift in Sources */, C8297E3E1B6CF905000589EA /* DetailViewController.swift in Sources */, - C86409DB1BA5909000D3C4E8 /* Observable+Binding.swift in Sources */, C8297E3F1B6CF905000589EA /* SectionModelType.swift in Sources */, - C86409C61BA5909000D3C4E8 /* ObserveOnSerialDispatchQueue.swift in Sources */, - C86409DA1BA5909000D3C4E8 /* Observable+Aggregate.swift in Sources */, - C86409931BA5909000D3C4E8 /* ConnectableObservableType.swift in Sources */, - C84B3A371BA4345A001B7D88 /* Observable+CocoaExtensions.swift in Sources */, - C84B3A351BA4345A001B7D88 /* DelegateProxyType.swift in Sources */, - C86409B01BA5909000D3C4E8 /* CombineLatest+arity.swift in Sources */, C8297E401B6CF905000589EA /* ImageService.swift in Sources */, - C86409E51BA5909000D3C4E8 /* AnonymousObserver.swift in Sources */, - C86409DD1BA5909000D3C4E8 /* Observable+Creation.swift in Sources */, - C84B3A4B1BA4345A001B7D88 /* RxAlertViewDelegateProxy.swift in Sources */, - C84B3A411BA4345A001B7D88 /* RxCLLocationManagerDelegateProxy.swift in Sources */, - C86409A01BA5909000D3C4E8 /* ScheduledDisposable.swift in Sources */, - C86409CF1BA5909000D3C4E8 /* SubscribeOn.swift in Sources */, - C86409B21BA5909000D3C4E8 /* CombineLatest+CollectionType.swift in Sources */, - C86409E01BA5909000D3C4E8 /* Observable+Single.swift in Sources */, + C89464AD1BC6C2B00055219D /* NopDisposable.swift in Sources */, + C89465771BC6C2BC0055219D /* NSNotificationCenter+Rx.swift in Sources */, + C89465091BC6C2B00055219D /* ReplaySubject.swift in Sources */, C8297E411B6CF905000589EA /* RxCollectionViewSectionedReloadDataSource.swift in Sources */, - C84B3A5F1BA4345A001B7D88 /* UISegmentedControl+Rx.swift in Sources */, - C86409D61BA5909000D3C4E8 /* Zip+arity.swift in Sources */, - C86409B31BA5909000D3C4E8 /* CombineLatest.swift in Sources */, - C86409C81BA5909000D3C4E8 /* Reduce.swift in Sources */, - C86409C01BA5909000D3C4E8 /* Just.swift in Sources */, - C84B3A341BA4345A001B7D88 /* DelegateProxy.swift in Sources */, + C89464A81BC6C2B00055219D /* CompositeDisposable.swift in Sources */, + C89464D21BC6C2B00055219D /* Multicast.swift in Sources */, + C89465821BC6C2BC0055219D /* RxCollectionViewDataSourceType.swift in Sources */, C8297E421B6CF905000589EA /* WikipediaSearchResult.swift in Sources */, + C89465701BC6C2BC0055219D /* Logging.swift in Sources */, + C89464A31BC6C2B00055219D /* InfiniteSequence.swift in Sources */, + C89465041BC6C2B00055219D /* SchedulerServices+Emulation.swift in Sources */, + C89465611BC6C2BC0055219D /* _RX.m in Sources */, + C89465841BC6C2BC0055219D /* RxActionSheetDelegateProxy.swift in Sources */, + C89464F51BC6C2B00055219D /* ObserverOf.swift in Sources */, + C89464D71BC6C2B00055219D /* Range.swift in Sources */, C8297E431B6CF905000589EA /* GitHubAPI.swift in Sources */, - C86409AE1BA5909000D3C4E8 /* Buffer.swift in Sources */, + C89464EB1BC6C2B00055219D /* Observable+Aggregate.swift in Sources */, C8297E441B6CF905000589EA /* PseudoRandomGenerator.swift in Sources */, C8297E451B6CF905000589EA /* SectionedViewType.swift in Sources */, - C84B3A4A1BA4345A001B7D88 /* RxActionSheetDelegateProxy.swift in Sources */, - C864099B1BA5909000D3C4E8 /* DisposeBag.swift in Sources */, - C86409D01BA5909000D3C4E8 /* Switch.swift in Sources */, - C86409E71BA5909000D3C4E8 /* TailRecursiveSink.swift in Sources */, + C89464D51BC6C2B00055219D /* ObserveOnSerialDispatchQueue.swift in Sources */, + C894658E1BC6C2BC0055219D /* UIAlertView+Rx.swift in Sources */, C8297E461B6CF905000589EA /* Example.swift in Sources */, - C84B3A301BA4345A001B7D88 /* _RXSwizzling.m in Sources */, - C86409C31BA5909000D3C4E8 /* Multicast.swift in Sources */, - C84B3A311BA4345A001B7D88 /* CLLocationManager+Rx.swift in Sources */, + C89465081BC6C2B00055219D /* PublishSubject.swift in Sources */, + C89464FC1BC6C2B00055219D /* RxBox.swift in Sources */, + C89465811BC6C2BC0055219D /* ItemEvents.swift in Sources */, + C89465861BC6C2BC0055219D /* RxCollectionViewDataSourceProxy.swift in Sources */, + C89465981BC6C2BC0055219D /* UISearchBar+Rx.swift in Sources */, + C89464E21BC6C2B00055219D /* Take.swift in Sources */, + C89465901BC6C2BC0055219D /* UIButton+Rx.swift in Sources */, + C89464BC1BC6C2B00055219D /* AsObservable.swift in Sources */, + C89464DD1BC6C2B00055219D /* Sink.swift in Sources */, + C89464FE1BC6C2B00055219D /* CurrentThreadScheduler.swift in Sources */, + C89464BE1BC6C2B00055219D /* Catch.swift in Sources */, + C89CDB721BCC45EE002063D9 /* SkipUntil.swift in Sources */, C8297E471B6CF905000589EA /* ViewController.swift in Sources */, - C84B3A5E1BA4345A001B7D88 /* UISearchBar+Rx.swift in Sources */, - C86409E41BA5909000D3C4E8 /* ObserverOf.swift in Sources */, - C86409EF1BA5909000D3C4E8 /* MainScheduler.swift in Sources */, + C89464E41BC6C2B00055219D /* TakeWhile.swift in Sources */, + C89465681BC6C2BC0055219D /* ControlProperty+Driver.swift in Sources */, + C89464F71BC6C2B00055219D /* ObserverBase.swift in Sources */, + C89465951BC6C2BC0055219D /* UIImageView+Rx.swift in Sources */, + C89464E31BC6C2B00055219D /* TakeUntil.swift in Sources */, + C89464FB1BC6C2B00055219D /* Rx.swift in Sources */, + C89464FD1BC6C2B00055219D /* ConcurrentDispatchQueueScheduler.swift in Sources */, + C89464C71BC6C2B00055219D /* DelaySubscription.swift in Sources */, C8297E481B6CF905000589EA /* Differentiator.swift in Sources */, - C84B3A381BA4345A001B7D88 /* ControlTarget.swift in Sources */, - C84B3A461BA4345A001B7D88 /* RxTableViewReactiveArrayDataSource.swift in Sources */, - C86409A91BA5909000D3C4E8 /* Observable+Extensions.swift in Sources */, - C84B3A541BA4345A001B7D88 /* UIAlertView+Rx.swift in Sources */, - C84B3A3D1BA4345A001B7D88 /* NSNotificationCenter+Rx.swift in Sources */, - C84B3A421BA4345A001B7D88 /* RxCocoa.swift in Sources */, - C86409F21BA5909000D3C4E8 /* ScheduledItem.swift in Sources */, - C84B3A521BA4345A001B7D88 /* RxTextViewDelegateProxy.swift in Sources */, C8297E491B6CF905000589EA /* WikipediaSearchCell.swift in Sources */, - C86409911BA5909000D3C4E8 /* AsyncLock.swift in Sources */, - C86409C71BA5909000D3C4E8 /* Producer.swift in Sources */, - C86409AB1BA5909000D3C4E8 /* Amb.swift in Sources */, - C84B3A3F1BA4345A001B7D88 /* NSObject+Rx.swift in Sources */, - C864099C1BA5909000D3C4E8 /* DisposeBase.swift in Sources */, + C89464D81BC6C2B00055219D /* Reduce.swift in Sources */, C8297E4A1B6CF905000589EA /* GitHubSignupViewController.swift in Sources */, - C86409F51BA5909000D3C4E8 /* SchedulerType.swift in Sources */, - C86409B81BA5909000D3C4E8 /* DelaySubscription.swift in Sources */, - C84B3A641BA4345A001B7D88 /* UITextView+Rx.swift in Sources */, - C86409AF1BA5909000D3C4E8 /* Catch.swift in Sources */, - C86409D81BA5909000D3C4E8 /* Zip+CollectionType.swift in Sources */, - C84B3A471BA4345A001B7D88 /* ItemEvents.swift in Sources */, + C89465941BC6C2BC0055219D /* UIGestureRecognizer+Rx.swift in Sources */, + C89464A91BC6C2B00055219D /* DisposeBag.swift in Sources */, C8297E4C1B6CF905000589EA /* APIWrappersViewController.swift in Sources */, + C89465621BC6C2BC0055219D /* _RXDelegateProxy.m in Sources */, + C89464D31BC6C2B00055219D /* Never.swift in Sources */, C8297E4D1B6CF905000589EA /* RxTableViewSectionedReloadDataSource.swift in Sources */, - C86409A51BA5909000D3C4E8 /* Error.swift in Sources */, - C86409971BA5909000D3C4E8 /* Disposable.swift in Sources */, + C89465931BC6C2BC0055219D /* UIDatePicker+Rx.swift in Sources */, C8297E4E1B6CF905000589EA /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */, - C84B3A3B1BA4345A001B7D88 /* KVOObservable.swift in Sources */, - C86409D51BA5909000D3C4E8 /* Timer.swift in Sources */, - C86409D31BA5909000D3C4E8 /* TakeWhile.swift in Sources */, + C89CDB711BCC45E5002063D9 /* ShareReplay1.swift in Sources */, + C89464BD1BC6C2B00055219D /* Buffer.swift in Sources */, + C89464B31BC6C2B00055219D /* Error.swift in Sources */, + C89464C51BC6C2B00055219D /* Debug.swift in Sources */, + C89464AB1BC6C2B00055219D /* NAryDisposable.swift in Sources */, + C89465631BC6C2BC0055219D /* _RXKVOObserver.m in Sources */, + C894656E1BC6C2BC0055219D /* DelegateProxy.swift in Sources */, + C89464EF1BC6C2B00055219D /* Observable+Debug.swift in Sources */, + C89464E91BC6C2B00055219D /* Zip+CollectionType.swift in Sources */, + C89465761BC6C2BC0055219D /* KVOObserver.swift in Sources */, C8297E4F1B6CF905000589EA /* Wireframe.swift in Sources */, + C89465641BC6C2BC0055219D /* _RXSwizzling.m in Sources */, + C89464CA1BC6C2B00055219D /* Empty.swift in Sources */, + C89464C61BC6C2B00055219D /* Deferred.swift in Sources */, C8297E501B6CF905000589EA /* TableViewController.swift in Sources */, - C86409AD1BA5909000D3C4E8 /* AsObservable.swift in Sources */, - C86409A21BA5909000D3C4E8 /* SerialDisposable.swift in Sources */, C8297E511B6CF905000589EA /* PartialUpdatesViewController.swift in Sources */, - C86409E31BA5909000D3C4E8 /* ObservableType.swift in Sources */, - C86409A11BA5909000D3C4E8 /* ScopedDisposable.swift in Sources */, - C84B3A3C1BA4345A001B7D88 /* KVOObserver.swift in Sources */, - C86409941BA5909000D3C4E8 /* Bag.swift in Sources */, - C86409F81BA5909000D3C4E8 /* ReplaySubject.swift in Sources */, + C894649F1BC6C2B00055219D /* AsyncLock.swift in Sources */, C8297E521B6CF905000589EA /* Dependencies.swift in Sources */, - C84B3A4F1BA4345A001B7D88 /* RxSearchBarDelegateProxy.swift in Sources */, - C86409F71BA5909000D3C4E8 /* PublishSubject.swift in Sources */, - C86409BB1BA5909000D3C4E8 /* Empty.swift in Sources */, - C86409DE1BA5909000D3C4E8 /* Observable+Debug.swift in Sources */, - C84B3A551BA4345A001B7D88 /* UIBarButtonItem+Rx.swift in Sources */, - C86409CD1BA5909000D3C4E8 /* Skip.swift in Sources */, + C89464E11BC6C2B00055219D /* Switch.swift in Sources */, + C89465851BC6C2BC0055219D /* RxAlertViewDelegateProxy.swift in Sources */, + C89464A11BC6C2B00055219D /* ConnectableObservableType.swift in Sources */, + C89464C41BC6C2B00055219D /* ConnectableObservable.swift in Sources */, + C894658B1BC6C2BC0055219D /* RxTableViewDelegateProxy.swift in Sources */, C8297E531B6CF905000589EA /* WikipediaAPI.swift in Sources */, - C864099A1BA5909000D3C4E8 /* CompositeDisposable.swift in Sources */, - C84B3A4E1BA4345A001B7D88 /* RxScrollViewDelegateProxy.swift in Sources */, + C89465071BC6C2B00055219D /* BehaviorSubject.swift in Sources */, + C89465911BC6C2BC0055219D /* UICollectionView+Rx.swift in Sources */, + C89465781BC6C2BC0055219D /* NSObject+Rx+CoreGraphics.swift in Sources */, + C89464D01BC6C2B00055219D /* Map.swift in Sources */, C8297E541B6CF905000589EA /* AppDelegate.swift in Sources */, - C86409CA1BA5909000D3C4E8 /* Sample.swift in Sources */, + C894659D1BC6C2BC0055219D /* UITableView+Rx.swift in Sources */, + C894657F1BC6C2BC0055219D /* RxCollectionViewReactiveArrayDataSource.swift in Sources */, + C894656A1BC6C2BC0055219D /* Driver+Operators.swift in Sources */, C8297E551B6CF905000589EA /* RxTableViewSectionedDataSource.swift in Sources */, - C86409C21BA5909000D3C4E8 /* Merge.swift in Sources */, - C84B3A581BA4345A001B7D88 /* UIControl+Rx.swift in Sources */, C8297E561B6CF905000589EA /* WikipediaPage.swift in Sources */, - C86409921BA5909000D3C4E8 /* Lock.swift in Sources */, - C86409901BA5909000D3C4E8 /* Cancelable.swift in Sources */, - C8640A011BA5AB5A00D3C4E8 /* Repeat.swift in Sources */, - C84B3A2F1BA4345A001B7D88 /* _RXKVOObserver.m in Sources */, C8297E571B6CF905000589EA /* Randomizer.swift in Sources */, - C86409981BA5909000D3C4E8 /* AnonymousDisposable.swift in Sources */, - C86409ED1BA5909000D3C4E8 /* CurrentThreadScheduler.swift in Sources */, - C864099F1BA5909000D3C4E8 /* NopDisposable.swift in Sources */, - C86409CB1BA5909000D3C4E8 /* Scan.swift in Sources */, - C84B3A5B1BA4345A001B7D88 /* UIImageView+Rx.swift in Sources */, - C86409DC1BA5909000D3C4E8 /* Observable+Concurrency.swift in Sources */, - C86409F91BA5909000D3C4E8 /* SubjectType.swift in Sources */, - C86409C11BA5909000D3C4E8 /* Map.swift in Sources */, - C86409B41BA5909000D3C4E8 /* Concat.swift in Sources */, - C86409D21BA5909000D3C4E8 /* TakeUntil.swift in Sources */, - C86409F41BA5909000D3C4E8 /* SerialDispatchQueueScheduler.swift in Sources */, - C86409BA1BA5909000D3C4E8 /* Do.swift in Sources */, - C84B3A2D1BA4345A001B7D88 /* _RX.m in Sources */, - C86409AA1BA5909000D3C4E8 /* Observable.swift in Sources */, + C89464C31BC6C2B00055219D /* Concat.swift in Sources */, + C894657A1BC6C2BC0055219D /* NSURLSession+Rx.swift in Sources */, + C89465661BC6C2BC0055219D /* ControlEvent+Driver.swift in Sources */, + C89464F41BC6C2B00055219D /* ObservableType.swift in Sources */, + C89464CE1BC6C2B00055219D /* Generate.swift in Sources */, + C89465711BC6C2BC0055219D /* Observable+Bind.swift in Sources */, + C89464B01BC6C2B00055219D /* SerialDisposable.swift in Sources */, + C89464A21BC6C2B00055219D /* Bag.swift in Sources */, + C894657B1BC6C2BC0055219D /* RxCLLocationManagerDelegateProxy.swift in Sources */, C8297E581B6CF905000589EA /* User.swift in Sources */, - C86409A41BA5909000D3C4E8 /* StableCompositeDisposable.swift in Sources */, - C84B3A531BA4345A001B7D88 /* UIActionSheet+Rx.swift in Sources */, + C89464B41BC6C2B00055219D /* Event.swift in Sources */, + C89465691BC6C2BC0055219D /* ControlProperty.swift in Sources */, + C89465061BC6C2B00055219D /* SchedulerType.swift in Sources */, + C894659F1BC6C2BC0055219D /* UITextView+Rx.swift in Sources */, + C894658C1BC6C2BC0055219D /* RxTextViewDelegateProxy.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchResultViewModel.swift b/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchResultViewModel.swift index 867cc668..a6677789 100644 --- a/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchResultViewModel.swift +++ b/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchResultViewModel.swift @@ -15,8 +15,8 @@ import RxCocoa class SearchResultViewModel { let searchResult: WikipediaSearchResult - var title: Observable - var imageURLs: Observable<[NSURL]> + var title: Driver + var imageURLs: Driver<[NSURL]> let API = DefaultWikipediaAPI.sharedAPI let $: Dependencies = Dependencies.sharedDependencies @@ -24,13 +24,13 @@ class SearchResultViewModel { init(searchResult: WikipediaSearchResult) { self.searchResult = searchResult - self.title = never() - self.imageURLs = never() + self.title = Drive.never() + self.imageURLs = Drive.never() let URLs = configureImageURLs() - self.imageURLs = URLs.catchErrorJustReturn([]) - self.title = configureTitle(URLs).catchErrorJustReturn("Error during fetching") + self.imageURLs = URLs.asDriver(onErrorJustReturn: []) + self.title = configureTitle(URLs).asDriver(onErrorJustReturn: "Error during fetching") } // private methods @@ -64,7 +64,5 @@ class SearchResultViewModel { return [] } } - .observeOn($.mainScheduler) - .shareReplay(1) } } diff --git a/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchViewModel.swift b/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchViewModel.swift index b92dcf81..4e7ffbd9 100644 --- a/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchViewModel.swift +++ b/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchViewModel.swift @@ -15,14 +15,14 @@ import RxCocoa class SearchViewModel { // outputs - let rows: Observable<[SearchResultViewModel]> + let rows: Driver<[SearchResultViewModel]> let subscriptions = DisposeBag() // public methods - init(searchText: Observable, - selectedResult: Observable) { + init(searchText: Driver, + selectedResult: Driver) { let $: Dependencies = Dependencies.sharedDependencies let wireframe = Dependencies.sharedDependencies.wireframe @@ -35,7 +35,7 @@ class SearchViewModel { API.getSearchResults(query) .retry(3) .startWith([]) // clears results on new search term - .catchErrorJustReturn([]) + .asDriver(onErrorJustReturn: []) } .switchLatest() .map { results in @@ -47,7 +47,7 @@ class SearchViewModel { } selectedResult - .subscribeNext { searchResult in + .driveNext { searchResult in wireframe.openURL(searchResult.searchResult.URL) } .addDisposableTo(subscriptions) diff --git a/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchCell.swift b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchCell.swift index eeb6a509..8503322a 100644 --- a/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchCell.swift +++ b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchCell.swift @@ -33,14 +33,14 @@ public class WikipediaSearchCell: UITableViewCell { didSet { let disposeBag = DisposeBag() - (viewModel?.title ?? just("")) - .subscribe(self.titleOutlet.rx_text) + (viewModel?.title ?? Drive.just("")) + .drive(self.titleOutlet.rx_text) .addDisposableTo(disposeBag) self.URLOutlet.text = viewModel.searchResult.URL.absoluteString ?? "" viewModel.imageURLs - .bindTo(self.imagesOutlet.rx_itemsWithCellIdentifier("ImageCell")) { [unowned self] (_, URL, cell: CollectionViewImageCell) in + .drive(self.imagesOutlet.rx_itemsWithCellIdentifier("ImageCell")) { [unowned self] (_, URL, cell: CollectionViewImageCell) in let loadingPlaceholder: UIImage? = nil cell.image = self.imageService.imageFromURL(URL) diff --git a/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchViewController.swift b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchViewController.swift index 77af4748..3b0158f9 100644 --- a/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchViewController.swift +++ b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchViewController.swift @@ -33,17 +33,15 @@ class WikipediaSearchViewController: ViewController { resultsTableView.rowHeight = 194 - let selectedResult: Observable = resultsTableView.rx_modelSelected().asObservable() - let viewModel = SearchViewModel( - searchText: searchBar.rx_text.asObservable(), - selectedResult: selectedResult + searchText: searchBar.rx_text.asDriver(), + selectedResult: resultsTableView.rx_modelSelected().asDriver() ) // map table view rows // { viewModel.rows - .bindTo(resultsTableView.rx_itemsWithCellIdentifier("WikipediaSearchCell")) { (_, viewModel, cell: WikipediaSearchCell) in + .drive(resultsTableView.rx_itemsWithCellIdentifier("WikipediaSearchCell")) { (_, viewModel, cell: WikipediaSearchCell) in cell.viewModel = viewModel } .addDisposableTo(disposeBag) @@ -52,7 +50,8 @@ class WikipediaSearchViewController: ViewController { // dismiss keyboard on scroll // { resultsTableView.rx_contentOffset - .subscribeNext { _ in + .asDriver() + .driveNext { _ in if searchBar.isFirstResponder() { _ = searchBar.resignFirstResponder() } diff --git a/RxExample/RxExample/Services/ImageService.swift b/RxExample/RxExample/Services/ImageService.swift index eb5cc7e7..ceac773b 100644 --- a/RxExample/RxExample/Services/ImageService.swift +++ b/RxExample/RxExample/Services/ImageService.swift @@ -74,14 +74,14 @@ class DefaultImageService: ImageService { else { // fetch from network decodedImage = self.$.URLSession.rx_data(NSURLRequest(URL: URL)) - .doOn(next: { data in + .doOn(onNext: { data in self.imageDataCache.setObject(data, forKey: URL) }) .flatMap(self.decodeImage) } } - return decodedImage.doOn(next: { image in + return decodedImage.doOn(onNext: { image in self.imageCache.setObject(image, forKey: URL) }) }.observeOn($.mainScheduler) diff --git a/RxSwift/Concurrency/AsyncLock.swift b/RxSwift/Concurrency/AsyncLock.swift index 64b55ba6..c90572e0 100644 --- a/RxSwift/Concurrency/AsyncLock.swift +++ b/RxSwift/Concurrency/AsyncLock.swift @@ -21,7 +21,7 @@ That means that enqueued work could possibly be executed later on a different th class AsyncLock : Disposable { typealias Action = () -> Void - private var lock = NSRecursiveLock() + private let lock = NSRecursiveLock() private var queue: Queue = Queue(capacity: 2) private var isAcquired: Bool = false diff --git a/RxSwift/Observable+Extensions.swift b/RxSwift/Observable+Extensions.swift index 17f00d44..e0adffab 100644 --- a/RxSwift/Observable+Extensions.swift +++ b/RxSwift/Observable+Extensions.swift @@ -11,7 +11,7 @@ import Foundation extension ObservableType { /** Subscribes an event handler to an observable sequence. - + - parameter on: Action to invoke for each event in the observable sequence. - returns: Subscription object used to unsubscribe from the observable sequence. */ @@ -26,36 +26,36 @@ extension ObservableType { /** Subscribes an element handler, an error handler, a completion handler and disposed handler to an observable sequence. - - - parameter next: Action to invoke for each element in the observable sequence. - - parameter error: Action to invoke upon errored termination of the observable sequence. - - parameter completed: Action to invoke upon graceful termination of the observable sequence. - - parameter disposed: Action to invoke upon any type of termination of sequence (if the sequence has + + - parameter onNext: Action to invoke for each element in the observable sequence. + - parameter onError: Action to invoke upon errored termination of the observable sequence. + - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence. + - parameter onDisposed: Action to invoke upon any type of termination of sequence (if the sequence has gracefully completed, errored, or if the generation is cancelled by disposing subscription) - returns: Subscription object used to unsubscribe from the observable sequence. */ @warn_unused_result - public func subscribe(next next: ((E) -> Void)? = nil, error: ((ErrorType) -> Void)? = nil, completed: (() -> Void)? = nil, disposed: (() -> Void)? = nil) + public func subscribe(onNext onNext: ((E) -> Void)? = nil, onError: ((ErrorType) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil) -> Disposable { - + let disposable: Disposable - - if let disposed = disposed { + + if let disposed = onDisposed { disposable = AnonymousDisposable(disposed) } else { disposable = NopDisposable.instance } - + let observer = AnonymousObserver { e in switch e { case .Next(let value): - next?(value) + onNext?(value) case .Error(let e): - error?(e) + onError?(e) disposable.dispose() case .Completed: - completed?() + onCompleted?() disposable.dispose() } } @@ -67,7 +67,7 @@ extension ObservableType { /** Subscribes an element handler to an observable sequence. - + - parameter onNext: Action to invoke for each element in the observable sequence. - returns: Subscription object used to unsubscribe from the observable sequence. */ @@ -84,7 +84,7 @@ extension ObservableType { /** Subscribes an error handler to an observable sequence. - + - parameter onRrror: Action to invoke upon errored termination of the observable sequence. - returns: Subscription object used to unsubscribe from the observable sequence. */ @@ -101,7 +101,7 @@ extension ObservableType { /** Subscribes a completion handler to an observable sequence. - + - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence. - returns: Subscription object used to unsubscribe from the observable sequence. */ @@ -124,4 +124,4 @@ public extension ObservableType { func subscribeSafe(observer: O) -> Disposable { return self.subscribe(observer) } -} \ No newline at end of file +} diff --git a/RxSwift/Observable.swift b/RxSwift/Observable.swift index 2084462a..411a422e 100644 --- a/RxSwift/Observable.swift +++ b/RxSwift/Observable.swift @@ -26,7 +26,7 @@ public class Observable : ObservableType { } public func subscribe(observer: O) -> Disposable { - return abstractMethod() + abstractMethod() } public func asObservable() -> Observable { diff --git a/RxSwift/ObservableConvertibleType.swift b/RxSwift/ObservableConvertibleType.swift new file mode 100644 index 00000000..6cc3046b --- /dev/null +++ b/RxSwift/ObservableConvertibleType.swift @@ -0,0 +1,26 @@ +// +// ObservableConvertibleType.swift +// Rx +// +// Created by Krunoslav Zaher on 9/17/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +/** +Type that can be converted to observable sequence (`Observer`). +*/ +public protocol ObservableConvertibleType { + /** + Type of elements in sequence. + */ + typealias E + + /** + Converts `self` to `Observable` sequence. + + - returns: Observable sequence that represents `self`. + */ + func asObservable() -> Observable +} diff --git a/RxSwift/ObservableType.swift b/RxSwift/ObservableType.swift index 816ac444..9e79ee5c 100644 --- a/RxSwift/ObservableType.swift +++ b/RxSwift/ObservableType.swift @@ -11,7 +11,7 @@ import Foundation /** Represents a push style sequence. */ -public protocol ObservableType { +public protocol ObservableType : ObservableConvertibleType { /** Type of elements in sequence. */ @@ -42,8 +42,14 @@ public protocol ObservableType { */ func subscribe(observer: O) -> Disposable +} + +extension ObservableType { + /** - - returns: Canonical interface for push style sequence + Default implementation of converting `ObservableType` to `Observable`. */ - func asObservable() -> Observable + public func asObservable() -> Observable { + return create(self.subscribe) + } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Catch.swift b/RxSwift/Observables/Implementations/Catch.swift index 7653e54b..9283343f 100644 --- a/RxSwift/Observables/Implementations/Catch.swift +++ b/RxSwift/Observables/Implementations/Catch.swift @@ -97,7 +97,7 @@ class Catch : Producer { // catch enumerable -class CatchSequenceSink : TailRecursiveSink { +class CatchSequenceSink : TailRecursiveSink { typealias Element = O.E typealias Parent = CatchSequence @@ -141,7 +141,7 @@ class CatchSequenceSink : Producer { +class CatchSequence : Producer { typealias Element = S.Generator.Element.E let sources: S diff --git a/RxSwift/Observables/Implementations/CombineLatest.swift b/RxSwift/Observables/Implementations/CombineLatest.swift index 50e4311f..9fa55ce7 100644 --- a/RxSwift/Observables/Implementations/CombineLatest.swift +++ b/RxSwift/Observables/Implementations/CombineLatest.swift @@ -34,7 +34,7 @@ class CombineLatestSink : Sink, CombineLatestProtocol { } func getResult() throws -> Element { - return abstractMethod() + abstractMethod() } func next(index: Int) { diff --git a/RxSwift/Observables/Implementations/Concat.swift b/RxSwift/Observables/Implementations/Concat.swift index 042bf4df..0b1023d6 100644 --- a/RxSwift/Observables/Implementations/Concat.swift +++ b/RxSwift/Observables/Implementations/Concat.swift @@ -9,7 +9,7 @@ import Foundation -class ConcatSink : TailRecursiveSink { +class ConcatSink : TailRecursiveSink { typealias Element = O.E override init(observer: O, cancel: Disposable) { @@ -38,7 +38,7 @@ class ConcatSink : Producer { +class Concat : Producer { typealias Element = S.Generator.Element.E let sources: S diff --git a/RxSwift/Observables/Implementations/FlatMap.swift b/RxSwift/Observables/Implementations/FlatMap.swift index 7395e01a..34715a7c 100644 --- a/RxSwift/Observables/Implementations/FlatMap.swift +++ b/RxSwift/Observables/Implementations/FlatMap.swift @@ -11,7 +11,7 @@ import Foundation // It's value is one because initial source subscription is always in CompositeDisposable let FlatMapNoIterators = 1 -class FlatMapSinkIter : ObserverType { +class FlatMapSinkIter : ObserverType { typealias Parent = FlatMapSink typealias DisposeKey = CompositeDisposable.DisposeKey typealias E = O.E @@ -52,7 +52,7 @@ class FlatMapSinkIter : Sink, ObserverType { +class FlatMapSink : Sink, ObserverType { typealias ResultType = O.E typealias Element = SourceType typealias Parent = FlatMap @@ -73,7 +73,7 @@ class FlatMapSink S { - return abstractMethod() + abstractMethod() } func on(event: Event) { @@ -133,7 +133,7 @@ class FlatMapSink : FlatMapSink { +class FlatMapSink1 : FlatMapSink { override init(parent: Parent, observer: O, cancel: Disposable) { super.init(parent: parent, observer: observer, cancel: cancel) } @@ -143,10 +143,9 @@ class FlatMapSink1 : FlatMapSink { - +class FlatMapSink2 : FlatMapSink { private var _index = 0 - + override init(parent: Parent, observer: O, cancel: Disposable) { super.init(parent: parent, observer: observer, cancel: cancel) } @@ -156,7 +155,7 @@ class FlatMapSink2: Producer { +class FlatMap: Producer { typealias Selector1 = (SourceType) throws -> S typealias Selector2 = (SourceType, Int) throws -> S diff --git a/RxSwift/Observables/Implementations/Map.swift b/RxSwift/Observables/Implementations/Map.swift index 58cba636..4d559e8b 100644 --- a/RxSwift/Observables/Implementations/Map.swift +++ b/RxSwift/Observables/Implementations/Map.swift @@ -21,7 +21,7 @@ class MapSink : Sink, ObserverType { } func performMap(element: SourceType) throws -> ResultType { - return abstractMethod() + abstractMethod() } func on(event: Event) { diff --git a/RxSwift/Observables/Implementations/Merge.swift b/RxSwift/Observables/Implementations/Merge.swift index bdbd2dbe..52fe4701 100644 --- a/RxSwift/Observables/Implementations/Merge.swift +++ b/RxSwift/Observables/Implementations/Merge.swift @@ -10,7 +10,7 @@ import Foundation // sequential -class MergeSinkIter : ObserverType { +class MergeSinkIter : ObserverType { typealias E = O.E typealias DisposeKey = Bag.KeyType typealias Parent = MergeSink @@ -43,7 +43,7 @@ class MergeSinkIter : Obser } } -class MergeSink : Sink, ObserverType { +class MergeSink : Sink, ObserverType { typealias E = S typealias Parent = Merge @@ -80,7 +80,7 @@ class MergeSink : Sink, if let key = maybeKey { let observer = MergeSinkIter(parent: self, disposeKey: key) - let disposable = value.subscribeSafe(observer) + let disposable = value.asObservable().subscribeSafe(observer) innerSubscription.disposable = disposable } case .Error(let error): @@ -106,7 +106,7 @@ class MergeSink : Sink, // concurrent -class MergeConcurrentSinkIter : ObserverType { +class MergeConcurrentSinkIter : ObserverType { typealias E = O.E typealias DisposeKey = Bag.KeyType typealias Parent = MergeConcurrentSink @@ -147,7 +147,7 @@ class MergeConcurrentSinkIter : Sink, ObserverType { +class MergeConcurrentSink : Sink, ObserverType { typealias E = S typealias Parent = Merge typealias QueueType = Queue @@ -187,7 +187,7 @@ class MergeConcurrentSink : if let key = key { let observer = MergeConcurrentSinkIter(parent: self, disposeKey: key) - let disposable = innerSource.subscribeSafe(observer) + let disposable = innerSource.asObservable().subscribeSafe(observer) subscription.disposable = disposable } } @@ -230,7 +230,7 @@ class MergeConcurrentSink : } } -class Merge : Producer { +class Merge : Producer { let sources: Observable let maxConcurrent: Int diff --git a/RxSwift/Observables/Implementations/ObserveOn.swift b/RxSwift/Observables/Implementations/ObserveOn.swift index 72c5c7a8..f128cee5 100644 --- a/RxSwift/Observables/Implementations/ObserveOn.swift +++ b/RxSwift/Observables/Implementations/ObserveOn.swift @@ -46,6 +46,8 @@ class ObserveOnSink : ObserverBase { var cancel: Disposable + var lock = SpinLock() + let scheduler: ImmediateSchedulerType var observer: O? diff --git a/RxSwift/Observables/Implementations/Producer.swift b/RxSwift/Observables/Implementations/Producer.swift index 59974d4a..c1d32a08 100644 --- a/RxSwift/Observables/Implementations/Producer.swift +++ b/RxSwift/Observables/Implementations/Producer.swift @@ -44,6 +44,6 @@ public class Producer : Observable { } public func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - return abstractMethod() + abstractMethod() } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/SkipUntil.swift b/RxSwift/Observables/Implementations/SkipUntil.swift index 6ce123ce..16e42f2c 100644 --- a/RxSwift/Observables/Implementations/SkipUntil.swift +++ b/RxSwift/Observables/Implementations/SkipUntil.swift @@ -18,7 +18,7 @@ class SkipUntilSinkOther : Sink, ObserverType { +class SwitchSink : Sink, ObserverType { typealias E = S typealias Parent = Switch @@ -48,7 +48,7 @@ class SwitchSink : Sink, innerSubscription.disposable = d let observer = SwitchSinkIter(parent: self, id: latest, _self: d) - let disposable = observable.subscribeSafe(observer) + let disposable = observable.asObservable().subscribeSafe(observer) d.disposable = disposable case .Error(let error): self.lock.performLocked { @@ -70,7 +70,7 @@ class SwitchSink : Sink, } } -class SwitchSinkIter : ObserverType { +class SwitchSinkIter : ObserverType { typealias E = O.E typealias Parent = SwitchSink @@ -116,7 +116,7 @@ class SwitchSinkIter : Obse } } -class Switch : Producer { +class Switch : Producer { let sources: Observable init(sources: Observable) { diff --git a/RxSwift/Observables/Implementations/TakeUntil.swift b/RxSwift/Observables/Implementations/TakeUntil.swift index 2109546b..f791a4c2 100644 --- a/RxSwift/Observables/Implementations/TakeUntil.swift +++ b/RxSwift/Observables/Implementations/TakeUntil.swift @@ -18,7 +18,7 @@ class TakeUntilSinkOther : Sink, ZipSinkProtocol { } func getResult() throws -> Element { - return abstractMethod() + abstractMethod() } func hasElements(index: Int) -> Bool { - return abstractMethod() + abstractMethod() } func next(index: Int) { diff --git a/RxSwift/Observables/Observable+Multiple.swift b/RxSwift/Observables/Observable+Multiple.swift index d9829dda..0ed6e70a 100644 --- a/RxSwift/Observables/Observable+Multiple.swift +++ b/RxSwift/Observables/Observable+Multiple.swift @@ -40,7 +40,7 @@ extension CollectionType where Generator.Element : ObservableType { // switch -extension ObservableType where E : ObservableType { +extension ObservableType where E : ObservableConvertibleType { /** Transforms an observable sequence of observable sequences into an observable sequence @@ -51,7 +51,6 @@ extension ObservableType where E : ObservableType { - returns: The observable sequence that at any point in time produces the elements of the most recent inner observable sequence that has been received. */ - public func switchLatest() -> Observable { return Switch(sources: self.asObservable()) } @@ -59,7 +58,7 @@ extension ObservableType where E : ObservableType { // concat -extension SequenceType where Generator.Element : ObservableType { +extension SequenceType where Generator.Element : ObservableConvertibleType { /** Concatenates all observable sequences in the given sequence, as long as the previous observable sequence terminated successfully. @@ -72,7 +71,7 @@ extension SequenceType where Generator.Element : ObservableType { } } -extension ObservableType where E : ObservableType { +extension ObservableType where E : ObservableConvertibleType { /** Concatenates all inner observable sequences, as long as the previous observable sequence terminated successfully. @@ -86,7 +85,7 @@ extension ObservableType where E : ObservableType { // merge -extension ObservableType where E : ObservableType { +extension ObservableType where E : ObservableConvertibleType { /** Merges elements from all observable sequences in the given enumerable sequence into a single observable sequence. @@ -137,7 +136,7 @@ extension ObservableType { } -extension SequenceType where Generator.Element : ObservableType { +extension SequenceType where Generator.Element : ObservableConvertibleType { /** Continues an observable sequence that is terminated by an error with the next observable sequence. @@ -198,7 +197,7 @@ extension ObservableType { } } -extension SequenceType where Generator.Element : ObservableType { +extension SequenceType where Generator.Element : ObservableConvertibleType { /** Propagates the observable sequence that reacts first. @@ -208,7 +207,7 @@ extension SequenceType where Generator.Element : ObservableType { public func amb() -> Observable { return self.reduce(never()) { a, o in - return a.amb(o) + return a.amb(o.asObservable()) } } } diff --git a/RxSwift/Observables/Observable+Single.swift b/RxSwift/Observables/Observable+Single.swift index 7b1efb9c..cfe98678 100644 --- a/RxSwift/Observables/Observable+Single.swift +++ b/RxSwift/Observables/Observable+Single.swift @@ -77,21 +77,21 @@ extension ObservableType { /** Invokes an action for each event in the observable sequence, and propagates all observer messages through the result sequence. - - parameter next: Action to invoke for each element in the observable sequence. - - parameter error: Action to invoke upon errored termination of the observable sequence. - - parameter completed: Action to invoke upon graceful termination of the observable sequence. + - parameter onNext: Action to invoke for each element in the observable sequence. + - parameter onError: Action to invoke upon errored termination of the observable sequence. + - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence. - returns: The source sequence with the side-effecting behavior applied. */ - public func doOn(next next: (E throws -> Void)? = nil, error: (ErrorType throws -> Void)? = nil, completed: (() throws -> Void)? = nil) + public func doOn(onNext onNext: (E throws -> Void)? = nil, onError: (ErrorType throws -> Void)? = nil, onCompleted: (() throws -> Void)? = nil) -> Observable { return Do(source: self.asObservable()) { e in switch e { case .Next(let element): - try next?(element) + try onNext?(element) case .Error(let e): - try error?(e) + try onError?(e) case .Completed: - try completed?() + try onCompleted?() } } } diff --git a/RxSwift/Observables/Observable+StandardSequenceOperators.swift b/RxSwift/Observables/Observable+StandardSequenceOperators.swift index 8a12aeb5..680dd457 100644 --- a/RxSwift/Observables/Observable+StandardSequenceOperators.swift +++ b/RxSwift/Observables/Observable+StandardSequenceOperators.swift @@ -153,7 +153,7 @@ extension ObservableType { - parameter selector: A transform function to apply to each element. - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence. */ - public func flatMap(selector: (E) throws -> O) + public func flatMap(selector: (E) throws -> O) -> Observable { return FlatMap(source: self.asObservable(), selector: selector) } @@ -164,7 +164,7 @@ extension ObservableType { - parameter selector: A transform function to apply to each element; the second parameter of the function represents the index of the source element. - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence. */ - public func flatMapWithIndex(selector: (E, Int) throws -> O) + public func flatMapWithIndex(selector: (E, Int) throws -> O) -> Observable { return FlatMap(source: self.asObservable(), selector: selector) } diff --git a/RxSwift/Observables/Observable+Time.swift b/RxSwift/Observables/Observable+Time.swift index a0c6f897..e0f2fe03 100644 --- a/RxSwift/Observables/Observable+Time.swift +++ b/RxSwift/Observables/Observable+Time.swift @@ -34,7 +34,7 @@ extension ObservableType { - parameter scheduler: Scheduler to run the throttle timers and send events on. - returns: The throttled sequence. */ - public func debounce(dueTime: S.TimeInterval, scheduler: S) + public func debounce(dueTime: S.TimeInterval, _ scheduler: S) -> Observable { return Throttle(source: self.asObservable(), dueTime: dueTime, scheduler: scheduler) } diff --git a/RxSwift/Observers/ObserverBase.swift b/RxSwift/Observers/ObserverBase.swift index fac004fc..912f6876 100644 --- a/RxSwift/Observers/ObserverBase.swift +++ b/RxSwift/Observers/ObserverBase.swift @@ -11,7 +11,6 @@ import Foundation class ObserverBase : Disposable, ObserverType { typealias E = ElementType - var lock = SpinLock() var isStopped: Int32 = 0 init() { @@ -34,7 +33,7 @@ class ObserverBase : Disposable, ObserverType { } func onCore(event: Event) { - return abstractMethod() + abstractMethod() } func dispose() { diff --git a/RxSwift/Observers/TailRecursiveSink.swift b/RxSwift/Observers/TailRecursiveSink.swift index 218e58e4..1061c2c8 100644 --- a/RxSwift/Observers/TailRecursiveSink.swift +++ b/RxSwift/Observers/TailRecursiveSink.swift @@ -9,7 +9,7 @@ import Foundation /// This class is usually used with `Generator` version of the operators. -class TailRecursiveSink : Sink, ObserverType { +class TailRecursiveSink : Sink, ObserverType { typealias E = O.E var generators: [S.Generator] = [] @@ -54,11 +54,11 @@ class TailRecursiveSink) -> S.Generator? { - return abstractMethod() + abstractMethod() } func on(event: Event) { - return abstractMethod() + abstractMethod() } // should be done on gate locked diff --git a/RxSwift/Rx.swift b/RxSwift/Rx.swift index 64c1f6d7..90598ec3 100644 --- a/RxSwift/Rx.swift +++ b/RxSwift/Rx.swift @@ -19,13 +19,11 @@ public var resourceCount: Int32 = 0 // Swift doesn't have a concept of abstract metods. // This function is being used as a runtime check that abstract methods aren't being called. -func abstractMethod() -> T { +@noreturn func abstractMethod() -> Void { rxFatalError("Abstract method") - let dummyValue: T? = nil - return dummyValue! } -func rxFatalError(lastMessage: String) { +@noreturn func rxFatalError(lastMessage: String) { // The temptation to comment this line is great, but please don't, it's for your own good. The choice is yours. fatalError(lastMessage) } diff --git a/RxSwift/Schedulers/RecursiveScheduler.swift b/RxSwift/Schedulers/RecursiveScheduler.swift index 38e9b898..1f3b9f61 100644 --- a/RxSwift/Schedulers/RecursiveScheduler.swift +++ b/RxSwift/Schedulers/RecursiveScheduler.swift @@ -45,11 +45,11 @@ public class RecursiveSchedulerOf { // abstract methods func scheduleRelativeAdapter(state: State, dueTime: TimeInterval, action: State -> Disposable) -> Disposable { - return abstractMethod() + abstractMethod() } func scheduleAdapter(state: State, action: State -> Disposable) -> Disposable { - return abstractMethod() + abstractMethod() } /** diff --git a/RxSwift/Subjects/ReplaySubject.swift b/RxSwift/Subjects/ReplaySubject.swift index 7fcf3f32..a21ff787 100644 --- a/RxSwift/Subjects/ReplaySubject.swift +++ b/RxSwift/Subjects/ReplaySubject.swift @@ -19,7 +19,7 @@ public class ReplaySubject : Observable, SubjectType, Observer typealias DisposeKey = Bag>.KeyType func unsubscribe(key: DisposeKey) { - return abstractMethod() + abstractMethod() } /** @@ -28,7 +28,7 @@ public class ReplaySubject : Observable, SubjectType, Observer - parameter event: Event to send to the observers. */ public func on(event: Event) { - return abstractMethod() + abstractMethod() } /** @@ -73,15 +73,15 @@ class ReplayBufferBase : ReplaySubject { } func trim() { - return abstractMethod() + abstractMethod() } func addValueToBuffer(value: Element) { - return abstractMethod() + abstractMethod() } func replayBuffer(observer: ObserverOf) { - return abstractMethod() + abstractMethod() } override func on(event: Event) { diff --git a/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift b/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift index a042c81b..c9d30db7 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift @@ -91,7 +91,7 @@ extension ObservableCreationTests { return x + 1 } .take(4) - .subscribe(next: { x in + .subscribe(onNext: { x in elements.append(x) }) diff --git a/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift b/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift index eae22e2d..ddbc3d35 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift @@ -772,9 +772,9 @@ extension ObservableTimeTest { OSSpinLockLock(&lock) - let d = interval(0, scheduler).takeWhile { $0 < 10 } .subscribe(next: { t in + let d = interval(0, scheduler).takeWhile { $0 < 10 } .subscribe(onNext: { t in observer.on(.Next(t)) - }, completed: { + }, onCompleted: { OSSpinLockUnlock(&lock) }) diff --git a/RxTests/RxSwiftTests/Tests/ObserverTests.swift b/RxTests/RxSwiftTests/Tests/ObserverTests.swift index f3da5516..cb3db335 100644 --- a/RxTests/RxSwiftTests/Tests/ObserverTests.swift +++ b/RxTests/RxSwiftTests/Tests/ObserverTests.swift @@ -12,77 +12,77 @@ import RxSwift class ObserverTests: RxTest { } extension ObserverTests { - + func testConvenienceOn_Next() { var observer: ObserverOf! let a: Observable = create { o in observer = o return NopDisposable.instance } - + var elements = [Int]() - + _ = a.subscribeNext { n in elements.append(n) } - + XCTAssertEqual(elements, []) - + observer.onNext(0) - + XCTAssertEqual(elements, [0]) } - + func testConvenienceOn_Error() { var observer: ObserverOf! let a: Observable = create { o in observer = o return NopDisposable.instance } - + var elements = [Int]() var errrorNotification: NSError! - + _ = a.subscribe( - next: { n in elements.append(n) }, - error: { e in + onNext: { n in elements.append(n) }, + onError: { e in errrorNotification = e as NSError } ) - + XCTAssertEqual(elements, []) - + observer.onNext(0) XCTAssertEqual(elements, [0]) - + observer.onError(testError) - + observer.onNext(1) XCTAssertEqual(elements, [0]) XCTAssertEqual(errrorNotification, testError) } - + func testConvenienceOn_Complete() { var observer: ObserverOf! let a: Observable = create { o in observer = o return NopDisposable.instance } - + var elements = [Int]() - + _ = a.subscribeNext { n in elements.append(n) } - + XCTAssertEqual(elements, []) - + observer.onNext(0) XCTAssertEqual(elements, [0]) - + observer.onComplete() - + observer.onNext(1) XCTAssertEqual(elements, [0]) } -} \ No newline at end of file +} From 9a3ae4625c0e421abfb127d854ba14c62604b8f8 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Tue, 13 Oct 2015 22:18:30 +0200 Subject: [PATCH 032/210] Performance optimizations, makes `Observable` init internal. --- Rx.xcodeproj/project.pbxproj | 10 + RxSwift/DataStructures/Bag.swift | 39 +- RxSwift/Observable.swift | 2 +- .../Implementations/AsObservable.swift | 9 - .../ConnectableObservable.swift | 2 +- .../Implementations/ShareReplay1.swift | 77 ++++ .../Implementations/SubscribeOn.swift | 14 +- RxSwift/Observables/Observable+Binding.swift | 7 +- .../Observables/Observable+Concurrency.swift | 2 +- .../Mocks/ColdObservable.swift | 8 +- .../Mocks/HotObservable.swift | 7 +- .../TestImplementations/Mocks/MySubject.swift | 9 +- .../Mocks/PrimitiveHotObservable.swift | 10 +- .../Mocks/TestConnectableObservable.swift | 6 +- .../Observable+Extensions.swift | 16 + .../RxSwiftTests/Tests/AssumptionsTest.swift | 2 +- .../RxSwiftTests/Tests/DisposableTest.swift | 2 +- .../Tests/Observable+BindingTest.swift | 409 ++++++++++++++---- .../Tests/Observable+MultipleTest.swift | 32 +- ...rvable+StandardSequenceOperatorsTest.swift | 4 +- 20 files changed, 477 insertions(+), 190 deletions(-) create mode 100644 RxSwift/Observables/Implementations/ShareReplay1.swift diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index a14326bd..fe9c2125 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -326,6 +326,10 @@ C8945FEA1BC6C09D0055219D /* ObservableConvertibleType+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BCC1BADE8D600D7F20C /* ObservableConvertibleType+Driver.swift */; }; C89461751BC6C1210055219D /* ObservableConvertibleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C849BE2A1BAB5D070019AD27 /* ObservableConvertibleType.swift */; }; C89461761BC6C1220055219D /* ObservableConvertibleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C849BE2A1BAB5D070019AD27 /* ObservableConvertibleType.swift */; }; + C89CDB361BCB0DD7002063D9 /* ShareReplay1.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89CDB351BCB0DD7002063D9 /* ShareReplay1.swift */; }; + C89CDB371BCB0DD7002063D9 /* ShareReplay1.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89CDB351BCB0DD7002063D9 /* ShareReplay1.swift */; }; + C89CDB381BCB0DD7002063D9 /* ShareReplay1.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89CDB351BCB0DD7002063D9 /* ShareReplay1.swift */; }; + C89CDB391BCB0DD7002063D9 /* ShareReplay1.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89CDB351BCB0DD7002063D9 /* ShareReplay1.swift */; }; C8C3D9FE1B935EDF004D233E /* Zip+CollectionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C3D9FD1B935EDF004D233E /* Zip+CollectionType.swift */; }; C8C3D9FF1B935EDF004D233E /* Zip+CollectionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C3D9FD1B935EDF004D233E /* Zip+CollectionType.swift */; }; C8C3DA031B9390C4004D233E /* Just.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C3DA021B9390C4004D233E /* Just.swift */; }; @@ -928,6 +932,7 @@ C88254131B8A752B00B02D69 /* UITextField+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextField+Rx.swift"; sourceTree = ""; }; C88254141B8A752B00B02D69 /* UITextView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextView+Rx.swift"; sourceTree = ""; }; C88BB8711B07E5ED0064D411 /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C89CDB351BCB0DD7002063D9 /* ShareReplay1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareReplay1.swift; sourceTree = ""; }; C8A56AD71AD7424700B4673B /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8C3D9FD1B935EDF004D233E /* Zip+CollectionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Zip+CollectionType.swift"; sourceTree = ""; }; C8C3DA021B9390C4004D233E /* Just.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Just.swift; sourceTree = ""; }; @@ -1160,6 +1165,7 @@ C8640A021BA5B12A00D3C4E8 /* Repeat.swift */, C8093C861B8A72BE0088E94D /* Sample.swift */, C8093C871B8A72BE0088E94D /* Scan.swift */, + C89CDB351BCB0DD7002063D9 /* ShareReplay1.swift */, C8093C881B8A72BE0088E94D /* Sink.swift */, C8093C891B8A72BE0088E94D /* Skip.swift */, D285BAC31BC0231000B3F602 /* SkipUntil.swift */, @@ -2068,6 +2074,7 @@ C8093D7A1B8A72BE0088E94D /* TailRecursiveSink.swift in Sources */, C8093CC81B8A72BE0088E94D /* AsyncLock.swift in Sources */, C8093CD81B8A72BE0088E94D /* BinaryDisposable.swift in Sources */, + C89CDB371BCB0DD7002063D9 /* ShareReplay1.swift in Sources */, C8093D2A1B8A72BE0088E94D /* ObserveOn.swift in Sources */, C8093D361B8A72BE0088E94D /* Sample.swift in Sources */, D2752D621BC5551A0070C418 /* SkipUntil.swift in Sources */, @@ -2183,6 +2190,7 @@ C8093D791B8A72BE0088E94D /* TailRecursiveSink.swift in Sources */, C8093CC71B8A72BE0088E94D /* AsyncLock.swift in Sources */, C8093CD71B8A72BE0088E94D /* BinaryDisposable.swift in Sources */, + C89CDB361BCB0DD7002063D9 /* ShareReplay1.swift in Sources */, C8093D291B8A72BE0088E94D /* ObserveOn.swift in Sources */, C8093D351B8A72BE0088E94D /* Sample.swift in Sources */, D285BAC41BC0231000B3F602 /* SkipUntil.swift in Sources */, @@ -2298,6 +2306,7 @@ C8F0BFB41BBBFB8B001B112F /* TailRecursiveSink.swift in Sources */, C8F0BFB51BBBFB8B001B112F /* AsyncLock.swift in Sources */, C8F0BFB61BBBFB8B001B112F /* BinaryDisposable.swift in Sources */, + C89CDB391BCB0DD7002063D9 /* ShareReplay1.swift in Sources */, C8F0BFB71BBBFB8B001B112F /* ObserveOn.swift in Sources */, C8F0BFB81BBBFB8B001B112F /* Sample.swift in Sources */, D21C29311BC6A1C300448E70 /* SkipUntil.swift in Sources */, @@ -2559,6 +2568,7 @@ D2EBEAE21BB9B697003A27DC /* Observable.swift in Sources */, D2EBEB091BB9B6C1003A27DC /* DistinctUntilChanged.swift in Sources */, D2EBEB2A1BB9B6C5003A27DC /* Zip+CollectionType.swift in Sources */, + C89CDB381BCB0DD7002063D9 /* ShareReplay1.swift in Sources */, D2EBEB401BB9B6DE003A27DC /* BehaviorSubject.swift in Sources */, D2EBEB271BB9B6C1003A27DC /* Timer.swift in Sources */, D2752D631BC5551B0070C418 /* SkipUntil.swift in Sources */, diff --git a/RxSwift/DataStructures/Bag.swift b/RxSwift/DataStructures/Bag.swift index 9b902f7c..f8d144e4 100644 --- a/RxSwift/DataStructures/Bag.swift +++ b/RxSwift/DataStructures/Bag.swift @@ -53,9 +53,6 @@ public struct Bag : CustomStringConvertible { private var uniqueIdentity: Identity? private var nextKey: ScopeUniqueTokenType = 0 - var preallocated_0: Entry? - var preallocated_1: Entry? - var pairs = [Entry]() /** @@ -92,16 +89,6 @@ public struct Bag : CustomStringConvertible { let key = BagKey(uniqueIdentity: uniqueIdentity, key: nextKey) - if preallocated_0 == nil { - preallocated_0 = (key: key, value: element) - return key - } - - if preallocated_1 == nil { - preallocated_1 = (key: key, value: element) - return key - } - pairs.append(key: key, value: element) return key @@ -111,15 +98,13 @@ public struct Bag : CustomStringConvertible { - returns: Number of elements in bag. */ public var count: Int { - return pairs.count + (preallocated_0 != nil ? 1 : 0) + (preallocated_1 != nil ? 1 : 0) + return pairs.count } /** Removes all elements from bag and clears capacity. */ public mutating func removeAll() { - preallocated_0 = nil - preallocated_1 = nil pairs.removeAll(keepCapacity: false) } @@ -130,17 +115,6 @@ public struct Bag : CustomStringConvertible { - returns: Element that bag contained, or nil in case element was already removed. */ public mutating func removeKey(key: BagKey) -> T? { - if preallocated_0?.key == key { - let value = preallocated_0!.value - preallocated_0 = nil - return value - } - if preallocated_1?.key == key { - let value = preallocated_1!.value - preallocated_1 = nil - return value - } - for i in 0 ..< pairs.count { if pairs[i].key == key { let value = pairs[i].value @@ -160,19 +134,8 @@ extension Bag { - parameter action: Enumeration closure. */ public func forEach(@noescape action: (T) -> Void) { - let value0 = preallocated_0 - let value1 = preallocated_1 - let pairs = self.pairs - if let value = value0?.value { - action(value) - } - - if let value = value1?.value { - action(value) - } - for i in 0 ..< pairs.count { action(pairs[i].value) } diff --git a/RxSwift/Observable.swift b/RxSwift/Observable.swift index 411a422e..8fa99d36 100644 --- a/RxSwift/Observable.swift +++ b/RxSwift/Observable.swift @@ -19,7 +19,7 @@ public class Observable : ObservableType { */ public typealias E = Element - public init() { + init() { #if TRACE_RESOURCES OSAtomicIncrement32(&resourceCount) #endif diff --git a/RxSwift/Observables/Implementations/AsObservable.swift b/RxSwift/Observables/Implementations/AsObservable.swift index 0f7a42c6..96623006 100644 --- a/RxSwift/Observables/Implementations/AsObservable.swift +++ b/RxSwift/Observables/Implementations/AsObservable.swift @@ -24,7 +24,6 @@ class AsObservableSink : Sink, ObserverType { default: break } } - } class AsObservable : Producer { @@ -35,14 +34,6 @@ class AsObservable : Producer { self.source = source } - func omega() -> Observable { - return self - } - - func eval() -> Observable { - return source - } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { let sink = AsObservableSink(observer: observer, cancel: cancel) setSink(sink) diff --git a/RxSwift/Observables/Implementations/ConnectableObservable.swift b/RxSwift/Observables/Implementations/ConnectableObservable.swift index 497e09a2..83db99f0 100644 --- a/RxSwift/Observables/Implementations/ConnectableObservable.swift +++ b/RxSwift/Observables/Implementations/ConnectableObservable.swift @@ -28,7 +28,7 @@ class Connection : Disposable { } self.subscription = nil - if let parent = self.parent { + if self.parent?.connection === self { parent.connection = nil } self.parent = nil diff --git a/RxSwift/Observables/Implementations/ShareReplay1.swift b/RxSwift/Observables/Implementations/ShareReplay1.swift new file mode 100644 index 00000000..fa4bdc78 --- /dev/null +++ b/RxSwift/Observables/Implementations/ShareReplay1.swift @@ -0,0 +1,77 @@ +// +// ShareReplay1.swift +// Rx +// +// Created by Krunoslav Zaher on 10/10/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +// optimized version of share replay for most common case +class ShareReplay1 : Observable, ObserverType { + private let _source: Observable + + private let _lock = NSRecursiveLock() + + private var _subscription: Disposable? + private var _element: Element? + private var _stopEvent = nil as Event? + private var _observers = Bag>() + + init(source: Observable) { + self._source = source + } + + override func subscribe(observer: O) -> Disposable { + return _lock.calculateLocked { + if let element = self._element { + observer.on(.Next(element)) + } + + if let stopEvent = self._stopEvent { + observer.on(stopEvent) + return NopDisposable.instance + } + + let initialCount = self._observers.count + + let observerKey = self._observers.insert(ObserverOf(observer)) + + if initialCount == 0 { + self._subscription = self._source.subscribe(self) + } + + return AnonymousDisposable { + self._lock.performLocked { + self._observers.removeKey(observerKey) + + if self._observers.count == 0 { + self._subscription?.dispose() + self._subscription = nil + } + } + } + } + } + + func on(event: Event) { + _lock.performLocked { + if self._stopEvent != nil { + return + } + + if case .Next(let element) = event { + self._element = element + } + + if event.isStopEvent { + self._stopEvent = event + } + + _observers.forEach { o in + o.on(event) + } + } + } +} \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/SubscribeOn.swift b/RxSwift/Observables/Implementations/SubscribeOn.swift index 3862497a..e1419d40 100644 --- a/RxSwift/Observables/Implementations/SubscribeOn.swift +++ b/RxSwift/Observables/Implementations/SubscribeOn.swift @@ -8,9 +8,9 @@ import Foundation -class SubscribeOnSink : Sink, ObserverType { +class SubscribeOnSink : Sink, ObserverType { typealias Element = O.E - typealias Parent = SubscribeOn + typealias Parent = SubscribeOn let parent: Parent @@ -34,7 +34,7 @@ class SubscribeOnSink : Sink, ObserverType { disposeEverything.disposable = cancelSchedule cancelSchedule.disposable = parent.scheduler.schedule(()) { (_) -> Disposable in - let subscription = self.parent.source.subscribeSafe(self) + let subscription = self.parent.source.subscribe(self) disposeEverything.disposable = ScheduledDisposable(scheduler: self.parent.scheduler, disposable: subscription) return NopDisposable.instance } @@ -43,16 +43,16 @@ class SubscribeOnSink : Sink, ObserverType { } } -class SubscribeOn : Producer { - let source: Observable +class SubscribeOn : Producer { + let source: Ob let scheduler: ImmediateSchedulerType - init(source: Observable, scheduler: ImmediateSchedulerType) { + init(source: Ob, scheduler: ImmediateSchedulerType) { self.source = source self.scheduler = scheduler } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { let sink = SubscribeOnSink(parent: self, observer: observer, cancel: cancel) setSink(sink) return sink.run() diff --git a/RxSwift/Observables/Observable+Binding.swift b/RxSwift/Observables/Observable+Binding.swift index 5bf5454b..a3548121 100644 --- a/RxSwift/Observables/Observable+Binding.swift +++ b/RxSwift/Observables/Observable+Binding.swift @@ -126,6 +126,11 @@ extension ObservableType { */ public func shareReplay(bufferSize: Int) -> Observable { - return self.replay(bufferSize).refCount() + if bufferSize == 1 { + return ShareReplay1(source: self.asObservable()) + } + else { + return self.replay(bufferSize).refCount() + } } } \ No newline at end of file diff --git a/RxSwift/Observables/Observable+Concurrency.swift b/RxSwift/Observables/Observable+Concurrency.swift index 349a142c..b6959b37 100644 --- a/RxSwift/Observables/Observable+Concurrency.swift +++ b/RxSwift/Observables/Observable+Concurrency.swift @@ -51,6 +51,6 @@ extension ObservableType { */ public func subscribeOn(scheduler: ImmediateSchedulerType) -> Observable { - return SubscribeOn(source: self.asObservable(), scheduler: scheduler) + return SubscribeOn(source: self, scheduler: scheduler) } } \ No newline at end of file diff --git a/RxTests/RxSwiftTests/TestImplementations/Mocks/ColdObservable.swift b/RxTests/RxSwiftTests/TestImplementations/Mocks/ColdObservable.swift index 7d0e360a..d0be91f7 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Mocks/ColdObservable.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Mocks/ColdObservable.swift @@ -8,7 +8,9 @@ import RxSwift -class ColdObservable: Observable { +class ColdObservable : ObservableType, ObservableConvertibleType { + typealias E = Element + typealias Events = Recorded typealias Observer = ObserverOf @@ -24,11 +26,9 @@ class ColdObservable: Observable { self.recordedEvents = recordedEvents self.subscriptions = [] self.observers = Bag() - - super.init() } - override func subscribe(observer: O) -> Disposable { + func subscribe(observer: O) -> Disposable { let key = observers.insert(ObserverOf(observer)) subscriptions.append(Subscription(self.testScheduler.now)) diff --git a/RxTests/RxSwiftTests/TestImplementations/Mocks/HotObservable.swift b/RxTests/RxSwiftTests/TestImplementations/Mocks/HotObservable.swift index b79d11f2..fc1b644c 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Mocks/HotObservable.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Mocks/HotObservable.swift @@ -9,7 +9,8 @@ import Foundation import RxSwift -class HotObservable : Observable { +class HotObservable : ObservableType, ObservableConvertibleType { + typealias E = Element typealias Events = Recorded typealias Observer = ObserverOf @@ -27,8 +28,6 @@ class HotObservable : Observable { self.subscriptions = [] self.observers = Bag() - super.init() - for recordedEvent in recordedEvents { testScheduler.schedule((), time: recordedEvent.time) { t in self.observers.forEach { $0.on(recordedEvent.event) } @@ -37,7 +36,7 @@ class HotObservable : Observable { } } - override func subscribe(observer: O) -> Disposable { + func subscribe(observer: O) -> Disposable { let key = observers.insert(ObserverOf(observer)) subscriptions.append(Subscription(self.testScheduler.now)) diff --git a/RxTests/RxSwiftTests/TestImplementations/Mocks/MySubject.swift b/RxTests/RxSwiftTests/TestImplementations/Mocks/MySubject.swift index 5c66e331..e1e35c76 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Mocks/MySubject.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Mocks/MySubject.swift @@ -9,7 +9,8 @@ import Foundation import RxSwift -class MySubject : Observable, SubjectType, ObserverType { +class MySubject : SubjectType, ObserverType { + typealias E = Element typealias SubjectObserverType = MySubject var _disposeOn: [Element : Disposable] = [:] @@ -17,10 +18,6 @@ class MySubject : Observable, Subject var _subscribeCount: Int = 0 var _disposed: Bool = false - override init() { - super.init() - } - var subscribeCount: Int { get { return _subscribeCount @@ -48,7 +45,7 @@ class MySubject : Observable, Subject } } - override func subscribe(observer: O) -> Disposable { + func subscribe(observer: O) -> Disposable { _subscribeCount++ _observer = ObserverOf(observer) diff --git a/RxTests/RxSwiftTests/TestImplementations/Mocks/PrimitiveHotObservable.swift b/RxTests/RxSwiftTests/TestImplementations/Mocks/PrimitiveHotObservable.swift index c110dc6f..f094f193 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Mocks/PrimitiveHotObservable.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Mocks/PrimitiveHotObservable.swift @@ -12,25 +12,25 @@ import RxSwift let SubscribedToHotObservable = Subscription(0) let UnsunscribedFromHotObservable = Subscription(0, 0) -class PrimitiveHotObservable : Observable, ObserverType { +class PrimitiveHotObservable : ObservableType { + typealias E = ElementType + typealias Events = Recorded typealias Observer = ObserverOf var subscriptions: [Subscription] var observers: Bag> - override init() { + init() { self.subscriptions = [] self.observers = Bag() - - super.init() } func on(event: Event) { observers.forEach { $0.on(event) } } - override func subscribe(observer: O) -> Disposable { + func subscribe(observer: O) -> Disposable { let key = observers.insert(ObserverOf(observer)) subscriptions.append(SubscribedToHotObservable) diff --git a/RxTests/RxSwiftTests/TestImplementations/Mocks/TestConnectableObservable.swift b/RxTests/RxSwiftTests/TestImplementations/Mocks/TestConnectableObservable.swift index ef7f2f57..f528584f 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Mocks/TestConnectableObservable.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Mocks/TestConnectableObservable.swift @@ -9,20 +9,20 @@ import Foundation import RxSwift -class TestConnectableObservable : Observable, ConnectableObservableType { +class TestConnectableObservable : ConnectableObservableType { + typealias E = S.E let _o: ConnectableObservable init(o: Observable, s: S) { _o = o.multicast(s) - super.init() } func connect() -> Disposable { return _o.connect() } - override func subscribe(observer: O) -> Disposable { + func subscribe(observer: O) -> Disposable { return _o.subscribe(observer) } } \ No newline at end of file diff --git a/RxTests/RxSwiftTests/TestImplementations/Observable+Extensions.swift b/RxTests/RxSwiftTests/TestImplementations/Observable+Extensions.swift index 01c97905..a468879f 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Observable+Extensions.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Observable+Extensions.swift @@ -11,4 +11,20 @@ import RxSwift public func == (lhs: Observable, rhs: Observable) -> Bool { return lhs === rhs +} + +extension HotObservable : Equatable { + +} + +extension ColdObservable : Equatable { + +} + +func == (lhs: HotObservable, rhs: HotObservable) -> Bool { + return lhs === rhs +} + +func == (lhs: ColdObservable, rhs: ColdObservable) -> Bool { + return lhs === rhs } \ No newline at end of file diff --git a/RxTests/RxSwiftTests/Tests/AssumptionsTest.swift b/RxTests/RxSwiftTests/Tests/AssumptionsTest.swift index 14741ca8..019569c4 100644 --- a/RxTests/RxSwiftTests/Tests/AssumptionsTest.swift +++ b/RxTests/RxSwiftTests/Tests/AssumptionsTest.swift @@ -94,7 +94,7 @@ class AssumptionsTest : RxTest { #if TRACE_RESOURCES let startResourceCount = resourceCount - var observable: Observable! = Observable() + var observable: Observable! = just(1) XCTAssertTrue(observable != nil) XCTAssertEqual(resourceCount, startResourceCount + 1) diff --git a/RxTests/RxSwiftTests/Tests/DisposableTest.swift b/RxTests/RxSwiftTests/Tests/DisposableTest.swift index 9bc6bd3a..4a309809 100644 --- a/RxTests/RxSwiftTests/Tests/DisposableTest.swift +++ b/RxTests/RxSwiftTests/Tests/DisposableTest.swift @@ -52,7 +52,7 @@ class DisposableTest : RxTest { ]) let res = scheduler.start(400) { () -> Observable in - return xs + return xs.asObservable() } XCTAssertEqual(res.messages, [ diff --git a/RxTests/RxSwiftTests/Tests/Observable+BindingTest.swift b/RxTests/RxSwiftTests/Tests/Observable+BindingTest.swift index 1809ca45..f99e6c01 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+BindingTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+BindingTest.swift @@ -22,7 +22,7 @@ extension ObservableBindingTest { var nEvents = 0 let observable = TestConnectableObservable(o: sequenceOf(0, 1, 2), s: subject) - let d = observable .subscribeNext { n in + let d = observable.subscribeNext { n in nEvents++ } @@ -105,7 +105,7 @@ extension ObservableBindingTest { let subject = MySubject() - let conn = TestConnectableObservable(o: xs, s: subject) + let conn = TestConnectableObservable(o: xs.asObservable(), s: subject) let res = scheduler.start { conn.refCount() } @@ -262,94 +262,6 @@ extension ObservableBindingTest { // replay extension ObservableBindingTest { - func testReplay_DeadlockSimple() { - var nEvents = 0 - - let observable = sequenceOf(0, 1, 2).replay(3).refCount() - _ = observable.subscribeNext { n in - nEvents++ - } - - XCTAssertEqual(nEvents, 3) - } - - func testReplay_DeadlockErrorAfterN() { - var nEvents = 0 - - let observable = [sequenceOf(0, 1, 2), failWith(testError)].concat().replay(3).refCount() - _ = observable.subscribeError { n in - nEvents++ - } - - XCTAssertEqual(nEvents, 1) - } - - func testReplay_DeadlockErrorImmediatelly() { - var nEvents = 0 - - let observable: Observable = failWith(testError).replay(3).refCount() - _ = observable.subscribeError { n in - nEvents++ - } - - XCTAssertEqual(nEvents, 1) - } - - func testReplay_DeadlockEmpty() { - var nEvents = 0 - - let observable: Observable = empty().replay(3).refCount() - _ = observable.subscribeCompleted { - nEvents++ - } - - XCTAssertEqual(nEvents, 1) - } - - func testReplay1_DeadlockSimple() { - var nEvents = 0 - - let observable = sequenceOf(0, 1, 2).replay(1).refCount() - _ = observable.subscribeNext { n in - nEvents++ - } - - XCTAssertEqual(nEvents, 3) - } - - func testReplay1_DeadlockErrorAfterN() { - var nEvents = 0 - - let observable = [just(0, 1, 2), failWith(testError)].concat().replay(1).refCount() - _ = observable.subscribeError { n in - nEvents++ - } - - XCTAssertEqual(nEvents, 1) - } - - func testReplay1_DeadlockErrorImmediatelly() { - var nEvents = 0 - - let observable: Observable = failWith(testError).replay(1).refCount() - _ = observable.subscribeError { n in - nEvents++ - } - - XCTAssertEqual(nEvents, 1) - } - - func testReplay1_DeadlockEmpty() { - var nEvents = 0 - - let observable: Observable = empty().replay(1).refCount() - _ = observable.subscribeCompleted { - nEvents++ - } - - XCTAssertEqual(nEvents, 1) - } - func testReplayCount_Basic() { let scheduler = TestScheduler(initialClock: 0) @@ -764,3 +676,320 @@ extension ObservableBindingTest { ]) } } + + +// shareReplay(1) +extension ObservableBindingTest { + func _testIdenticalBehaviorOfShareReplayOptimizedAndComposed(action: (transform: (Observable -> Observable)) -> Void) { + action { $0.shareReplay(1) } + action { $0.replay(1).refCount() } + } + + func testShareReplay_DeadlockImmediatelly() { + _testIdenticalBehaviorOfShareReplayOptimizedAndComposed { transform in + var nEvents = 0 + + let observable = transform(sequenceOf(0, 1, 2)) + _ = observable.subscribeNext { n in + nEvents++ + } + + XCTAssertEqual(nEvents, 3) + } + } + + func testShareReplay_DeadlockEmpty() { + _testIdenticalBehaviorOfShareReplayOptimizedAndComposed { transform in + var nEvents = 0 + + let observable = transform(empty()) + _ = observable.subscribeCompleted { n in + nEvents++ + } + + XCTAssertEqual(nEvents, 1) + } + } + + func testShareReplay_DeadlockError() { + _testIdenticalBehaviorOfShareReplayOptimizedAndComposed { transform in + var nEvents = 0 + + let observable = transform(failWith(testError)) + _ = observable.subscribeError { _ in + nEvents++ + } + + XCTAssertEqual(nEvents, 1) + } + } + + func testShareReplay1_DeadlockErrorAfterN() { + _testIdenticalBehaviorOfShareReplayOptimizedAndComposed { transform in + var nEvents = 0 + + let observable = transform([sequenceOf(0, 1, 2), failWith(testError)].concat()) + _ = observable.subscribeError { n in + nEvents++ + } + + XCTAssertEqual(nEvents, 1) + } + } + + func testShareReplay1_Basic() { + _testIdenticalBehaviorOfShareReplayOptimizedAndComposed { transform in + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(110, 7), + next(220, 3), + next(280, 4), + next(290, 1), + next(340, 8), + next(360, 5), + next(370, 6), + next(390, 7), + next(410, 13), + next(430, 2), + next(450, 9), + next(520, 11), + next(560, 20), + error(600, testError) + ]) + + var ys: Observable! = nil + + var subscription1: Disposable! = nil + var subscription2: Disposable! = nil + + let res1: MockObserver = scheduler.createObserver() + let res2: MockObserver = scheduler.createObserver() + + scheduler.scheduleAt(Defaults.created) { ys = transform(xs.asObservable()) } + + scheduler.scheduleAt(335) { subscription1 = ys.subscribe(res1) } + scheduler.scheduleAt(400) { subscription1.dispose() } + + scheduler.scheduleAt(355) { subscription2 = ys.subscribe(res2) } + scheduler.scheduleAt(415) { subscription2.dispose() } + + scheduler.scheduleAt(440) { subscription1 = ys.subscribe(res1) } + scheduler.scheduleAt(455) { subscription1.dispose() } + + scheduler.start(); + + XCTAssertEqual(res1.messages, [ + // 1rt batch + next(340, 8), + next(360, 5), + next(370, 6), + next(390, 7), + + // 2nd batch + next(440, 13), + next(450, 9) + ]) + + XCTAssertEqual(res2.messages, [ + next(355, 8), + next(360, 5), + next(370, 6), + next(390, 7), + next(410, 13) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(335, 415), + Subscription(440, 455) + ]) + } + } + + func testShareReplay1_Error() { + _testIdenticalBehaviorOfShareReplayOptimizedAndComposed { transform in + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(110, 7), + next(220, 3), + next(280, 4), + next(290, 1), + next(340, 8), + next(360, 5), + error(365, testError), + next(370, 6), + next(390, 7), + next(410, 13), + next(430, 2), + next(450, 9), + next(520, 11), + next(560, 20), + ]) + + var ys: Observable! = nil + + var subscription1: Disposable! = nil + var subscription2: Disposable! = nil + + let res1: MockObserver = scheduler.createObserver() + let res2: MockObserver = scheduler.createObserver() + + scheduler.scheduleAt(Defaults.created) { ys = transform(xs.asObservable()) } + + scheduler.scheduleAt(335) { subscription1 = ys.subscribe(res1) } + scheduler.scheduleAt(400) { subscription1.dispose() } + + scheduler.scheduleAt(355) { subscription2 = ys.subscribe(res2) } + scheduler.scheduleAt(415) { subscription2.dispose() } + + scheduler.scheduleAt(440) { subscription1 = ys.subscribe(res1) } + scheduler.scheduleAt(455) { subscription1.dispose() } + + scheduler.start(); + + XCTAssertEqual(res1.messages, [ + // 1rt batch + next(340, 8), + next(360, 5), + error(365, testError), + + // 2nd batch + next(440, 5), + error(440, testError), + ]) + + XCTAssertEqual(res2.messages, [ + next(355, 8), + next(360, 5), + error(365, testError), + ]) + + // unoptimized version of replay subject will make a subscription and kill it immediatelly + XCTAssertEqual(xs.subscriptions[0], Subscription(335, 365)) + XCTAssertTrue(xs.subscriptions.count <= 2) + XCTAssertTrue(xs.subscriptions.count == 1 || xs.subscriptions[1] == Subscription(440, 440)) + } + } + + func testShareReplay1_Completed() { + _testIdenticalBehaviorOfShareReplayOptimizedAndComposed { transform in + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(110, 7), + next(220, 3), + next(280, 4), + next(290, 1), + next(340, 8), + next(360, 5), + completed(365), + next(370, 6), + next(390, 7), + next(410, 13), + next(430, 2), + next(450, 9), + next(520, 11), + next(560, 20), + ]) + + var ys: Observable! = nil + + var subscription1: Disposable! = nil + var subscription2: Disposable! = nil + + let res1: MockObserver = scheduler.createObserver() + let res2: MockObserver = scheduler.createObserver() + + scheduler.scheduleAt(Defaults.created) { ys = transform(xs.asObservable()) } + + scheduler.scheduleAt(335) { subscription1 = ys.subscribe(res1) } + scheduler.scheduleAt(400) { subscription1.dispose() } + + scheduler.scheduleAt(355) { subscription2 = ys.subscribe(res2) } + scheduler.scheduleAt(415) { subscription2.dispose() } + + scheduler.scheduleAt(440) { subscription1 = ys.subscribe(res1) } + scheduler.scheduleAt(455) { subscription1.dispose() } + + scheduler.start(); + + XCTAssertEqual(res1.messages, [ + // 1rt batch + next(340, 8), + next(360, 5), + completed(365), + + // 2nd batch + next(440, 5), + completed(440) + ]) + + XCTAssertEqual(res2.messages, [ + next(355, 8), + next(360, 5), + completed(365) + ]) + + // unoptimized version of replay subject will make a subscription and kill it immediatelly + XCTAssertEqual(xs.subscriptions[0], Subscription(335, 365)) + XCTAssertTrue(xs.subscriptions.count <= 2) + XCTAssertTrue(xs.subscriptions.count == 1 || xs.subscriptions[1] == Subscription(440, 440)) + } + } + + func testShareReplay1_Canceled() { + _testIdenticalBehaviorOfShareReplayOptimizedAndComposed { transform in + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + completed(365), + next(370, 6), + next(390, 7), + next(410, 13), + next(430, 2), + next(450, 9), + next(520, 11), + next(560, 20), + ]) + + var ys: Observable! = nil + + var subscription1: Disposable! = nil + var subscription2: Disposable! = nil + + let res1: MockObserver = scheduler.createObserver() + let res2: MockObserver = scheduler.createObserver() + + scheduler.scheduleAt(Defaults.created) { ys = transform(xs.asObservable()) } + + scheduler.scheduleAt(335) { subscription1 = ys.subscribe(res1) } + scheduler.scheduleAt(400) { subscription1.dispose() } + + scheduler.scheduleAt(355) { subscription2 = ys.subscribe(res2) } + scheduler.scheduleAt(415) { subscription2.dispose() } + + scheduler.scheduleAt(440) { subscription1 = ys.subscribe(res1) } + scheduler.scheduleAt(455) { subscription1.dispose() } + + scheduler.start(); + + XCTAssertEqual(res1.messages, [ + // 1rt batch + completed(365), + + // 2nd batch + completed(440) + ]) + + XCTAssertEqual(res2.messages, [ + completed(365) + ]) + + // unoptimized version of replay subject will make a subscription and kill it immediatelly + XCTAssertEqual(xs.subscriptions[0], Subscription(335, 365)) + XCTAssertTrue(xs.subscriptions.count <= 2) + XCTAssertTrue(xs.subscriptions.count == 1 || xs.subscriptions[1] == Subscription(440, 440)) + } + } +} \ No newline at end of file diff --git a/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift b/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift index 7de57089..7b424157 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift @@ -42,7 +42,7 @@ extension ObservableMultipleTest { let res = scheduler.start { o1.catchError { e in handlerCalled = scheduler.clock - return o2 + return o2.asObservable() } } @@ -419,7 +419,7 @@ extension ObservableMultipleTest { completed(150) ]) - let xSequence: [Recorded>] = [ + let xSequence: [Recorded>] = [ next(300, ys1), next(400, ys2), next(500, ys3), @@ -502,7 +502,7 @@ extension ObservableMultipleTest { completed(150) ]) - let xSequence: [Recorded>] = [ + let xSequence: [Recorded>] = [ next(300, ys1), next(400, ys2), next(500, ys3), @@ -572,7 +572,7 @@ extension ObservableMultipleTest { completed(50) ]) - let xSequence: [Recorded>] = [ + let xSequence: [Recorded>] = [ next(300, ys1), next(400, ys2), error(500, testError) @@ -1170,7 +1170,7 @@ extension ObservableMultipleTest { ]) let res = scheduler.start { - [xs1, xs2, xs3, xs2].concat() + [xs1.asObservable(), xs2.asObservable(), xs3.asObservable(), xs2.asObservable()].concat() } let messages: [Recorded] = [ @@ -1376,7 +1376,7 @@ extension ObservableMultipleTest { completed(150) ]) - let xs: Observable> = scheduler.createHotObservable([ + let xs: HotObservable> = scheduler.createHotObservable([ next(300, ys1), next(400, ys2), next(500, ys3), @@ -1446,7 +1446,7 @@ extension ObservableMultipleTest { completed(50) ]) - let xs: HotObservable> = scheduler.createHotObservable([ + let xs: HotObservable> = scheduler.createHotObservable([ next(300, ys1), next(400, ys2), next(500, ys3), @@ -1519,7 +1519,7 @@ extension ObservableMultipleTest { completed(150) ]) - let xs: HotObservable> = scheduler.createHotObservable([ + let xs: HotObservable> = scheduler.createHotObservable([ next(300, ys1), next(400, ys2), next(500, ys3), @@ -1581,7 +1581,7 @@ extension ObservableMultipleTest { completed(50) ]) - let xs: HotObservable> = scheduler.createHotObservable([ + let xs: HotObservable> = scheduler.createHotObservable([ next(300, ys1), next(400, ys2), error(500, testError1), @@ -1647,7 +1647,7 @@ extension ObservableMultipleTest { completed(300) ]) - let xs: HotObservable> = scheduler.createHotObservable([ + let xs: HotObservable> = scheduler.createHotObservable([ next(210, ys1), next(260, ys2), next(270, ys3), @@ -1725,7 +1725,7 @@ extension ObservableMultipleTest { completed(300) ]) - let xs: HotObservable> = scheduler.createHotObservable([ + let xs: HotObservable> = scheduler.createHotObservable([ next(210, ys1), next(260, ys2), next(270, ys3), @@ -1803,7 +1803,7 @@ extension ObservableMultipleTest { completed(300) ]) - let xs: HotObservable> = scheduler.createHotObservable([ + let xs: HotObservable> = scheduler.createHotObservable([ next(210, ys1), next(260, ys2), next(270, ys3), @@ -1881,7 +1881,7 @@ extension ObservableMultipleTest { completed(300) ]) - let xs: HotObservable> = scheduler.createHotObservable([ + let xs: HotObservable> = scheduler.createHotObservable([ next(210, ys1), next(260, ys2), next(270, ys3), @@ -1959,7 +1959,7 @@ extension ObservableMultipleTest { completed(300) ]) - let xs: HotObservable> = scheduler.createHotObservable([ + let xs: HotObservable> = scheduler.createHotObservable([ next(210, ys1), next(260, ys2), next(270, ys3), @@ -2032,7 +2032,7 @@ extension ObservableMultipleTest { completed(300) ]) - let xs: HotObservable> = scheduler.createHotObservable([ + let xs: HotObservable> = scheduler.createHotObservable([ next(210, ys1), next(260, ys2), next(270, ys3), @@ -2105,7 +2105,7 @@ extension ObservableMultipleTest { completed(300) ]) - let xs: HotObservable> = scheduler.createHotObservable([ + let xs: HotObservable> = scheduler.createHotObservable([ next(210, ys1), next(260, ys2), next(270, ys3), diff --git a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift index 83194fc5..fa6ff595 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift @@ -1604,7 +1604,7 @@ extension ObservableStandardSequenceOperators { var invoked = 0 let res = scheduler.start { - return xs.flatMap { (x: ColdObservable) -> Observable in + return xs.flatMap { (x: ColdObservable) -> ColdObservable in invoked++ if invoked == 3 { throw testError @@ -2273,7 +2273,7 @@ extension ObservableStandardSequenceOperators { var invoked = 0 let res = scheduler.start { - return xs.flatMapWithIndex { (x: ColdObservable, _: Int) -> Observable in + return xs.flatMapWithIndex { (x: ColdObservable, _: Int) -> ColdObservable in invoked++ if invoked == 3 { throw testError From af463f15876a49de557bccb254cda19b80367691 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Tue, 13 Oct 2015 23:00:28 +0200 Subject: [PATCH 033/210] Adds Observable+Extensions tests. --- .../Tests/Observable+SubscriptionTest.swift | 209 ++++++++++++++++++ RxTests/RxTests.xcodeproj/project.pbxproj | 8 + 2 files changed, 217 insertions(+) create mode 100644 RxTests/RxSwiftTests/Tests/Observable+SubscriptionTest.swift diff --git a/RxTests/RxSwiftTests/Tests/Observable+SubscriptionTest.swift b/RxTests/RxSwiftTests/Tests/Observable+SubscriptionTest.swift new file mode 100644 index 00000000..6d1bc977 --- /dev/null +++ b/RxTests/RxSwiftTests/Tests/Observable+SubscriptionTest.swift @@ -0,0 +1,209 @@ +// +// Observable+SubscriptionTest.swift +// RxTests +// +// Created by Krunoslav Zaher on 10/13/15. +// +// + +import Foundation +import RxSwift +import XCTest + +class ObservableSubscriptionTests : RxTest { + func testSubscribeOnNext() { + let publishSubject = PublishSubject() + + var onNextCalled = 0 + var onErrorCalled = 0 + var onCompletedCalled = 0 + var onDisposedCalled = 0 + + var lastElement: Int? = nil + var lastError: ErrorType? = nil + + let subscription = publishSubject.subscribe(onNext: { n in + lastElement = n + onNextCalled++ + }, onError: { e in + lastError = e + onErrorCalled++ + }, onCompleted: { + onCompletedCalled++ + }, onDisposed: { + onDisposedCalled++ + }) + + XCTAssertTrue(lastElement == nil) + XCTAssertTrue(lastError == nil) + XCTAssertTrue(onNextCalled == 0) + XCTAssertTrue(onErrorCalled == 0) + XCTAssertTrue(onCompletedCalled == 0) + XCTAssertTrue(onDisposedCalled == 0) + + publishSubject.on(.Next(1)) + + XCTAssertTrue(lastElement == 1) + XCTAssertTrue(lastError == nil) + XCTAssertTrue(onNextCalled == 1) + XCTAssertTrue(onErrorCalled == 0) + XCTAssertTrue(onCompletedCalled == 0) + XCTAssertTrue(onDisposedCalled == 0) + + subscription.dispose() + publishSubject.on(.Next(2)) + + XCTAssertTrue(lastElement == 1) + XCTAssertTrue(lastError == nil) + XCTAssertTrue(onNextCalled == 1) + XCTAssertTrue(onErrorCalled == 0) + XCTAssertTrue(onCompletedCalled == 0) + XCTAssertTrue(onDisposedCalled == 1) + } + + func testSubscribeOnError() { + let publishSubject = PublishSubject() + + var onNextCalled = 0 + var onErrorCalled = 0 + var onCompletedCalled = 0 + var onDisposedCalled = 0 + + var lastElement: Int? = nil + var lastError: ErrorType? = nil + + let subscription = publishSubject.subscribe(onNext: { n in + lastElement = n + onNextCalled++ + }, onError: { e in + lastError = e + onErrorCalled++ + }, onCompleted: { + onCompletedCalled++ + }, onDisposed: { + onDisposedCalled++ + }) + + XCTAssertTrue(lastElement == nil) + XCTAssertTrue(lastError == nil) + XCTAssertTrue(onNextCalled == 0) + XCTAssertTrue(onErrorCalled == 0) + XCTAssertTrue(onCompletedCalled == 0) + XCTAssertTrue(onDisposedCalled == 0) + + publishSubject.on(.Error(testError)) + + XCTAssertTrue(lastElement == nil) + XCTAssertTrue((lastError as? NSError) === testError) + XCTAssertTrue(onNextCalled == 0) + XCTAssertTrue(onErrorCalled == 1) + XCTAssertTrue(onCompletedCalled == 0) + XCTAssertTrue(onDisposedCalled == 1) + + subscription.dispose() + publishSubject.on(.Next(2)) + publishSubject.on(.Completed) + + XCTAssertTrue(lastElement == nil) + XCTAssertTrue((lastError as? NSError) === testError) + XCTAssertTrue(onNextCalled == 0) + XCTAssertTrue(onErrorCalled == 1) + XCTAssertTrue(onCompletedCalled == 0) + XCTAssertTrue(onDisposedCalled == 1) + } + + func testSubscribeOnCompleted() { + let publishSubject = PublishSubject() + + var onNextCalled = 0 + var onErrorCalled = 0 + var onCompletedCalled = 0 + var onDisposedCalled = 0 + + var lastElement: Int? = nil + var lastError: ErrorType? = nil + + let subscription = publishSubject.subscribe(onNext: { n in + lastElement = n + onNextCalled++ + }, onError: { e in + lastError = e + onErrorCalled++ + }, onCompleted: { + onCompletedCalled++ + }, onDisposed: { + onDisposedCalled++ + }) + + XCTAssertTrue(lastElement == nil) + XCTAssertTrue(lastError == nil) + XCTAssertTrue(onNextCalled == 0) + XCTAssertTrue(onErrorCalled == 0) + XCTAssertTrue(onCompletedCalled == 0) + XCTAssertTrue(onDisposedCalled == 0) + + publishSubject.on(.Completed) + + XCTAssertTrue(lastElement == nil) + XCTAssertTrue(lastError == nil) + XCTAssertTrue(onNextCalled == 0) + XCTAssertTrue(onErrorCalled == 0) + XCTAssertTrue(onCompletedCalled == 1) + XCTAssertTrue(onDisposedCalled == 1) + + subscription.dispose() + publishSubject.on(.Next(2)) + publishSubject.on(.Error(testError)) + + XCTAssertTrue(lastElement == nil) + XCTAssertTrue(lastError == nil) + XCTAssertTrue(onNextCalled == 0) + XCTAssertTrue(onErrorCalled == 0) + XCTAssertTrue(onCompletedCalled == 1) + XCTAssertTrue(onDisposedCalled == 1) + } + + func testDisposed() { + let publishSubject = PublishSubject() + + var onNextCalled = 0 + var onErrorCalled = 0 + var onCompletedCalled = 0 + var onDisposedCalled = 0 + + var lastElement: Int? = nil + var lastError: ErrorType? = nil + + let subscription = publishSubject.subscribe(onNext: { n in + lastElement = n + onNextCalled++ + }, onError: { e in + lastError = e + onErrorCalled++ + }, onCompleted: { + onCompletedCalled++ + }, onDisposed: { + onDisposedCalled++ + }) + + XCTAssertTrue(lastElement == nil) + XCTAssertTrue(lastError == nil) + XCTAssertTrue(onNextCalled == 0) + XCTAssertTrue(onErrorCalled == 0) + XCTAssertTrue(onCompletedCalled == 0) + XCTAssertTrue(onDisposedCalled == 0) + + publishSubject.on(.Next(1)) + subscription.dispose() + publishSubject.on(.Next(2)) + publishSubject.on(.Error(testError)) + publishSubject.on(.Completed) + + XCTAssertTrue(lastElement == 1) + XCTAssertTrue(lastError == nil) + XCTAssertTrue(onNextCalled == 1) + XCTAssertTrue(onErrorCalled == 0) + XCTAssertTrue(onCompletedCalled == 0) + XCTAssertTrue(onDisposedCalled == 1) + } +} \ No newline at end of file diff --git a/RxTests/RxTests.xcodeproj/project.pbxproj b/RxTests/RxTests.xcodeproj/project.pbxproj index 2362c406..7b6f4428 100644 --- a/RxTests/RxTests.xcodeproj/project.pbxproj +++ b/RxTests/RxTests.xcodeproj/project.pbxproj @@ -87,6 +87,9 @@ C897EC3C1B10E000009C2CB0 /* BehaviorSubjectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C897EC3A1B10E000009C2CB0 /* BehaviorSubjectTest.swift */; }; C897EC4A1B1123DA009C2CB0 /* Observable+MultipleTest+Zip.swift in Sources */ = {isa = PBXBuildFile; fileRef = C897EC491B1123DA009C2CB0 /* Observable+MultipleTest+Zip.swift */; }; C897EC4B1B1123DA009C2CB0 /* Observable+MultipleTest+Zip.swift in Sources */ = {isa = PBXBuildFile; fileRef = C897EC491B1123DA009C2CB0 /* Observable+MultipleTest+Zip.swift */; }; + C89CDB961BCDA1F1002063D9 /* Observable+SubscriptionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89CDB951BCDA1F1002063D9 /* Observable+SubscriptionTest.swift */; }; + C89CDB971BCDA1F1002063D9 /* Observable+SubscriptionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89CDB951BCDA1F1002063D9 /* Observable+SubscriptionTest.swift */; }; + C89CDB981BCDA1F1002063D9 /* Observable+SubscriptionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89CDB951BCDA1F1002063D9 /* Observable+SubscriptionTest.swift */; }; C8A468CD1B8A897800BF917B /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468CC1B8A897800BF917B /* RxCocoa.framework */; }; C8A468CF1B8A897D00BF917B /* RxBlocking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468CE1B8A897D00BF917B /* RxBlocking.framework */; }; C8A468D01B8A899C00BF917B /* RxBlocking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468CE1B8A897D00BF917B /* RxBlocking.framework */; }; @@ -207,6 +210,7 @@ C897EC3A1B10E000009C2CB0 /* BehaviorSubjectTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BehaviorSubjectTest.swift; sourceTree = ""; }; C897EC461B112070009C2CB0 /* Observable+MultipleTest+Zip.tt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Observable+MultipleTest+Zip.tt"; sourceTree = ""; }; C897EC491B1123DA009C2CB0 /* Observable+MultipleTest+Zip.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+MultipleTest+Zip.swift"; sourceTree = ""; }; + C89CDB951BCDA1F1002063D9 /* Observable+SubscriptionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+SubscriptionTest.swift"; sourceTree = ""; }; C8A468CC1B8A897800BF917B /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8A468CE1B8A897D00BF917B /* RxBlocking.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = RxBlocking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8AF26EE1B499E5C00131C03 /* DelegateProxyTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxyTest.swift; sourceTree = ""; }; @@ -394,6 +398,7 @@ C811084B1AF50E2A001C13E4 /* QueueTests.swift */, C814CEA21AF5622600E98087 /* VariableTest.swift */, C69B64FF1BA88FAC00A7FA73 /* ObserverTests.swift */, + C89CDB951BCDA1F1002063D9 /* Observable+SubscriptionTest.swift */, ); path = Tests; sourceTree = ""; @@ -558,6 +563,7 @@ C8B787FA1AF55CDE00206D02 /* Observable+ConcurrencyTest.swift in Sources */, C81108691AF50E2A001C13E4 /* RxTest.swift in Sources */, C81108671AF50E2A001C13E4 /* Observable+TimeTest.swift in Sources */, + C89CDB961BCDA1F1002063D9 /* Observable+SubscriptionTest.swift in Sources */, C868D0F91BB76A29003D1474 /* PerformanceTools.swift in Sources */, C81108551AF50E2A001C13E4 /* TestObservable.swift in Sources */, C8E3812B1B2083C2008CDC33 /* PrimitiveMockObserver.swift in Sources */, @@ -641,6 +647,7 @@ C801EB5B1B97951100C4D8C4 /* Observable+CreationTest.swift in Sources */, C88BB8A21B07E64B0064D411 /* ColdObservable.swift in Sources */, C85F4E441B7F70EA00A866C7 /* CompositeObserverTest.swift in Sources */, + C89CDB971BCDA1F1002063D9 /* Observable+SubscriptionTest.swift in Sources */, C88BB8A31B07E64B0064D411 /* HotObservable.swift in Sources */, C8FDC5F91B2B5B7E0065F8D9 /* ElementIndexPair.swift in Sources */, C88BB8A41B07E64B0064D411 /* Observable.Extensions.swift in Sources */, @@ -673,6 +680,7 @@ D2EBEB5A1BB9B7CC003A27DC /* PrimitiveHotObservable.swift in Sources */, D2EBEB521BB9B7CC003A27DC /* ColdObservable.swift in Sources */, D2EBEB551BB9B7CC003A27DC /* MockObserver.swift in Sources */, + C89CDB981BCDA1F1002063D9 /* Observable+SubscriptionTest.swift in Sources */, D2EBEB6F1BB9B7EF003A27DC /* Observable+CreationTest.swift in Sources */, D203C4EC1BB9C22800D02D00 /* UI+RxTests.swift in Sources */, D2EBEB591BB9B7CC003A27DC /* TestObserver.swift in Sources */, From 97f805ce4f97dad86443b3ebeb933c5d7f207e69 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Tue, 13 Oct 2015 23:11:28 +0200 Subject: [PATCH 034/210] Adds excluded files. --- RxExample/RxExample.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index 3c8c5283..062a608e 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 07E300071B14995F00F00100 /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E300061B14995F00F00100 /* TableViewController.swift */; }; 07E300091B149A2A00F00100 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E300081B149A2A00F00100 /* User.swift */; }; 07E3C2331B03605B0010338D /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E3C2321B03605B0010338D /* Dependencies.swift */; }; + C80DDE881BCDAA0F006A1832 /* SkipWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE7A1BCDA952006A1832 /* SkipWhile.swift */; }; C8297E2F1B6CF905000589EA /* RxTableViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88C78631B3EB0A00061C5AB /* RxTableViewSectionedAnimatedDataSource.swift */; }; C8297E301B6CF905000589EA /* RandomUserAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0706E19E1B17703E00BA2D3A /* RandomUserAPI.swift */; }; C8297E311B6CF905000589EA /* SearchResultViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86E2F321AE5A0CA00C31024 /* SearchResultViewModel.swift */; }; @@ -438,6 +439,7 @@ 07E300061B14995F00F00100 /* TableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; }; 07E300081B149A2A00F00100 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 07E3C2321B03605B0010338D /* Dependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Dependencies.swift; path = Examples/Dependencies.swift; sourceTree = ""; }; + C80DDE7A1BCDA952006A1832 /* SkipWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipWhile.swift; sourceTree = ""; }; C81B39F11BC1C28400EF5A9F /* Rx.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Rx.xcodeproj; path = ../Rx.xcodeproj; sourceTree = ""; }; C8297E691B6CF905000589EA /* RxExample-iOS-no-module.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "RxExample-iOS-no-module.app"; sourceTree = BUILT_PRODUCTS_DIR; }; C83366DD1AD0293800C668A7 /* RxExample-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "RxExample-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1086,6 +1088,7 @@ C894646C1BC6C2B00055219D /* Sink.swift */, C894646D1BC6C2B00055219D /* Skip.swift */, C89CDB621BCC45DC002063D9 /* SkipUntil.swift */, + C80DDE7A1BCDA952006A1832 /* SkipWhile.swift */, C894646E1BC6C2B00055219D /* StartWith.swift */, C894646F1BC6C2B00055219D /* SubscribeOn.swift */, C89464701BC6C2B00055219D /* Switch.swift */, @@ -1593,6 +1596,7 @@ C89464EA1BC6C2B00055219D /* Zip.swift in Sources */, C89464E51BC6C2B00055219D /* Throttle.swift in Sources */, C89465831BC6C2BC0055219D /* RxTableViewDataSourceType.swift in Sources */, + C80DDE881BCDAA0F006A1832 /* SkipWhile.swift in Sources */, C89464ED1BC6C2B00055219D /* Observable+Concurrency.swift in Sources */, C89464DA1BC6C2B00055219D /* Repeat.swift in Sources */, C89465651BC6C2BC0055219D /* CLLocationManager+Rx.swift in Sources */, From 89d9094521a834e928d877c2cd12bd9c85577415 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Wed, 14 Oct 2015 10:41:44 +0200 Subject: [PATCH 035/210] Moves `subscribeOn` from `Driver` to `ControlProperty`. --- RxCocoa/Common/CocoaUnits/ControlEvent.swift | 2 +- RxCocoa/Common/CocoaUnits/ControlProperty.swift | 2 +- RxCocoa/Common/CocoaUnits/Driver.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/RxCocoa/Common/CocoaUnits/ControlEvent.swift b/RxCocoa/Common/CocoaUnits/ControlEvent.swift index 6b45100c..ac92d4c4 100644 --- a/RxCocoa/Common/CocoaUnits/ControlEvent.swift +++ b/RxCocoa/Common/CocoaUnits/ControlEvent.swift @@ -39,7 +39,7 @@ public struct ControlEvent : ControlEventType { let source: Observable init(source: Observable) { - self.source = source + self.source = source.subscribeOn(MainScheduler.sharedInstance) } /** diff --git a/RxCocoa/Common/CocoaUnits/ControlProperty.swift b/RxCocoa/Common/CocoaUnits/ControlProperty.swift index 151ee797..4970fb14 100644 --- a/RxCocoa/Common/CocoaUnits/ControlProperty.swift +++ b/RxCocoa/Common/CocoaUnits/ControlProperty.swift @@ -41,7 +41,7 @@ public struct ControlProperty : ControlPropertyType { let observer: ObserverOf init(source: Observable, observer: ObserverOf) { - self.source = source + self.source = source.subscribeOn(MainScheduler.sharedInstance) self.observer = observer } diff --git a/RxCocoa/Common/CocoaUnits/Driver.swift b/RxCocoa/Common/CocoaUnits/Driver.swift index 74e14a3e..166a961a 100644 --- a/RxCocoa/Common/CocoaUnits/Driver.swift +++ b/RxCocoa/Common/CocoaUnits/Driver.swift @@ -54,7 +54,7 @@ public struct Driver : DriverConvertibleType { #endif public func asObservable() -> Observable { - return _source.subscribeOn(MainScheduler.sharedInstance) + return _source } public func asDriver() -> Driver { From 85cc137d32b311f9cf63d723be50be575f56bec9 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Wed, 14 Oct 2015 10:42:27 +0200 Subject: [PATCH 036/210] Moves redundant brackets from `subscribe`. --- RxSwift/Observable+Extensions.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RxSwift/Observable+Extensions.swift b/RxSwift/Observable+Extensions.swift index e0adffab..0f885d36 100644 --- a/RxSwift/Observable+Extensions.swift +++ b/RxSwift/Observable+Extensions.swift @@ -35,7 +35,7 @@ extension ObservableType { - returns: Subscription object used to unsubscribe from the observable sequence. */ @warn_unused_result - public func subscribe(onNext onNext: ((E) -> Void)? = nil, onError: ((ErrorType) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil) + public func subscribe(onNext onNext: (E -> Void)? = nil, onError: (ErrorType -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil) -> Disposable { let disposable: Disposable From eba73eec264b68112e5f9fc946c52d43e45845dc Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Wed, 14 Oct 2015 15:43:25 +0200 Subject: [PATCH 037/210] Adds `Zip` and `CombineLatest` to `Driver` unit. --- Rx.xcodeproj/project.pbxproj | 140 +++++---- .../{ => Driver}/ControlEvent+Driver.swift | 0 .../{ => Driver}/ControlProperty+Driver.swift | 0 .../Driver/Driver+Operators+arity.swift | 281 ++++++++++++++++++ .../Driver/Driver+Operators+arity.tt | 52 ++++ .../{ => Driver}/Driver+Operators.swift | 28 ++ .../{ => Driver}/Driver+Subscription.swift | 0 .../CocoaUnits/{ => Driver}/Driver.swift | 18 +- .../ObservableConvertibleType+Driver.swift | 0 RxExample/RxExample.xcodeproj/project.pbxproj | 64 ++-- .../Implementations/CombineLatest+arity.swift | 14 +- .../Implementations/CombineLatest+arity.tt | 2 +- .../Implementations/Zip+CollectionType.swift | 7 +- .../Implementations/Zip+arity.swift | 14 +- .../Observables/Implementations/Zip+arity.tt | 2 +- RxSwift/Observables/Observable+Multiple.swift | 2 +- RxTests/RxSwiftTests/Tests/VariableTest.swift | 2 +- 17 files changed, 514 insertions(+), 112 deletions(-) rename RxCocoa/Common/CocoaUnits/{ => Driver}/ControlEvent+Driver.swift (100%) rename RxCocoa/Common/CocoaUnits/{ => Driver}/ControlProperty+Driver.swift (100%) create mode 100644 RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.swift create mode 100644 RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.tt rename RxCocoa/Common/CocoaUnits/{ => Driver}/Driver+Operators.swift (89%) rename RxCocoa/Common/CocoaUnits/{ => Driver}/Driver+Subscription.swift (100%) rename RxCocoa/Common/CocoaUnits/{ => Driver}/Driver.swift (91%) rename RxCocoa/Common/CocoaUnits/{ => Driver}/ObservableConvertibleType+Driver.swift (100%) diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index fe9c2125..65e285d5 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -256,20 +256,36 @@ C80D339B1B922FB00014629D /* ControlProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D33941B922FB00014629D /* ControlProperty.swift */; }; C80D342E1B9245A40014629D /* CombineLatest+CollectionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D342D1B9245A40014629D /* CombineLatest+CollectionType.swift */; }; C80D342F1B9245A40014629D /* CombineLatest+CollectionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D342D1B9245A40014629D /* CombineLatest+CollectionType.swift */; }; + C80DDE931BCE69BA006A1832 /* ControlEvent+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE8D1BCE69BA006A1832 /* ControlEvent+Driver.swift */; }; + C80DDE941BCE69BA006A1832 /* ControlEvent+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE8D1BCE69BA006A1832 /* ControlEvent+Driver.swift */; }; + C80DDE951BCE69BA006A1832 /* ControlEvent+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE8D1BCE69BA006A1832 /* ControlEvent+Driver.swift */; }; + C80DDE961BCE69BA006A1832 /* ControlEvent+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE8D1BCE69BA006A1832 /* ControlEvent+Driver.swift */; }; + C80DDE971BCE69BA006A1832 /* ControlProperty+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE8E1BCE69BA006A1832 /* ControlProperty+Driver.swift */; }; + C80DDE981BCE69BA006A1832 /* ControlProperty+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE8E1BCE69BA006A1832 /* ControlProperty+Driver.swift */; }; + C80DDE991BCE69BA006A1832 /* ControlProperty+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE8E1BCE69BA006A1832 /* ControlProperty+Driver.swift */; }; + C80DDE9A1BCE69BA006A1832 /* ControlProperty+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE8E1BCE69BA006A1832 /* ControlProperty+Driver.swift */; }; + C80DDE9B1BCE69BA006A1832 /* Driver+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE8F1BCE69BA006A1832 /* Driver+Operators.swift */; }; + C80DDE9C1BCE69BA006A1832 /* Driver+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE8F1BCE69BA006A1832 /* Driver+Operators.swift */; }; + C80DDE9D1BCE69BA006A1832 /* Driver+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE8F1BCE69BA006A1832 /* Driver+Operators.swift */; }; + C80DDE9E1BCE69BA006A1832 /* Driver+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE8F1BCE69BA006A1832 /* Driver+Operators.swift */; }; + C80DDE9F1BCE69BA006A1832 /* Driver+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE901BCE69BA006A1832 /* Driver+Subscription.swift */; }; + C80DDEA01BCE69BA006A1832 /* Driver+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE901BCE69BA006A1832 /* Driver+Subscription.swift */; }; + C80DDEA11BCE69BA006A1832 /* Driver+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE901BCE69BA006A1832 /* Driver+Subscription.swift */; }; + C80DDEA21BCE69BA006A1832 /* Driver+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE901BCE69BA006A1832 /* Driver+Subscription.swift */; }; + C80DDEA31BCE69BA006A1832 /* Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE911BCE69BA006A1832 /* Driver.swift */; }; + C80DDEA41BCE69BA006A1832 /* Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE911BCE69BA006A1832 /* Driver.swift */; }; + C80DDEA51BCE69BA006A1832 /* Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE911BCE69BA006A1832 /* Driver.swift */; }; + C80DDEA61BCE69BA006A1832 /* Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE911BCE69BA006A1832 /* Driver.swift */; }; + C80DDEA71BCE69BA006A1832 /* ObservableConvertibleType+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE921BCE69BA006A1832 /* ObservableConvertibleType+Driver.swift */; }; + C80DDEA81BCE69BA006A1832 /* ObservableConvertibleType+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE921BCE69BA006A1832 /* ObservableConvertibleType+Driver.swift */; }; + C80DDEA91BCE69BA006A1832 /* ObservableConvertibleType+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE921BCE69BA006A1832 /* ObservableConvertibleType+Driver.swift */; }; + C80DDEAA1BCE69BA006A1832 /* ObservableConvertibleType+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE921BCE69BA006A1832 /* ObservableConvertibleType+Driver.swift */; }; + C80DDEB11BCE8CA3006A1832 /* Driver+Operators+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEB01BCE8CA3006A1832 /* Driver+Operators+arity.swift */; }; + C80DDEB21BCE8CA3006A1832 /* Driver+Operators+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEB01BCE8CA3006A1832 /* Driver+Operators+arity.swift */; }; + C80DDEB31BCE8CA3006A1832 /* Driver+Operators+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEB01BCE8CA3006A1832 /* Driver+Operators+arity.swift */; }; + C80DDEB41BCE8CA3006A1832 /* Driver+Operators+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEB01BCE8CA3006A1832 /* Driver+Operators+arity.swift */; }; C821DBA21BA4DCAB008F3809 /* Buffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C821DBA11BA4DCAB008F3809 /* Buffer.swift */; }; C821DBA31BA4DCAB008F3809 /* Buffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C821DBA11BA4DCAB008F3809 /* Buffer.swift */; }; - C8226BC41BADDF2800D7F20C /* Driver+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BC11BADDD3600D7F20C /* Driver+Subscription.swift */; }; - C8226BC51BADDF2800D7F20C /* Driver+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BC11BADDD3600D7F20C /* Driver+Subscription.swift */; }; - C8226BC71BADE87100D7F20C /* ControlProperty+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BC61BADE87100D7F20C /* ControlProperty+Driver.swift */; }; - C8226BC81BADE87100D7F20C /* ControlProperty+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BC61BADE87100D7F20C /* ControlProperty+Driver.swift */; }; - C8226BCA1BADE87D00D7F20C /* ControlEvent+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BC91BADE87D00D7F20C /* ControlEvent+Driver.swift */; }; - C8226BCB1BADE87D00D7F20C /* ControlEvent+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BC91BADE87D00D7F20C /* ControlEvent+Driver.swift */; }; - C8226BCD1BADE8D600D7F20C /* ObservableConvertibleType+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BCC1BADE8D600D7F20C /* ObservableConvertibleType+Driver.swift */; }; - C8226BCE1BADE8D600D7F20C /* ObservableConvertibleType+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BCC1BADE8D600D7F20C /* ObservableConvertibleType+Driver.swift */; }; - C8226BD01BADEBDF00D7F20C /* Driver+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BCF1BADEBDF00D7F20C /* Driver+Operators.swift */; }; - C8226BD11BADEBDF00D7F20C /* Driver+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BCF1BADEBDF00D7F20C /* Driver+Operators.swift */; }; - C836E8E71BA2165500AFEF77 /* Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C836E8E51BA2165500AFEF77 /* Driver.swift */; }; - C836E8E81BA2165500AFEF77 /* Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C836E8E51BA2165500AFEF77 /* Driver.swift */; }; C849BE2B1BAB5D070019AD27 /* ObservableConvertibleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C849BE2A1BAB5D070019AD27 /* ObservableConvertibleType.swift */; }; C849BE2C1BAB5D070019AD27 /* ObservableConvertibleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C849BE2A1BAB5D070019AD27 /* ObservableConvertibleType.swift */; }; C84B38E91BA43380001B7D88 /* ScheduledItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B38E71BA43380001B7D88 /* ScheduledItem.swift */; }; @@ -312,18 +328,6 @@ C88254341B8A752B00B02D69 /* UITableView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88254121B8A752B00B02D69 /* UITableView+Rx.swift */; }; C88254351B8A752B00B02D69 /* UITextField+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88254131B8A752B00B02D69 /* UITextField+Rx.swift */; }; C88254361B8A752B00B02D69 /* UITextView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88254141B8A752B00B02D69 /* UITextView+Rx.swift */; }; - C8945FDF1BC6C09D0055219D /* Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C836E8E51BA2165500AFEF77 /* Driver.swift */; }; - C8945FE01BC6C09D0055219D /* Driver+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BCF1BADEBDF00D7F20C /* Driver+Operators.swift */; }; - C8945FE11BC6C09D0055219D /* Driver+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BC11BADDD3600D7F20C /* Driver+Subscription.swift */; }; - C8945FE21BC6C09D0055219D /* ControlProperty+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BC61BADE87100D7F20C /* ControlProperty+Driver.swift */; }; - C8945FE31BC6C09D0055219D /* ControlEvent+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BC91BADE87D00D7F20C /* ControlEvent+Driver.swift */; }; - C8945FE41BC6C09D0055219D /* ObservableConvertibleType+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BCC1BADE8D600D7F20C /* ObservableConvertibleType+Driver.swift */; }; - C8945FE51BC6C09D0055219D /* Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C836E8E51BA2165500AFEF77 /* Driver.swift */; }; - C8945FE61BC6C09D0055219D /* Driver+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BCF1BADEBDF00D7F20C /* Driver+Operators.swift */; }; - C8945FE71BC6C09D0055219D /* Driver+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BC11BADDD3600D7F20C /* Driver+Subscription.swift */; }; - C8945FE81BC6C09D0055219D /* ControlProperty+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BC61BADE87100D7F20C /* ControlProperty+Driver.swift */; }; - C8945FE91BC6C09D0055219D /* ControlEvent+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BC91BADE87D00D7F20C /* ControlEvent+Driver.swift */; }; - C8945FEA1BC6C09D0055219D /* ObservableConvertibleType+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8226BCC1BADE8D600D7F20C /* ObservableConvertibleType+Driver.swift */; }; C89461751BC6C1210055219D /* ObservableConvertibleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C849BE2A1BAB5D070019AD27 /* ObservableConvertibleType.swift */; }; C89461761BC6C1220055219D /* ObservableConvertibleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C849BE2A1BAB5D070019AD27 /* ObservableConvertibleType.swift */; }; C89CDB361BCB0DD7002063D9 /* ShareReplay1.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89CDB351BCB0DD7002063D9 /* ShareReplay1.swift */; }; @@ -887,13 +891,15 @@ C80D33931B922FB00014629D /* ControlEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlEvent.swift; sourceTree = ""; }; C80D33941B922FB00014629D /* ControlProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlProperty.swift; sourceTree = ""; }; C80D342D1B9245A40014629D /* CombineLatest+CollectionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CombineLatest+CollectionType.swift"; sourceTree = ""; }; + C80DDE8D1BCE69BA006A1832 /* ControlEvent+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControlEvent+Driver.swift"; sourceTree = ""; }; + C80DDE8E1BCE69BA006A1832 /* ControlProperty+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControlProperty+Driver.swift"; sourceTree = ""; }; + C80DDE8F1BCE69BA006A1832 /* Driver+Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Driver+Operators.swift"; sourceTree = ""; }; + C80DDE901BCE69BA006A1832 /* Driver+Subscription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Driver+Subscription.swift"; sourceTree = ""; }; + C80DDE911BCE69BA006A1832 /* Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Driver.swift; sourceTree = ""; }; + C80DDE921BCE69BA006A1832 /* ObservableConvertibleType+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObservableConvertibleType+Driver.swift"; sourceTree = ""; }; + C80DDEAB1BCE83B2006A1832 /* Driver+Operators+arity.tt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Driver+Operators+arity.tt"; sourceTree = ""; }; + C80DDEB01BCE8CA3006A1832 /* Driver+Operators+arity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Driver+Operators+arity.swift"; sourceTree = ""; }; C821DBA11BA4DCAB008F3809 /* Buffer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Buffer.swift; sourceTree = ""; }; - C8226BC11BADDD3600D7F20C /* Driver+Subscription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Driver+Subscription.swift"; sourceTree = ""; }; - C8226BC61BADE87100D7F20C /* ControlProperty+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControlProperty+Driver.swift"; sourceTree = ""; }; - C8226BC91BADE87D00D7F20C /* ControlEvent+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControlEvent+Driver.swift"; sourceTree = ""; }; - C8226BCC1BADE8D600D7F20C /* ObservableConvertibleType+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObservableConvertibleType+Driver.swift"; sourceTree = ""; }; - C8226BCF1BADEBDF00D7F20C /* Driver+Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Driver+Operators.swift"; sourceTree = ""; }; - C836E8E51BA2165500AFEF77 /* Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Driver.swift; sourceTree = ""; }; C849BE2A1BAB5D070019AD27 /* ObservableConvertibleType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableConvertibleType.swift; sourceTree = ""; }; C84B38E71BA43380001B7D88 /* ScheduledItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduledItem.swift; sourceTree = ""; }; C84B38ED1BA433CD001B7D88 /* Generate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Generate.swift; sourceTree = ""; }; @@ -1318,18 +1324,28 @@ C80D33911B922FB00014629D /* CocoaUnits */ = { isa = PBXGroup; children = ( - C836E8E51BA2165500AFEF77 /* Driver.swift */, - C8226BCF1BADEBDF00D7F20C /* Driver+Operators.swift */, - C8226BC11BADDD3600D7F20C /* Driver+Subscription.swift */, - C8226BC61BADE87100D7F20C /* ControlProperty+Driver.swift */, - C8226BC91BADE87D00D7F20C /* ControlEvent+Driver.swift */, - C8226BCC1BADE8D600D7F20C /* ObservableConvertibleType+Driver.swift */, + C80DDE8C1BCE69BA006A1832 /* Driver */, C80D33931B922FB00014629D /* ControlEvent.swift */, C80D33941B922FB00014629D /* ControlProperty.swift */, ); path = CocoaUnits; sourceTree = ""; }; + C80DDE8C1BCE69BA006A1832 /* Driver */ = { + isa = PBXGroup; + children = ( + C80DDEB01BCE8CA3006A1832 /* Driver+Operators+arity.swift */, + C80DDE8D1BCE69BA006A1832 /* ControlEvent+Driver.swift */, + C80DDE8E1BCE69BA006A1832 /* ControlProperty+Driver.swift */, + C80DDE911BCE69BA006A1832 /* Driver.swift */, + C80DDE8F1BCE69BA006A1832 /* Driver+Operators.swift */, + C80DDEAB1BCE83B2006A1832 /* Driver+Operators+arity.tt */, + C80DDE901BCE69BA006A1832 /* Driver+Subscription.swift */, + C80DDE921BCE69BA006A1832 /* ObservableConvertibleType+Driver.swift */, + ); + path = Driver; + sourceTree = ""; + }; C88253EE1B8A752B00B02D69 /* iOS */ = { isa = PBXGroup; children = ( @@ -1912,11 +1928,14 @@ C88254321B8A752B00B02D69 /* UISlider+Rx.swift in Sources */, C8093ED91B8A732E0088E94D /* _RXKVOObserver.m in Sources */, C882542F1B8A752B00B02D69 /* UIScrollView+Rx.swift in Sources */, + C80DDE9B1BCE69BA006A1832 /* Driver+Operators.swift in Sources */, C8093EE31B8A732E0088E94D /* DelegateProxyType.swift in Sources */, C8093EFD1B8A732E0088E94D /* RxTarget.swift in Sources */, C88254361B8A752B00B02D69 /* UITextView+Rx.swift in Sources */, C88254171B8A752B00B02D69 /* RxTableViewReactiveArrayDataSource.swift in Sources */, C882541E1B8A752B00B02D69 /* RxCollectionViewDataSourceProxy.swift in Sources */, + C80DDE971BCE69BA006A1832 /* ControlProperty+Driver.swift in Sources */, + C80DDE931BCE69BA006A1832 /* ControlEvent+Driver.swift in Sources */, C8093ED11B8A732E0088E94D /* _RX.m in Sources */, C8093EFB1B8A732E0088E94D /* RxCocoa.swift in Sources */, C88254231B8A752B00B02D69 /* RxTableViewDelegateProxy.swift in Sources */, @@ -1934,14 +1953,15 @@ C882542E1B8A752B00B02D69 /* UILabel+Rx.swift in Sources */, C88254211B8A752B00B02D69 /* RxSearchBarDelegateProxy.swift in Sources */, C882541D1B8A752B00B02D69 /* RxAlertViewDelegateProxy.swift in Sources */, + C80DDEA71BCE69BA006A1832 /* ObservableConvertibleType+Driver.swift in Sources */, + C80DDE9F1BCE69BA006A1832 /* Driver+Subscription.swift in Sources */, C80D338F1B91EF9E0014629D /* Observable+Bind.swift in Sources */, C88254311B8A752B00B02D69 /* UISegmentedControl+Rx.swift in Sources */, - C8226BD01BADEBDF00D7F20C /* Driver+Operators.swift in Sources */, - C8226BC41BADDF2800D7F20C /* Driver+Subscription.swift in Sources */, C8093EED1B8A732E0088E94D /* KVOObservable.swift in Sources */, + C80DDEB11BCE8CA3006A1832 /* Driver+Operators+arity.swift in Sources */, C88254281B8A752B00B02D69 /* UIButton+Rx.swift in Sources */, C8093EDF1B8A732E0088E94D /* CLLocationManager+Rx.swift in Sources */, - C8226BC71BADE87100D7F20C /* ControlProperty+Driver.swift in Sources */, + C80DDEA31BCE69BA006A1832 /* Driver.swift in Sources */, C8093EEB1B8A732E0088E94D /* DeinitAction.swift in Sources */, C882541C1B8A752B00B02D69 /* RxActionSheetDelegateProxy.swift in Sources */, C8093ED51B8A732E0088E94D /* _RXDelegateProxy.m in Sources */, @@ -1951,13 +1971,11 @@ C88254251B8A752B00B02D69 /* UIActionSheet+Rx.swift in Sources */, C80D339A1B922FB00014629D /* ControlProperty.swift in Sources */, C882542B1B8A752B00B02D69 /* UIDatePicker+Rx.swift in Sources */, - C8226BCD1BADE8D600D7F20C /* ObservableConvertibleType+Driver.swift in Sources */, C88254221B8A752B00B02D69 /* RxTableViewDataSourceProxy.swift in Sources */, C8093EDD1B8A732E0088E94D /* _RXSwizzling.m in Sources */, C8093EE91B8A732E0088E94D /* Deallocating.swift in Sources */, C882542C1B8A752B00B02D69 /* UIGestureRecognizer+Rx.swift in Sources */, C8093EE11B8A732E0088E94D /* DelegateProxy.swift in Sources */, - C8226BCA1BADE87D00D7F20C /* ControlEvent+Driver.swift in Sources */, C8093EF91B8A732E0088E94D /* RxCLLocationManagerDelegateProxy.swift in Sources */, C88254331B8A752B00B02D69 /* UISwitch+Rx.swift in Sources */, C8093EE51B8A732E0088E94D /* Logging.swift in Sources */, @@ -1965,7 +1983,6 @@ C882541A1B8A752B00B02D69 /* RxCollectionViewDataSourceType.swift in Sources */, C8093EF11B8A732E0088E94D /* NSNotificationCenter+Rx.swift in Sources */, C88254351B8A752B00B02D69 /* UITextField+Rx.swift in Sources */, - C836E8E71BA2165500AFEF77 /* Driver.swift in Sources */, C8093EF71B8A732E0088E94D /* NSURLSession+Rx.swift in Sources */, C8093EE71B8A732E0088E94D /* ControlTarget.swift in Sources */, C88254301B8A752B00B02D69 /* UISearchBar+Rx.swift in Sources */, @@ -1978,6 +1995,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C80DDEA41BCE69BA006A1832 /* Driver.swift in Sources */, C8093F4A1B8A732E0088E94D /* NSImageView+Rx.swift in Sources */, C8093EDA1B8A732E0088E94D /* _RXKVOObserver.m in Sources */, C8093EE41B8A732E0088E94D /* DelegateProxyType.swift in Sources */, @@ -1987,28 +2005,28 @@ C8093ED21B8A732E0088E94D /* _RX.m in Sources */, C8093EFC1B8A732E0088E94D /* RxCocoa.swift in Sources */, C80D33991B922FB00014629D /* ControlEvent.swift in Sources */, + C80DDEA81BCE69BA006A1832 /* ObservableConvertibleType+Driver.swift in Sources */, C80D339B1B922FB00014629D /* ControlProperty.swift in Sources */, - C8226BCE1BADE8D600D7F20C /* ObservableConvertibleType+Driver.swift in Sources */, C8093EF41B8A732E0088E94D /* NSObject+Rx+CoreGraphics.swift in Sources */, - C836E8E81BA2165500AFEF77 /* Driver.swift in Sources */, C8093EF01B8A732E0088E94D /* KVOObserver.swift in Sources */, C8093EEE1B8A732E0088E94D /* KVOObservable.swift in Sources */, C8093EE01B8A732E0088E94D /* CLLocationManager+Rx.swift in Sources */, C8093EEC1B8A732E0088E94D /* DeinitAction.swift in Sources */, - C8226BD11BADEBDF00D7F20C /* Driver+Operators.swift in Sources */, - C8226BC51BADDF2800D7F20C /* Driver+Subscription.swift in Sources */, C8093F461B8A732E0088E94D /* NSButton+Rx.swift in Sources */, + C80DDEA01BCE69BA006A1832 /* Driver+Subscription.swift in Sources */, C8093ED61B8A732E0088E94D /* _RXDelegateProxy.m in Sources */, C8093EF61B8A732E0088E94D /* NSObject+Rx.swift in Sources */, - C8226BC81BADE87100D7F20C /* ControlProperty+Driver.swift in Sources */, + C80DDE981BCE69BA006A1832 /* ControlProperty+Driver.swift in Sources */, C8093EDE1B8A732E0088E94D /* _RXSwizzling.m in Sources */, + C80DDE941BCE69BA006A1832 /* ControlEvent+Driver.swift in Sources */, + C80DDEB21BCE8CA3006A1832 /* Driver+Operators+arity.swift in Sources */, C8093EEA1B8A732E0088E94D /* Deallocating.swift in Sources */, C8093EE21B8A732E0088E94D /* DelegateProxy.swift in Sources */, C8093EFA1B8A732E0088E94D /* RxCLLocationManagerDelegateProxy.swift in Sources */, C8093EE61B8A732E0088E94D /* Logging.swift in Sources */, C8093EF21B8A732E0088E94D /* NSNotificationCenter+Rx.swift in Sources */, - C8226BCB1BADE87D00D7F20C /* ControlEvent+Driver.swift in Sources */, C8093EF81B8A732E0088E94D /* NSURLSession+Rx.swift in Sources */, + C80DDE9C1BCE69BA006A1832 /* Driver+Operators.swift in Sources */, C8093F4C1B8A732E0088E94D /* NSSlider+Rx.swift in Sources */, C8093EE81B8A732E0088E94D /* ControlTarget.swift in Sources */, C80D33901B91EF9E0014629D /* Observable+Bind.swift in Sources */, @@ -2386,19 +2404,20 @@ C8F0C0061BBBFBB9001B112F /* UISlider+Rx.swift in Sources */, C8F0C0071BBBFBB9001B112F /* _RXKVOObserver.m in Sources */, C8F0C0081BBBFBB9001B112F /* UIScrollView+Rx.swift in Sources */, + C80DDE9E1BCE69BA006A1832 /* Driver+Operators.swift in Sources */, C8F0C0091BBBFBB9001B112F /* DelegateProxyType.swift in Sources */, C8F0C00A1BBBFBB9001B112F /* RxTarget.swift in Sources */, C8F0C00B1BBBFBB9001B112F /* UITextView+Rx.swift in Sources */, C8F0C00C1BBBFBB9001B112F /* RxTableViewReactiveArrayDataSource.swift in Sources */, C8F0C00D1BBBFBB9001B112F /* RxCollectionViewDataSourceProxy.swift in Sources */, + C80DDE9A1BCE69BA006A1832 /* ControlProperty+Driver.swift in Sources */, + C80DDE961BCE69BA006A1832 /* ControlEvent+Driver.swift in Sources */, C8F0C00E1BBBFBB9001B112F /* _RX.m in Sources */, C8F0C00F1BBBFBB9001B112F /* RxCocoa.swift in Sources */, C8F0C0101BBBFBB9001B112F /* RxTableViewDelegateProxy.swift in Sources */, C8F0C0111BBBFBB9001B112F /* UIStepper+Rx.swift in Sources */, C8F0C0121BBBFBB9001B112F /* UIImageView+Rx.swift in Sources */, C8F0C0131BBBFBB9001B112F /* ControlEvent.swift in Sources */, - C8945FE91BC6C09D0055219D /* ControlEvent+Driver.swift in Sources */, - C8945FE71BC6C09D0055219D /* Driver+Subscription.swift in Sources */, C8F0C0141BBBFBB9001B112F /* NSObject+Rx+CoreGraphics.swift in Sources */, C8F0C0151BBBFBB9001B112F /* UIControl+Rx.swift in Sources */, C8F0C0161BBBFBB9001B112F /* UITableView+Rx.swift in Sources */, @@ -2410,12 +2429,15 @@ C8F0C01C1BBBFBB9001B112F /* UILabel+Rx.swift in Sources */, C8F0C01D1BBBFBB9001B112F /* RxSearchBarDelegateProxy.swift in Sources */, C8F0C01E1BBBFBB9001B112F /* RxAlertViewDelegateProxy.swift in Sources */, + C80DDEAA1BCE69BA006A1832 /* ObservableConvertibleType+Driver.swift in Sources */, + C80DDEA21BCE69BA006A1832 /* Driver+Subscription.swift in Sources */, C8F0C01F1BBBFBB9001B112F /* Observable+Bind.swift in Sources */, - C8945FEA1BC6C09D0055219D /* ObservableConvertibleType+Driver.swift in Sources */, C8F0C0201BBBFBB9001B112F /* UISegmentedControl+Rx.swift in Sources */, C8F0C0211BBBFBB9001B112F /* KVOObservable.swift in Sources */, + C80DDEB41BCE8CA3006A1832 /* Driver+Operators+arity.swift in Sources */, C8F0C0221BBBFBB9001B112F /* UIButton+Rx.swift in Sources */, C8F0C0231BBBFBB9001B112F /* CLLocationManager+Rx.swift in Sources */, + C80DDEA61BCE69BA006A1832 /* Driver.swift in Sources */, C8F0C0241BBBFBB9001B112F /* DeinitAction.swift in Sources */, C8F0C0251BBBFBB9001B112F /* RxActionSheetDelegateProxy.swift in Sources */, C8F0C0261BBBFBB9001B112F /* _RXDelegateProxy.m in Sources */, @@ -2427,13 +2449,10 @@ C8F0C02C1BBBFBB9001B112F /* UIDatePicker+Rx.swift in Sources */, C8F0C02D1BBBFBB9001B112F /* RxTableViewDataSourceProxy.swift in Sources */, C8F0C02E1BBBFBB9001B112F /* _RXSwizzling.m in Sources */, - C8945FE61BC6C09D0055219D /* Driver+Operators.swift in Sources */, C8F0C02F1BBBFBB9001B112F /* Deallocating.swift in Sources */, C8F0C0301BBBFBB9001B112F /* UIGestureRecognizer+Rx.swift in Sources */, C8F0C0311BBBFBB9001B112F /* DelegateProxy.swift in Sources */, C8F0C0321BBBFBB9001B112F /* RxCLLocationManagerDelegateProxy.swift in Sources */, - C8945FE51BC6C09D0055219D /* Driver.swift in Sources */, - C8945FE81BC6C09D0055219D /* ControlProperty+Driver.swift in Sources */, C8F0C0331BBBFBB9001B112F /* UISwitch+Rx.swift in Sources */, C8F0C0341BBBFBB9001B112F /* Logging.swift in Sources */, C8F0C0351BBBFBB9001B112F /* UICollectionView+Rx.swift in Sources */, @@ -2463,19 +2482,20 @@ D203C4F71BB9C53100D02D00 /* RxTableViewDataSourceType.swift in Sources */, D203C5021BB9C53E00D02D00 /* UIAlertView+Rx.swift in Sources */, D203C50E1BB9C53E00D02D00 /* UISlider+Rx.swift in Sources */, + C80DDE9D1BCE69BA006A1832 /* Driver+Operators.swift in Sources */, D2138C8C1BB9BECA00339B5C /* ControlEvent.swift in Sources */, D2138C981BB9BEEE00339B5C /* RxCocoa.swift in Sources */, D2138C991BB9BEEE00339B5C /* RxTarget.swift in Sources */, D203C5081BB9C53E00D02D00 /* UIGestureRecognizer+Rx.swift in Sources */, D2138C931BB9BEDA00339B5C /* NSNotificationCenter+Rx.swift in Sources */, + C80DDE991BCE69BA006A1832 /* ControlProperty+Driver.swift in Sources */, + C80DDE951BCE69BA006A1832 /* ControlEvent+Driver.swift in Sources */, D203C4F61BB9C52E00D02D00 /* RxCollectionViewDataSourceType.swift in Sources */, D203C5131BB9C53E00D02D00 /* UITextView+Rx.swift in Sources */, D203C4F41BB9C52400D02D00 /* RxTableViewReactiveArrayDataSource.swift in Sources */, D2138C951BB9BEDA00339B5C /* NSObject+Rx.swift in Sources */, D2138C881BB9BEBE00339B5C /* DelegateProxy.swift in Sources */, D203C5101BB9C53E00D02D00 /* UISwitch+Rx.swift in Sources */, - C8945FE31BC6C09D0055219D /* ControlEvent+Driver.swift in Sources */, - C8945FE11BC6C09D0055219D /* Driver+Subscription.swift in Sources */, D203C5121BB9C53E00D02D00 /* UITextField+Rx.swift in Sources */, D203C4F91BB9C53700D02D00 /* RxAlertViewDelegateProxy.swift in Sources */, D203C4F31BB9C4CA00D02D00 /* RxCollectionViewReactiveArrayDataSource.swift in Sources */, @@ -2487,12 +2507,15 @@ D203C4FC1BB9C53700D02D00 /* RxScrollViewDelegateProxy.swift in Sources */, D2138C8E1BB9BED600339B5C /* ControlTarget.swift in Sources */, D203C5041BB9C53E00D02D00 /* UIButton+Rx.swift in Sources */, + C80DDEA91BCE69BA006A1832 /* ObservableConvertibleType+Driver.swift in Sources */, + C80DDEA11BCE69BA006A1832 /* Driver+Subscription.swift in Sources */, D2138C891BB9BEBE00339B5C /* DelegateProxyType.swift in Sources */, - C8945FE41BC6C09D0055219D /* ObservableConvertibleType+Driver.swift in Sources */, D2138C921BB9BED600339B5C /* KVOObserver.swift in Sources */, D2138C831BB9BEBE00339B5C /* _RXKVOObserver.m in Sources */, + C80DDEB31BCE8CA3006A1832 /* Driver+Operators+arity.swift in Sources */, D203C5061BB9C53E00D02D00 /* UIControl+Rx.swift in Sources */, D203C5111BB9C53E00D02D00 /* UITableView+Rx.swift in Sources */, + C80DDEA51BCE69BA006A1832 /* Driver.swift in Sources */, D203C4F81BB9C53700D02D00 /* RxActionSheetDelegateProxy.swift in Sources */, D2138C8F1BB9BED600339B5C /* Deallocating.swift in Sources */, D2138C961BB9BEDA00339B5C /* NSURLSession+Rx.swift in Sources */, @@ -2504,13 +2527,10 @@ D2138C861BB9BEBE00339B5C /* Observable+Bind.swift in Sources */, D203C50A1BB9C53E00D02D00 /* UILabel+Rx.swift in Sources */, D2138C901BB9BED600339B5C /* DeinitAction.swift in Sources */, - C8945FE01BC6C09D0055219D /* Driver+Operators.swift in Sources */, D203C4F51BB9C52900D02D00 /* ItemEvents.swift in Sources */, D2138C911BB9BED600339B5C /* KVOObservable.swift in Sources */, D203C4FA1BB9C53700D02D00 /* RxCollectionViewDataSourceProxy.swift in Sources */, D2138C7F1BB9BEBE00339B5C /* _RX.m in Sources */, - C8945FDF1BC6C09D0055219D /* Driver.swift in Sources */, - C8945FE21BC6C09D0055219D /* ControlProperty+Driver.swift in Sources */, D203C4FE1BB9C53700D02D00 /* RxTableViewDataSourceProxy.swift in Sources */, D203C5001BB9C53700D02D00 /* RxTextViewDelegateProxy.swift in Sources */, D203C5091BB9C53E00D02D00 /* UIImageView+Rx.swift in Sources */, diff --git a/RxCocoa/Common/CocoaUnits/ControlEvent+Driver.swift b/RxCocoa/Common/CocoaUnits/Driver/ControlEvent+Driver.swift similarity index 100% rename from RxCocoa/Common/CocoaUnits/ControlEvent+Driver.swift rename to RxCocoa/Common/CocoaUnits/Driver/ControlEvent+Driver.swift diff --git a/RxCocoa/Common/CocoaUnits/ControlProperty+Driver.swift b/RxCocoa/Common/CocoaUnits/Driver/ControlProperty+Driver.swift similarity index 100% rename from RxCocoa/Common/CocoaUnits/ControlProperty+Driver.swift rename to RxCocoa/Common/CocoaUnits/Driver/ControlProperty+Driver.swift diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.swift new file mode 100644 index 00000000..9d51cd7e --- /dev/null +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.swift @@ -0,0 +1,281 @@ +// This file is autogenerated. +// Take a look at `Preprocessor` target in RxSwift project +// +// Driver+Operators+arity.swift +// Rx +// +// Created by Krunoslav Zaher on 10/14/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE +import RxSwift +#endif + + + +// 2 + +/** +Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. + +- parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. +- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. +*/ +public func zip + (source1: O1, _ source2: O2, _ resultSelector: (O1.E, O2.E) throws -> R) + -> Driver { + let source = zip( + source1.asDriver().asObservable(), source2.asDriver().asObservable(), + resultSelector: resultSelector + ) + + return Driver(source) +} + +/** +Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. + +- parameter resultSelector: Function to invoke whenever any of the sources produces an element. +- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. +*/ +public func combineLatest + (source1: O1, _ source2: O2, _ resultSelector: (O1.E, O2.E) throws -> R) + -> Driver { + let source = combineLatest( + source1.asDriver().asObservable(), source2.asDriver().asObservable(), + resultSelector: resultSelector + ) + + return Driver(source) +} + + + +// 3 + +/** +Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. + +- parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. +- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. +*/ +public func zip + (source1: O1, _ source2: O2, _ source3: O3, _ resultSelector: (O1.E, O2.E, O3.E) throws -> R) + -> Driver { + let source = zip( + source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), + resultSelector: resultSelector + ) + + return Driver(source) +} + +/** +Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. + +- parameter resultSelector: Function to invoke whenever any of the sources produces an element. +- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. +*/ +public func combineLatest + (source1: O1, _ source2: O2, _ source3: O3, _ resultSelector: (O1.E, O2.E, O3.E) throws -> R) + -> Driver { + let source = combineLatest( + source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), + resultSelector: resultSelector + ) + + return Driver(source) +} + + + +// 4 + +/** +Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. + +- parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. +- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. +*/ +public func zip + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ resultSelector: (O1.E, O2.E, O3.E, O4.E) throws -> R) + -> Driver { + let source = zip( + source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), + resultSelector: resultSelector + ) + + return Driver(source) +} + +/** +Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. + +- parameter resultSelector: Function to invoke whenever any of the sources produces an element. +- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. +*/ +public func combineLatest + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ resultSelector: (O1.E, O2.E, O3.E, O4.E) throws -> R) + -> Driver { + let source = combineLatest( + source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), + resultSelector: resultSelector + ) + + return Driver(source) +} + + + +// 5 + +/** +Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. + +- parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. +- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. +*/ +public func zip + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> R) + -> Driver { + let source = zip( + source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), source5.asDriver().asObservable(), + resultSelector: resultSelector + ) + + return Driver(source) +} + +/** +Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. + +- parameter resultSelector: Function to invoke whenever any of the sources produces an element. +- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. +*/ +public func combineLatest + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> R) + -> Driver { + let source = combineLatest( + source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), source5.asDriver().asObservable(), + resultSelector: resultSelector + ) + + return Driver(source) +} + + + +// 6 + +/** +Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. + +- parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. +- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. +*/ +public func zip + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> R) + -> Driver { + let source = zip( + source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), source5.asDriver().asObservable(), source6.asDriver().asObservable(), + resultSelector: resultSelector + ) + + return Driver(source) +} + +/** +Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. + +- parameter resultSelector: Function to invoke whenever any of the sources produces an element. +- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. +*/ +public func combineLatest + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> R) + -> Driver { + let source = combineLatest( + source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), source5.asDriver().asObservable(), source6.asDriver().asObservable(), + resultSelector: resultSelector + ) + + return Driver(source) +} + + + +// 7 + +/** +Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. + +- parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. +- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. +*/ +public func zip + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> R) + -> Driver { + let source = zip( + source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), source5.asDriver().asObservable(), source6.asDriver().asObservable(), source7.asDriver().asObservable(), + resultSelector: resultSelector + ) + + return Driver(source) +} + +/** +Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. + +- parameter resultSelector: Function to invoke whenever any of the sources produces an element. +- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. +*/ +public func combineLatest + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> R) + -> Driver { + let source = combineLatest( + source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), source5.asDriver().asObservable(), source6.asDriver().asObservable(), source7.asDriver().asObservable(), + resultSelector: resultSelector + ) + + return Driver(source) +} + + + +// 8 + +/** +Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. + +- parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. +- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. +*/ +public func zip + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> R) + -> Driver { + let source = zip( + source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), source5.asDriver().asObservable(), source6.asDriver().asObservable(), source7.asDriver().asObservable(), source8.asDriver().asObservable(), + resultSelector: resultSelector + ) + + return Driver(source) +} + +/** +Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. + +- parameter resultSelector: Function to invoke whenever any of the sources produces an element. +- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. +*/ +public func combineLatest + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> R) + -> Driver { + let source = combineLatest( + source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), source5.asDriver().asObservable(), source6.asDriver().asObservable(), source7.asDriver().asObservable(), source8.asDriver().asObservable(), + resultSelector: resultSelector + ) + + return Driver(source) +} + diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.tt b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.tt new file mode 100644 index 00000000..781da9cd --- /dev/null +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.tt @@ -0,0 +1,52 @@ +// +// Driver+Operators+arity.swift +// Rx +// +// Created by Krunoslav Zaher on 10/14/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE +import RxSwift +#endif + +<% for i in 2 ... 8 { %> + +// <%= i %> + +/** +Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. + +- parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. +- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. +*/ +public func zip<<%= (Array(1...i).map { "O\($0): DriverConvertibleType" }).joinWithSeparator(", ") %>, R> + (<%= (Array(1...i).map { "source\($0): O\($0)" }).joinWithSeparator(", _ ") %>, _ resultSelector: (<%= (Array(1...i).map { "O\($0).E" }).joinWithSeparator(", ") %>) throws -> R) + -> Driver { + let source = zip( + <%= (Array(1...i).map { "source\($0).asDriver().asObservable()" }).joinWithSeparator(", ") %>, + resultSelector: resultSelector + ) + + return Driver(source) +} + +/** +Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. + +- parameter resultSelector: Function to invoke whenever any of the sources produces an element. +- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. +*/ +public func combineLatest<<%= (Array(1...i).map { "O\($0): DriverConvertibleType" }).joinWithSeparator(", ") %>, R> + (<%= (Array(1...i).map { "source\($0): O\($0)" }).joinWithSeparator(", _ ") %>, _ resultSelector: (<%= (Array(1...i).map { "O\($0).E" }).joinWithSeparator(", ") %>) throws -> R) + -> Driver { + let source = combineLatest( + <%= (Array(1...i).map { "source\($0).asDriver().asObservable()" }).joinWithSeparator(", ") %>, + resultSelector: resultSelector + ) + + return Driver(source) +} + +<% } %> \ No newline at end of file diff --git a/RxCocoa/Common/CocoaUnits/Driver+Operators.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift similarity index 89% rename from RxCocoa/Common/CocoaUnits/Driver+Operators.swift rename to RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift index 3b8a74ba..7d49271f 100644 --- a/RxCocoa/Common/CocoaUnits/Driver+Operators.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift @@ -289,4 +289,32 @@ extension Driver { .scan(seed, accumulator: accumulator) return Driver(source) } +} + +extension SequenceType where Generator.Element : DriverConvertibleType { + + /** + Concatenates all observable sequences in the given sequence, as long as the previous observable sequence terminated successfully. + + - returns: An observable sequence that contains the elements of each given sequence, in sequential order. + */ + public func concat() + -> Driver { + let source: Observable = self.lazy.map { $0.asDriver() }.concat() + return Driver(source) + } +} + +extension CollectionType where Generator.Element : DriverConvertibleType { + + /** + Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. + + - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. + - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. + */ + public func zip(resultSelector: [Generator.Element.E] throws -> R) -> Driver { + let source: Observable = self.map { $0.asDriver() }.zip(resultSelector) + return Driver(source) + } } \ No newline at end of file diff --git a/RxCocoa/Common/CocoaUnits/Driver+Subscription.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver+Subscription.swift similarity index 100% rename from RxCocoa/Common/CocoaUnits/Driver+Subscription.swift rename to RxCocoa/Common/CocoaUnits/Driver/Driver+Subscription.swift diff --git a/RxCocoa/Common/CocoaUnits/Driver.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver.swift similarity index 91% rename from RxCocoa/Common/CocoaUnits/Driver.swift rename to RxCocoa/Common/CocoaUnits/Driver/Driver.swift index 166a961a..165dd601 100644 --- a/RxCocoa/Common/CocoaUnits/Driver.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver.swift @@ -46,6 +46,10 @@ public struct Driver : DriverConvertibleType { init(_ source: Observable) { self._source = source.shareReplay(1) } + + init(raw: Observable) { + self._source = raw + } #if EXPANDABLE_DRIVER public static func createUnsafe(source: O) -> Driver { @@ -72,7 +76,7 @@ public struct Drive { - returns: An observable sequence with no elements. */ public static func empty() -> Driver { - return Driver(RxSwift.empty()) + return Driver(raw: RxSwift.empty()) } /** @@ -81,7 +85,7 @@ public struct Drive { - returns: An observable sequence whose observers will never get called. */ public static func never() -> Driver { - return Driver(RxSwift.never()) + return Driver(raw: RxSwift.never()) } /** @@ -91,7 +95,7 @@ public struct Drive { - returns: An observable sequence containing the single specified element. */ public static func just(element: E) -> Driver { - return Driver(RxSwift.just(element)) + return Driver(raw: RxSwift.just(element)) } #else @@ -102,7 +106,7 @@ public struct Drive { - returns: An observable sequence with no elements. */ public static func empty() -> Driver { - return Driver(_empty()) + return Driver(raw: _empty()) } /** @@ -111,7 +115,7 @@ public struct Drive { - returns: An observable sequence whose observers will never get called. */ public static func never() -> Driver { - return Driver(_never()) + return Driver(raw: _never()) } /** @@ -121,14 +125,14 @@ public struct Drive { - returns: An observable sequence containing the single specified element. */ public static func just(element: E) -> Driver { - return Driver(_just(element)) + return Driver(raw: _just(element)) } #endif public static func sequenceOf(elements: E ...) -> Driver { let source = elements.asObservable() - return Driver(source) + return Driver(raw: source) } } diff --git a/RxCocoa/Common/CocoaUnits/ObservableConvertibleType+Driver.swift b/RxCocoa/Common/CocoaUnits/Driver/ObservableConvertibleType+Driver.swift similarity index 100% rename from RxCocoa/Common/CocoaUnits/ObservableConvertibleType+Driver.swift rename to RxCocoa/Common/CocoaUnits/Driver/ObservableConvertibleType+Driver.swift diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index 062a608e..6b759a72 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -18,6 +18,14 @@ 07E300091B149A2A00F00100 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E300081B149A2A00F00100 /* User.swift */; }; 07E3C2331B03605B0010338D /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E3C2321B03605B0010338D /* Dependencies.swift */; }; C80DDE881BCDAA0F006A1832 /* SkipWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE7A1BCDA952006A1832 /* SkipWhile.swift */; }; + C80DDED21BCE9046006A1832 /* ControlEvent+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC21BCE9041006A1832 /* ControlEvent+Driver.swift */; }; + C80DDED31BCE9046006A1832 /* ControlProperty+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC31BCE9041006A1832 /* ControlProperty+Driver.swift */; }; + C80DDED41BCE9046006A1832 /* Driver+Operators+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC41BCE9041006A1832 /* Driver+Operators+arity.swift */; }; + C80DDED51BCE9046006A1832 /* Driver+Operators+arity.tt in Resources */ = {isa = PBXBuildFile; fileRef = C80DDEC51BCE9041006A1832 /* Driver+Operators+arity.tt */; }; + C80DDED61BCE9046006A1832 /* Driver+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC61BCE9041006A1832 /* Driver+Operators.swift */; }; + C80DDED71BCE9046006A1832 /* Driver+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC71BCE9041006A1832 /* Driver+Subscription.swift */; }; + C80DDED81BCE9046006A1832 /* Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC81BCE9041006A1832 /* Driver.swift */; }; + C80DDED91BCE9046006A1832 /* ObservableConvertibleType+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC91BCE9041006A1832 /* ObservableConvertibleType+Driver.swift */; }; C8297E2F1B6CF905000589EA /* RxTableViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88C78631B3EB0A00061C5AB /* RxTableViewSectionedAnimatedDataSource.swift */; }; C8297E301B6CF905000589EA /* RandomUserAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0706E19E1B17703E00BA2D3A /* RandomUserAPI.swift */; }; C8297E311B6CF905000589EA /* SearchResultViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86E2F321AE5A0CA00C31024 /* SearchResultViewModel.swift */; }; @@ -216,14 +224,8 @@ C89465631BC6C2BC0055219D /* _RXKVOObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = C89465131BC6C2BC0055219D /* _RXKVOObserver.m */; }; C89465641BC6C2BC0055219D /* _RXSwizzling.m in Sources */ = {isa = PBXBuildFile; fileRef = C89465151BC6C2BC0055219D /* _RXSwizzling.m */; }; C89465651BC6C2BC0055219D /* CLLocationManager+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465161BC6C2BC0055219D /* CLLocationManager+Rx.swift */; }; - C89465661BC6C2BC0055219D /* ControlEvent+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465181BC6C2BC0055219D /* ControlEvent+Driver.swift */; }; C89465671BC6C2BC0055219D /* ControlEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465191BC6C2BC0055219D /* ControlEvent.swift */; }; - C89465681BC6C2BC0055219D /* ControlProperty+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894651A1BC6C2BC0055219D /* ControlProperty+Driver.swift */; }; C89465691BC6C2BC0055219D /* ControlProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894651B1BC6C2BC0055219D /* ControlProperty.swift */; }; - C894656A1BC6C2BC0055219D /* Driver+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894651C1BC6C2BC0055219D /* Driver+Operators.swift */; }; - C894656B1BC6C2BC0055219D /* Driver+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894651D1BC6C2BC0055219D /* Driver+Subscription.swift */; }; - C894656C1BC6C2BC0055219D /* Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894651E1BC6C2BC0055219D /* Driver.swift */; }; - C894656D1BC6C2BC0055219D /* ObservableConvertibleType+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894651F1BC6C2BC0055219D /* ObservableConvertibleType+Driver.swift */; }; C894656E1BC6C2BC0055219D /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465201BC6C2BC0055219D /* DelegateProxy.swift */; }; C894656F1BC6C2BC0055219D /* DelegateProxyType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465211BC6C2BC0055219D /* DelegateProxyType.swift */; }; C89465701BC6C2BC0055219D /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465221BC6C2BC0055219D /* Logging.swift */; }; @@ -440,6 +442,14 @@ 07E300081B149A2A00F00100 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 07E3C2321B03605B0010338D /* Dependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Dependencies.swift; path = Examples/Dependencies.swift; sourceTree = ""; }; C80DDE7A1BCDA952006A1832 /* SkipWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipWhile.swift; sourceTree = ""; }; + C80DDEC21BCE9041006A1832 /* ControlEvent+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControlEvent+Driver.swift"; sourceTree = ""; }; + C80DDEC31BCE9041006A1832 /* ControlProperty+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControlProperty+Driver.swift"; sourceTree = ""; }; + C80DDEC41BCE9041006A1832 /* Driver+Operators+arity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Driver+Operators+arity.swift"; sourceTree = ""; }; + C80DDEC51BCE9041006A1832 /* Driver+Operators+arity.tt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Driver+Operators+arity.tt"; sourceTree = ""; }; + C80DDEC61BCE9041006A1832 /* Driver+Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Driver+Operators.swift"; sourceTree = ""; }; + C80DDEC71BCE9041006A1832 /* Driver+Subscription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Driver+Subscription.swift"; sourceTree = ""; }; + C80DDEC81BCE9041006A1832 /* Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Driver.swift; sourceTree = ""; }; + C80DDEC91BCE9041006A1832 /* ObservableConvertibleType+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObservableConvertibleType+Driver.swift"; sourceTree = ""; }; C81B39F11BC1C28400EF5A9F /* Rx.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Rx.xcodeproj; path = ../Rx.xcodeproj; sourceTree = ""; }; C8297E691B6CF905000589EA /* RxExample-iOS-no-module.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "RxExample-iOS-no-module.app"; sourceTree = BUILT_PRODUCTS_DIR; }; C83366DD1AD0293800C668A7 /* RxExample-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "RxExample-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -589,14 +599,8 @@ C89465141BC6C2BC0055219D /* _RXSwizzling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _RXSwizzling.h; sourceTree = ""; }; C89465151BC6C2BC0055219D /* _RXSwizzling.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _RXSwizzling.m; sourceTree = ""; }; C89465161BC6C2BC0055219D /* CLLocationManager+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CLLocationManager+Rx.swift"; sourceTree = ""; }; - C89465181BC6C2BC0055219D /* ControlEvent+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControlEvent+Driver.swift"; sourceTree = ""; }; C89465191BC6C2BC0055219D /* ControlEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlEvent.swift; sourceTree = ""; }; - C894651A1BC6C2BC0055219D /* ControlProperty+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControlProperty+Driver.swift"; sourceTree = ""; }; C894651B1BC6C2BC0055219D /* ControlProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlProperty.swift; sourceTree = ""; }; - C894651C1BC6C2BC0055219D /* Driver+Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Driver+Operators.swift"; sourceTree = ""; }; - C894651D1BC6C2BC0055219D /* Driver+Subscription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Driver+Subscription.swift"; sourceTree = ""; }; - C894651E1BC6C2BC0055219D /* Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Driver.swift; sourceTree = ""; }; - C894651F1BC6C2BC0055219D /* ObservableConvertibleType+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObservableConvertibleType+Driver.swift"; sourceTree = ""; }; C89465201BC6C2BC0055219D /* DelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxy.swift; sourceTree = ""; }; C89465211BC6C2BC0055219D /* DelegateProxyType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxyType.swift; sourceTree = ""; }; C89465221BC6C2BC0055219D /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = ""; }; @@ -762,6 +766,21 @@ path = TableView; sourceTree = ""; }; + C80DDEC11BCE9041006A1832 /* Driver */ = { + isa = PBXGroup; + children = ( + C80DDEC21BCE9041006A1832 /* ControlEvent+Driver.swift */, + C80DDEC31BCE9041006A1832 /* ControlProperty+Driver.swift */, + C80DDEC41BCE9041006A1832 /* Driver+Operators+arity.swift */, + C80DDEC51BCE9041006A1832 /* Driver+Operators+arity.tt */, + C80DDEC61BCE9041006A1832 /* Driver+Operators.swift */, + C80DDEC71BCE9041006A1832 /* Driver+Subscription.swift */, + C80DDEC81BCE9041006A1832 /* Driver.swift */, + C80DDEC91BCE9041006A1832 /* ObservableConvertibleType+Driver.swift */, + ); + path = Driver; + sourceTree = ""; + }; C81B39F21BC1C28400EF5A9F /* Products */ = { isa = PBXGroup; children = ( @@ -1183,14 +1202,9 @@ C89465171BC6C2BC0055219D /* CocoaUnits */ = { isa = PBXGroup; children = ( - C89465181BC6C2BC0055219D /* ControlEvent+Driver.swift */, + C80DDEC11BCE9041006A1832 /* Driver */, C89465191BC6C2BC0055219D /* ControlEvent.swift */, - C894651A1BC6C2BC0055219D /* ControlProperty+Driver.swift */, C894651B1BC6C2BC0055219D /* ControlProperty.swift */, - C894651C1BC6C2BC0055219D /* Driver+Operators.swift */, - C894651D1BC6C2BC0055219D /* Driver+Subscription.swift */, - C894651E1BC6C2BC0055219D /* Driver.swift */, - C894651F1BC6C2BC0055219D /* ObservableConvertibleType+Driver.swift */, ); path = CocoaUnits; sourceTree = ""; @@ -1536,6 +1550,7 @@ C8297E601B6CF905000589EA /* Images.xcassets in Resources */, C8297E611B6CF905000589EA /* Main.storyboard in Resources */, C894657E1BC6C2BC0055219D /* Info.plist in Resources */, + C80DDED51BCE9046006A1832 /* Driver+Operators+arity.tt in Resources */, C89464B61BC6C2B00055219D /* Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1568,7 +1583,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - C894656D1BC6C2BC0055219D /* ObservableConvertibleType+Driver.swift in Sources */, C89465971BC6C2BC0055219D /* UIScrollView+Rx.swift in Sources */, C8297E2F1B6CF905000589EA /* RxTableViewSectionedAnimatedDataSource.swift in Sources */, C89464FF1BC6C2B00055219D /* DispatchQueueSchedulerPriority.swift in Sources */, @@ -1600,6 +1614,8 @@ C89464ED1BC6C2B00055219D /* Observable+Concurrency.swift in Sources */, C89464DA1BC6C2B00055219D /* Repeat.swift in Sources */, C89465651BC6C2BC0055219D /* CLLocationManager+Rx.swift in Sources */, + C80DDED81BCE9046006A1832 /* Driver.swift in Sources */, + C80DDED71BCE9046006A1832 /* Driver+Subscription.swift in Sources */, 07A5C3DC1B70B703001EFE5C /* CalculatorViewController.swift in Sources */, C89465961BC6C2BC0055219D /* UILabel+Rx.swift in Sources */, C894659C1BC6C2BC0055219D /* UISwitch+Rx.swift in Sources */, @@ -1608,6 +1624,7 @@ C89465751BC6C2BC0055219D /* KVOObservable.swift in Sources */, C89464CB1BC6C2B00055219D /* FailWith.swift in Sources */, C8297E341B6CF905000589EA /* UIImageView+Extensions.swift in Sources */, + C80DDED61BCE9046006A1832 /* Driver+Operators.swift in Sources */, C89465051BC6C2B00055219D /* SerialDispatchQueueScheduler.swift in Sources */, C89464B81BC6C2B00055219D /* Observable.swift in Sources */, C89465001BC6C2B00055219D /* MainScheduler.swift in Sources */, @@ -1617,7 +1634,6 @@ C89464A71BC6C2B00055219D /* BinaryDisposable.swift in Sources */, C89465741BC6C2BC0055219D /* DeinitAction.swift in Sources */, C8297E361B6CF905000589EA /* RootViewController.swift in Sources */, - C894656C1BC6C2BC0055219D /* Driver.swift in Sources */, C89464BB1BC6C2B00055219D /* AnonymousObservable.swift in Sources */, C89465991BC6C2BC0055219D /* UISegmentedControl+Rx.swift in Sources */, C8297E371B6CF905000589EA /* RxCollectionViewSectionedDataSource.swift in Sources */, @@ -1633,7 +1649,6 @@ C89464F01BC6C2B00055219D /* Observable+Multiple.swift in Sources */, C89464DF1BC6C2B00055219D /* StartWith.swift in Sources */, C89465881BC6C2BC0055219D /* RxScrollViewDelegateProxy.swift in Sources */, - C894656B1BC6C2BC0055219D /* Driver+Subscription.swift in Sources */, C89464B71BC6C2B00055219D /* Observable+Extensions.swift in Sources */, C89464A01BC6C2B00055219D /* Lock.swift in Sources */, C89464C91BC6C2B00055219D /* Do.swift in Sources */, @@ -1649,12 +1664,14 @@ C8297E3A1B6CF905000589EA /* WikipediaSearchViewController.swift in Sources */, C89464F21BC6C2B00055219D /* Observable+StandardSequenceOperators.swift in Sources */, C89464CC1BC6C2B00055219D /* Filter.swift in Sources */, + C80DDED31BCE9046006A1832 /* ControlProperty+Driver.swift in Sources */, C89465021BC6C2B00055219D /* RecursiveScheduler.swift in Sources */, C89464C11BC6C2B00055219D /* CombineLatest+CollectionType.swift in Sources */, C89465671BC6C2BC0055219D /* ControlEvent.swift in Sources */, C8297E3B1B6CF905000589EA /* String+extensions.swift in Sources */, C89464A61BC6C2B00055219D /* AnonymousDisposable.swift in Sources */, C894657D1BC6C2BC0055219D /* RxTarget.swift in Sources */, + C80DDED21BCE9046006A1832 /* ControlEvent+Driver.swift in Sources */, C89464EE1BC6C2B00055219D /* Observable+Creation.swift in Sources */, C894659A1BC6C2BC0055219D /* UISlider+Rx.swift in Sources */, C89465891BC6C2BC0055219D /* RxSearchBarDelegateProxy.swift in Sources */, @@ -1689,6 +1706,7 @@ C89464A31BC6C2B00055219D /* InfiniteSequence.swift in Sources */, C89465041BC6C2B00055219D /* SchedulerServices+Emulation.swift in Sources */, C89465611BC6C2BC0055219D /* _RX.m in Sources */, + C80DDED41BCE9046006A1832 /* Driver+Operators+arity.swift in Sources */, C89465841BC6C2BC0055219D /* RxActionSheetDelegateProxy.swift in Sources */, C89464F51BC6C2B00055219D /* ObserverOf.swift in Sources */, C89464D71BC6C2B00055219D /* Range.swift in Sources */, @@ -1713,7 +1731,6 @@ C89CDB721BCC45EE002063D9 /* SkipUntil.swift in Sources */, C8297E471B6CF905000589EA /* ViewController.swift in Sources */, C89464E41BC6C2B00055219D /* TakeWhile.swift in Sources */, - C89465681BC6C2BC0055219D /* ControlProperty+Driver.swift in Sources */, C89464F71BC6C2B00055219D /* ObserverBase.swift in Sources */, C89465951BC6C2BC0055219D /* UIImageView+Rx.swift in Sources */, C89464E31BC6C2B00055219D /* TakeUntil.swift in Sources */, @@ -1750,6 +1767,7 @@ C8297E511B6CF905000589EA /* PartialUpdatesViewController.swift in Sources */, C894649F1BC6C2B00055219D /* AsyncLock.swift in Sources */, C8297E521B6CF905000589EA /* Dependencies.swift in Sources */, + C80DDED91BCE9046006A1832 /* ObservableConvertibleType+Driver.swift in Sources */, C89464E11BC6C2B00055219D /* Switch.swift in Sources */, C89465851BC6C2BC0055219D /* RxAlertViewDelegateProxy.swift in Sources */, C89464A11BC6C2B00055219D /* ConnectableObservableType.swift in Sources */, @@ -1763,13 +1781,11 @@ C8297E541B6CF905000589EA /* AppDelegate.swift in Sources */, C894659D1BC6C2BC0055219D /* UITableView+Rx.swift in Sources */, C894657F1BC6C2BC0055219D /* RxCollectionViewReactiveArrayDataSource.swift in Sources */, - C894656A1BC6C2BC0055219D /* Driver+Operators.swift in Sources */, C8297E551B6CF905000589EA /* RxTableViewSectionedDataSource.swift in Sources */, C8297E561B6CF905000589EA /* WikipediaPage.swift in Sources */, C8297E571B6CF905000589EA /* Randomizer.swift in Sources */, C89464C31BC6C2B00055219D /* Concat.swift in Sources */, C894657A1BC6C2BC0055219D /* NSURLSession+Rx.swift in Sources */, - C89465661BC6C2BC0055219D /* ControlEvent+Driver.swift in Sources */, C89464F41BC6C2B00055219D /* ObservableType.swift in Sources */, C89464CE1BC6C2B00055219D /* Generate.swift in Sources */, C89465711BC6C2BC0055219D /* Observable+Bind.swift in Sources */, diff --git a/RxSwift/Observables/Implementations/CombineLatest+arity.swift b/RxSwift/Observables/Implementations/CombineLatest+arity.swift index 31df249f..8f1e424b 100644 --- a/RxSwift/Observables/Implementations/CombineLatest+arity.swift +++ b/RxSwift/Observables/Implementations/CombineLatest+arity.swift @@ -21,7 +21,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func combineLatest - (source1: O1, _ source2: O2, _ resultSelector: (O1.E, O2.E) throws -> R) + (source1: O1, _ source2: O2, resultSelector: (O1.E, O2.E) throws -> R) -> Observable { return CombineLatest2( source1: source1.asObservable(), source2: source2.asObservable(), @@ -97,7 +97,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func combineLatest - (source1: O1, _ source2: O2, _ source3: O3, _ resultSelector: (O1.E, O2.E, O3.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, resultSelector: (O1.E, O2.E, O3.E) throws -> R) -> Observable { return CombineLatest3( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), @@ -180,7 +180,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func combineLatest - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ resultSelector: (O1.E, O2.E, O3.E, O4.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: (O1.E, O2.E, O3.E, O4.E) throws -> R) -> Observable { return CombineLatest4( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), @@ -270,7 +270,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func combineLatest - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> R) -> Observable { return CombineLatest5( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), @@ -367,7 +367,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func combineLatest - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> R) -> Observable { return CombineLatest6( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), @@ -471,7 +471,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func combineLatest - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> R) -> Observable { return CombineLatest7( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), @@ -582,7 +582,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func combineLatest - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> R) -> Observable { return CombineLatest8( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), source8: source8.asObservable(), diff --git a/RxSwift/Observables/Implementations/CombineLatest+arity.tt b/RxSwift/Observables/Implementations/CombineLatest+arity.tt index 5eafbfd5..47706697 100644 --- a/RxSwift/Observables/Implementations/CombineLatest+arity.tt +++ b/RxSwift/Observables/Implementations/CombineLatest+arity.tt @@ -19,7 +19,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func combineLatest<<%= (Array(1...i).map { "O\($0): ObservableType" }).joinWithSeparator(", ") %>, R> - (<%= (Array(1...i).map { "source\($0): O\($0)" }).joinWithSeparator(", _ ") %>, _ resultSelector: (<%= (Array(1...i).map { "O\($0).E" }).joinWithSeparator(", ") %>) throws -> R) + (<%= (Array(1...i).map { "source\($0): O\($0)" }).joinWithSeparator(", _ ") %>, resultSelector: (<%= (Array(1...i).map { "O\($0).E" }).joinWithSeparator(", ") %>) throws -> R) -> Observable { return CombineLatest<%= i %>( <%= (Array(1...i).map { "source\($0): source\($0).asObservable()" }).joinWithSeparator(", ") %>, diff --git a/RxSwift/Observables/Implementations/Zip+CollectionType.swift b/RxSwift/Observables/Implementations/Zip+CollectionType.swift index 15d4d685..b51b0044 100644 --- a/RxSwift/Observables/Implementations/Zip+CollectionType.swift +++ b/RxSwift/Observables/Implementations/Zip+CollectionType.swift @@ -8,7 +8,7 @@ import Foundation -class ZipCollectionTypeSink : Sink { +class ZipCollectionTypeSink : Sink { typealias Parent = ZipCollectionType typealias SourceElement = C.Generator.Element.E @@ -104,7 +104,8 @@ class ZipCollectionTypeSink : Producer { +class ZipCollectionType : Producer { typealias ResultSelector = [C.Generator.Element.E] throws -> R let sources: C diff --git a/RxSwift/Observables/Implementations/Zip+arity.swift b/RxSwift/Observables/Implementations/Zip+arity.swift index 1996fec5..964ce655 100644 --- a/RxSwift/Observables/Implementations/Zip+arity.swift +++ b/RxSwift/Observables/Implementations/Zip+arity.swift @@ -21,7 +21,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func zip - (source1: O1, _ source2: O2, _ resultSelector: (O1.E, O2.E) throws -> R) + (source1: O1, _ source2: O2, resultSelector: (O1.E, O2.E) throws -> R) -> Observable { return Zip2( source1: source1.asObservable(), source2: source2.asObservable(), @@ -109,7 +109,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func zip - (source1: O1, _ source2: O2, _ source3: O3, _ resultSelector: (O1.E, O2.E, O3.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, resultSelector: (O1.E, O2.E, O3.E) throws -> R) -> Observable { return Zip3( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), @@ -205,7 +205,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func zip - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ resultSelector: (O1.E, O2.E, O3.E, O4.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: (O1.E, O2.E, O3.E, O4.E) throws -> R) -> Observable { return Zip4( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), @@ -309,7 +309,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func zip - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> R) -> Observable { return Zip5( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), @@ -421,7 +421,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func zip - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> R) -> Observable { return Zip6( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), @@ -541,7 +541,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func zip - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> R) -> Observable { return Zip7( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), @@ -669,7 +669,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func zip - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> R) -> Observable { return Zip8( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), source8: source8.asObservable(), diff --git a/RxSwift/Observables/Implementations/Zip+arity.tt b/RxSwift/Observables/Implementations/Zip+arity.tt index e544bb5a..456b3313 100644 --- a/RxSwift/Observables/Implementations/Zip+arity.tt +++ b/RxSwift/Observables/Implementations/Zip+arity.tt @@ -19,7 +19,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func zip<<%= (Array(1...i).map { "O\($0): ObservableType" }).joinWithSeparator(", ") %>, R> - (<%= (Array(1...i).map { "source\($0): O\($0)" }).joinWithSeparator(", _ ") %>, _ resultSelector: (<%= (Array(1...i).map { "O\($0).E" }).joinWithSeparator(", ") %>) throws -> R) + (<%= (Array(1...i).map { "source\($0): O\($0)" }).joinWithSeparator(", _ ") %>, resultSelector: (<%= (Array(1...i).map { "O\($0).E" }).joinWithSeparator(", ") %>) throws -> R) -> Observable { return Zip<%= i %>( <%= (Array(1...i).map { "source\($0): source\($0).asObservable()" }).joinWithSeparator(", ") %>, diff --git a/RxSwift/Observables/Observable+Multiple.swift b/RxSwift/Observables/Observable+Multiple.swift index 0ed6e70a..7b8f1f48 100644 --- a/RxSwift/Observables/Observable+Multiple.swift +++ b/RxSwift/Observables/Observable+Multiple.swift @@ -25,7 +25,7 @@ extension CollectionType where Generator.Element : ObservableType { // zip -extension CollectionType where Generator.Element : ObservableType { +extension CollectionType where Generator.Element : ObservableConvertibleType { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index. diff --git a/RxTests/RxSwiftTests/Tests/VariableTest.swift b/RxTests/RxSwiftTests/Tests/VariableTest.swift index e65bdcc5..33add24d 100644 --- a/RxTests/RxSwiftTests/Tests/VariableTest.swift +++ b/RxTests/RxSwiftTests/Tests/VariableTest.swift @@ -15,7 +15,7 @@ class VariableTest : RxTest { let a = Variable(1) let b = Variable(2) - let c = combineLatest(a, b, +) + let c = combineLatest(a, b, resultSelector: +) var latestValue: Int? From f9b19afd10248a5204f0202e6945ec6d79035ee0 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Thu, 15 Oct 2015 23:11:09 +0200 Subject: [PATCH 038/210] Adds unit tests for `Driver` unit. --- .../Driver/Driver+Operators+arity.swift | 28 +- .../Driver/Driver+Operators+arity.tt | 4 +- .../CocoaUnits/Driver/Driver+Operators.swift | 42 +- .../ObservableConvertibleType+Driver.swift | 9 +- .../CombineLatest+CollectionType.swift | 7 +- RxSwift/Observables/Observable+Multiple.swift | 2 +- .../TestImplementations/EquatableArray.swift | 20 + .../MainThreadPrimitiveHotObservable.swift | 18 + .../Observable+Extensions.swift | 9 + .../TestImplementations/Recorded.swift | 12 +- .../TestImplementations/TestExtensions.swift | 14 - RxTests/RxSwiftTests/Tests/Driver+Test.swift | 610 ++++++++++++++++++ .../Tests/Observable+BlockingTest.swift | 4 - .../Tests/Observable+ConcurrencyTest.swift | 3 - .../Tests/Observable+TimeTest.swift | 8 - RxTests/RxTest.swift | 19 +- RxTests/RxTests.xcodeproj/project.pbxproj | 32 +- 17 files changed, 742 insertions(+), 99 deletions(-) create mode 100644 RxTests/RxSwiftTests/TestImplementations/EquatableArray.swift create mode 100644 RxTests/RxSwiftTests/TestImplementations/Mocks/MainThreadPrimitiveHotObservable.swift delete mode 100644 RxTests/RxSwiftTests/TestImplementations/TestExtensions.swift create mode 100644 RxTests/RxSwiftTests/Tests/Driver+Test.swift diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.swift index 9d51cd7e..8ca3a622 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.swift @@ -24,7 +24,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func zip - (source1: O1, _ source2: O2, _ resultSelector: (O1.E, O2.E) throws -> R) + (source1: O1, _ source2: O2, resultSelector: (O1.E, O2.E) throws -> R) -> Driver { let source = zip( source1.asDriver().asObservable(), source2.asDriver().asObservable(), @@ -41,7 +41,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func combineLatest - (source1: O1, _ source2: O2, _ resultSelector: (O1.E, O2.E) throws -> R) + (source1: O1, _ source2: O2, resultSelector: (O1.E, O2.E) throws -> R) -> Driver { let source = combineLatest( source1.asDriver().asObservable(), source2.asDriver().asObservable(), @@ -62,7 +62,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func zip - (source1: O1, _ source2: O2, _ source3: O3, _ resultSelector: (O1.E, O2.E, O3.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, resultSelector: (O1.E, O2.E, O3.E) throws -> R) -> Driver { let source = zip( source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), @@ -79,7 +79,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func combineLatest - (source1: O1, _ source2: O2, _ source3: O3, _ resultSelector: (O1.E, O2.E, O3.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, resultSelector: (O1.E, O2.E, O3.E) throws -> R) -> Driver { let source = combineLatest( source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), @@ -100,7 +100,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func zip - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ resultSelector: (O1.E, O2.E, O3.E, O4.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: (O1.E, O2.E, O3.E, O4.E) throws -> R) -> Driver { let source = zip( source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), @@ -117,7 +117,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func combineLatest - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ resultSelector: (O1.E, O2.E, O3.E, O4.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: (O1.E, O2.E, O3.E, O4.E) throws -> R) -> Driver { let source = combineLatest( source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), @@ -138,7 +138,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func zip - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> R) -> Driver { let source = zip( source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), source5.asDriver().asObservable(), @@ -155,7 +155,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func combineLatest - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> R) -> Driver { let source = combineLatest( source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), source5.asDriver().asObservable(), @@ -176,7 +176,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func zip - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> R) -> Driver { let source = zip( source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), source5.asDriver().asObservable(), source6.asDriver().asObservable(), @@ -193,7 +193,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func combineLatest - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> R) -> Driver { let source = combineLatest( source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), source5.asDriver().asObservable(), source6.asDriver().asObservable(), @@ -214,7 +214,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func zip - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> R) -> Driver { let source = zip( source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), source5.asDriver().asObservable(), source6.asDriver().asObservable(), source7.asDriver().asObservable(), @@ -231,7 +231,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func combineLatest - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> R) -> Driver { let source = combineLatest( source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), source5.asDriver().asObservable(), source6.asDriver().asObservable(), source7.asDriver().asObservable(), @@ -252,7 +252,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func zip - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> R) -> Driver { let source = zip( source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), source5.asDriver().asObservable(), source6.asDriver().asObservable(), source7.asDriver().asObservable(), source8.asDriver().asObservable(), @@ -269,7 +269,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func combineLatest - (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, _ resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> R) + (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> R) -> Driver { let source = combineLatest( source1.asDriver().asObservable(), source2.asDriver().asObservable(), source3.asDriver().asObservable(), source4.asDriver().asObservable(), source5.asDriver().asObservable(), source6.asDriver().asObservable(), source7.asDriver().asObservable(), source8.asDriver().asObservable(), diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.tt b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.tt index 781da9cd..349b85fa 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.tt +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.tt @@ -22,7 +22,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func zip<<%= (Array(1...i).map { "O\($0): DriverConvertibleType" }).joinWithSeparator(", ") %>, R> - (<%= (Array(1...i).map { "source\($0): O\($0)" }).joinWithSeparator(", _ ") %>, _ resultSelector: (<%= (Array(1...i).map { "O\($0).E" }).joinWithSeparator(", ") %>) throws -> R) + (<%= (Array(1...i).map { "source\($0): O\($0)" }).joinWithSeparator(", _ ") %>, resultSelector: (<%= (Array(1...i).map { "O\($0).E" }).joinWithSeparator(", ") %>) throws -> R) -> Driver { let source = zip( <%= (Array(1...i).map { "source\($0).asDriver().asObservable()" }).joinWithSeparator(", ") %>, @@ -39,7 +39,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public func combineLatest<<%= (Array(1...i).map { "O\($0): DriverConvertibleType" }).joinWithSeparator(", ") %>, R> - (<%= (Array(1...i).map { "source\($0): O\($0)" }).joinWithSeparator(", _ ") %>, _ resultSelector: (<%= (Array(1...i).map { "O\($0).E" }).joinWithSeparator(", ") %>) throws -> R) + (<%= (Array(1...i).map { "source\($0): O\($0)" }).joinWithSeparator(", _ ") %>, resultSelector: (<%= (Array(1...i).map { "O\($0).E" }).joinWithSeparator(", ") %>) throws -> R) -> Driver { let source = combineLatest( <%= (Array(1...i).map { "source\($0).asDriver().asObservable()" }).joinWithSeparator(", ") %>, diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift index 7d49271f..f535a521 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift @@ -25,18 +25,6 @@ extension Driver { return Driver(source) } - /** - Projects each element of an observable sequence into a new form by incorporating the element's index. - - - parameter selector: A transform function to apply to each source element; the second parameter of the function represents the index of the source element. - - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source. - */ - public func mapWithIndex(selector: (E, Int) -> R) -> Driver { - let source = _source - .mapWithIndex(selector) - return Driver(source) - } - /** Filters the elements of an observable sequence based on a predicate. @@ -127,7 +115,7 @@ extension Driver where Element: Equatable { public func distinctUntilChanged() -> Driver { let source = _source - .self.distinctUntilChanged({ $0 }, comparer: { ($0 == $1) }) + .distinctUntilChanged({ $0 }, comparer: { ($0 == $1) }) return Driver(source) } @@ -188,20 +176,6 @@ extension Driver { return Driver(source) } - - /** - Projects each element of an observable sequence to an observable sequence by incorporating the element's index and merges the resulting observable sequences into one observable sequence. - - - parameter selector: A transform function to apply to each element; the second parameter of the function represents the index of the source element. - - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence. - */ - public func flatMapWithIndex(selector: (E, Int) -> Driver) - -> Driver { - let source = _source - .flatMapWithIndex(selector) - - return Driver(source.asObservable()) - } } // merge @@ -317,4 +291,18 @@ extension CollectionType where Generator.Element : DriverConvertibleType { let source: Observable = self.map { $0.asDriver() }.zip(resultSelector) return Driver(source) } +} + +extension CollectionType where Generator.Element : DriverConvertibleType { + + /** + Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. + + - parameter resultSelector: Function to invoke whenever any of the sources produces an element. + - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. + */ + public func combineLatest(resultSelector: [Generator.Element.E] throws -> R) -> Driver { + let source : Observable = self.map { $0.asDriver() }.combineLatest(resultSelector) + return Driver(source) + } } \ No newline at end of file diff --git a/RxCocoa/Common/CocoaUnits/Driver/ObservableConvertibleType+Driver.swift b/RxCocoa/Common/CocoaUnits/Driver/ObservableConvertibleType+Driver.swift index d6ea653c..a89dfc3b 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/ObservableConvertibleType+Driver.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/ObservableConvertibleType+Driver.swift @@ -21,8 +21,9 @@ extension ObservableConvertibleType { public func asDriver(onErrorJustReturn onErrorJustReturn: E) -> Driver { let source = self .asObservable() - .catchErrorJustReturn(onErrorJustReturn) + .subscribeOn(MainScheduler.sharedInstance) .observeOn(MainScheduler.sharedInstance) + .catchErrorJustReturn(onErrorJustReturn) return Driver(source) } @@ -35,10 +36,11 @@ extension ObservableConvertibleType { public func asDriver(onErrorDriveWith onErrorDriveWith: Driver) -> Driver { let source = self .asObservable() + .subscribeOn(MainScheduler.sharedInstance) + .observeOn(MainScheduler.sharedInstance) .catchError { _ in onErrorDriveWith.asObservable() } - .observeOn(MainScheduler.sharedInstance) return Driver(source) } @@ -51,10 +53,11 @@ extension ObservableConvertibleType { public func asDriver(onErrorRecover onErrorRecover: (error: ErrorType) -> Driver) -> Driver { let source = self .asObservable() + .subscribeOn(MainScheduler.sharedInstance) + .observeOn(MainScheduler.sharedInstance) .catchError { error in onErrorRecover(error: error).asObservable() } - .observeOn(MainScheduler.sharedInstance) return Driver(source) } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift b/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift index 204fb6ca..4da0028b 100644 --- a/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift +++ b/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift @@ -8,7 +8,7 @@ import Foundation -class CombineLatestCollectionTypeSink : Sink { +class CombineLatestCollectionTypeSink : Sink { typealias Parent = CombineLatestCollectionType typealias SourceElement = C.Generator.Element.E @@ -91,7 +91,8 @@ class CombineLatestCollectionTypeSink : Producer { +class CombineLatestCollectionType : Producer { typealias ResultSelector = [C.Generator.Element.E] throws -> R let sources: C diff --git a/RxSwift/Observables/Observable+Multiple.swift b/RxSwift/Observables/Observable+Multiple.swift index 7b8f1f48..a5b6e23b 100644 --- a/RxSwift/Observables/Observable+Multiple.swift +++ b/RxSwift/Observables/Observable+Multiple.swift @@ -10,7 +10,7 @@ import Foundation // combineLatest -extension CollectionType where Generator.Element : ObservableType { +extension CollectionType where Generator.Element : ObservableConvertibleType { /** Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element. diff --git a/RxTests/RxSwiftTests/TestImplementations/EquatableArray.swift b/RxTests/RxSwiftTests/TestImplementations/EquatableArray.swift new file mode 100644 index 00000000..4fda0037 --- /dev/null +++ b/RxTests/RxSwiftTests/TestImplementations/EquatableArray.swift @@ -0,0 +1,20 @@ +// +// EquatableArray.swift +// RxTests +// +// Created by Krunoslav Zaher on 10/15/15. +// +// + +import Foundation + +struct EquatableArray : Equatable { + let elements: [Element] + init(_ elements: [Element]) { + self.elements = elements + } +} + +func == (lhs: EquatableArray, rhs: EquatableArray) -> Bool { + return lhs.elements == rhs.elements +} diff --git a/RxTests/RxSwiftTests/TestImplementations/Mocks/MainThreadPrimitiveHotObservable.swift b/RxTests/RxSwiftTests/TestImplementations/Mocks/MainThreadPrimitiveHotObservable.swift new file mode 100644 index 00000000..28870dc4 --- /dev/null +++ b/RxTests/RxSwiftTests/TestImplementations/Mocks/MainThreadPrimitiveHotObservable.swift @@ -0,0 +1,18 @@ +// +// MainThreadPrimitiveHotObservable.swift +// RxTests +// +// Created by Krunoslav Zaher on 10/14/15. +// +// + +import Foundation +import RxSwift +import XCTest + +class MainThreadPrimitiveHotObservable : PrimitiveHotObservable { + override func subscribe(observer: O) -> Disposable { + XCTAssertTrue(NSThread.isMainThread()) + return super.subscribe(observer) + } +} \ No newline at end of file diff --git a/RxTests/RxSwiftTests/TestImplementations/Observable+Extensions.swift b/RxTests/RxSwiftTests/TestImplementations/Observable+Extensions.swift index a468879f..b7cdb388 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Observable+Extensions.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Observable+Extensions.swift @@ -8,6 +8,7 @@ import Foundation import RxSwift +import RxCocoa public func == (lhs: Observable, rhs: Observable) -> Bool { return lhs === rhs @@ -27,4 +28,12 @@ func == (lhs: HotObservable, rhs: HotObservable) -> Bool { func == (lhs: ColdObservable, rhs: ColdObservable) -> Bool { return lhs === rhs +} + +extension Driver : Equatable { + +} + +public func == (lhs: Driver, rhs: Driver) -> Bool { + return lhs.asObservable() === rhs.asObservable() } \ No newline at end of file diff --git a/RxTests/RxSwiftTests/TestImplementations/Recorded.swift b/RxTests/RxSwiftTests/TestImplementations/Recorded.swift index cfb227a0..606f3a2e 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Recorded.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Recorded.swift @@ -8,6 +8,7 @@ import Foundation import RxSwift +import Swift struct Recorded : CustomStringConvertible, Equatable { let time: Time @@ -43,14 +44,3 @@ func == (lhs: Recorded, rhs: Recorded) -> Bool { } -// workaround for swift compiler bug -struct EquatableArray : Equatable { - let elements: [Element] - init(_ elements: [Element]) { - self.elements = elements - } -} - -func == (lhs: EquatableArray, rhs: EquatableArray) -> Bool { - return lhs.elements == rhs.elements -} \ No newline at end of file diff --git a/RxTests/RxSwiftTests/TestImplementations/TestExtensions.swift b/RxTests/RxSwiftTests/TestImplementations/TestExtensions.swift deleted file mode 100644 index bc799558..00000000 --- a/RxTests/RxSwiftTests/TestImplementations/TestExtensions.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// TestExtensions.swift -// Rx -// -// Created by Krunoslav Zaher on 2/15/15. -// Copyright (c) 2015 Krunoslav Zaher. All rights reserved. -// - -import Foundation -import XCTest - -func assertEquals(lhs: T, rhs: T) { - XCTAssertTrue(lhs == rhs) -} \ No newline at end of file diff --git a/RxTests/RxSwiftTests/Tests/Driver+Test.swift b/RxTests/RxSwiftTests/Tests/Driver+Test.swift new file mode 100644 index 00000000..95bc89a8 --- /dev/null +++ b/RxTests/RxSwiftTests/Tests/Driver+Test.swift @@ -0,0 +1,610 @@ +// +// Driver+Test.swift +// RxTests +// +// Created by Krunoslav Zaher on 10/14/15. +// +// + +import Foundation +import RxSwift +import RxCocoa +import XCTest + +class DriverTest : RxTest { + var backgroundScheduler = SerialDispatchQueueScheduler(globalConcurrentQueuePriority: .Default) + + override func tearDown() { + super.tearDown() + } +} + +// test helpers that make sure that resulting driver operator honors definition +// * only one subscription is made and shared - shareReplay(1) +// * subscription is made on main thread - subscribeOn(MainScheduler.sharedInstance) +// * events are observed on main thread - observeOn(MainScheduler.sharedInstance) +// * it can't error out - it needs to have catch somewhere +extension DriverTest { + + func subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver: Driver, subscribedOnBackground: () -> ()) -> [R] { + var firstElements = [R]() + var secondElements = [R]() + + let subscribeFinished = self.expectationWithDescription("subscribeFinished") + + var expectation1: XCTestExpectation! + var expectation2: XCTestExpectation! + + backgroundScheduler.schedule(()) { _ in + _ = driver.asObservable().subscribe { e in + XCTAssertTrue(NSThread.isMainThread()) + switch e { + case .Next(let element): + firstElements.append(element) + case .Error(let error): + XCTFail("Error passed \(error)") + case .Completed: + expectation1.fulfill() + } + } + _ = driver.asDriver().asObservable().subscribe { e in + XCTAssertTrue(NSThread.isMainThread()) + switch e { + case .Next(let element): + secondElements.append(element) + case .Error(let error): + XCTFail("Error passed \(error)") + case .Completed: + expectation2.fulfill() + } + } + + // Subscription should be made on main scheduler + // so this will make sure execution is continued after + // subscription because of serial nature of main scheduler. + MainScheduler.sharedInstance.schedule(()) { _ in + subscribeFinished.fulfill() + return NopDisposable.instance + + } + + return NopDisposable.instance + } + + waitForExpectationsWithTimeout(1.0) { error in + XCTAssertTrue(error == nil) + } + + expectation1 = self.expectationWithDescription("finished1") + expectation2 = self.expectationWithDescription("finished2") + + subscribedOnBackground() + + waitForExpectationsWithTimeout(1.0) { error in + XCTAssertTrue(error == nil) + } + + XCTAssertTrue(firstElements == secondElements) + + return firstElements + } +} + +// conversions +extension DriverTest { + func testAsDriver_onErrorJustReturn() { + let hotObservable = MainThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorJustReturn: -1) + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(1)) + hotObservable.on(.Next(2)) + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [1, 2, -1]) + } + + func testAsDriver_onErrorDriveWith() { + let hotObservable = MainThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorDriveWith: Drive.just(-1)) + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(1)) + hotObservable.on(.Next(2)) + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [1, 2, -1]) + } + + func testAsDriver_onErrorRecover() { + let hotObservable = MainThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver { e in + return Drive.empty() + } + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(1)) + hotObservable.on(.Next(2)) + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [1, 2]) + } +} + +// map +extension DriverTest { + func testAsDriver_map() { + let hotObservable = MainThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorJustReturn: -1).map { (n: Int) -> Int in + XCTAssertTrue(NSThread.isMainThread()) + return n + 1 + } + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(1)) + hotObservable.on(.Next(2)) + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [2, 3, 0]) + } + +} + +// filter +extension DriverTest { + func testAsDriver_filter() { + let hotObservable = MainThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorJustReturn: -1).filter { n in + XCTAssertTrue(NSThread.isMainThread()) + return n % 2 == 0 + } + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(1)) + hotObservable.on(.Next(2)) + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [2]) + } +} + + +// switch latest +extension DriverTest { + func testAsDriver_switchLatest() { + let hotObservable = MainThreadPrimitiveHotObservable>() + let hotObservable1 = MainThreadPrimitiveHotObservable() + let hotObservable2 = MainThreadPrimitiveHotObservable() + + let driver = hotObservable.asDriver(onErrorJustReturn: hotObservable1.asDriver(onErrorJustReturn: -1)).switchLatest() + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(hotObservable1.asDriver(onErrorJustReturn: -2))) + + hotObservable1.on(.Next(1)) + hotObservable1.on(.Next(2)) + hotObservable1.on(.Error(testError)) + + hotObservable.on(.Next(hotObservable2.asDriver(onErrorJustReturn: -3))) + + hotObservable2.on(.Next(10)) + hotObservable2.on(.Next(11)) + hotObservable2.on(.Error(testError)) + + hotObservable.on(.Error(testError)) + + hotObservable1.on(.Completed) + hotObservable.on(.Completed) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [ + 1, 2, -2, + 10, 11, -3 + ]) + } +} + +// doOn +extension DriverTest { + func testAsDriver_doOn() { + let hotObservable = MainThreadPrimitiveHotObservable() + + var events = [Event]() + + let driver = hotObservable.asDriver(onErrorJustReturn: -1).doOn { e in + XCTAssertTrue(NSThread.isMainThread()) + + events.append(e) + } + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(1)) + hotObservable.on(.Next(2)) + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [1, 2, -1]) + let expectedEvents = [Event.Next(1), Event.Next(2), Event.Next(-1), Event.Completed] as [Event] + XCTAssertEqual(events, expectedEvents, ==) + } +} + +// distinct until change +extension DriverTest { + func testAsDriver_distinctUntilChanged1() { + let hotObservable = MainThreadPrimitiveHotObservable() + + let driver = hotObservable.asDriver(onErrorJustReturn: -1).distinctUntilChanged() + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(1)) + hotObservable.on(.Next(2)) + hotObservable.on(.Next(2)) + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [1, 2, -1]) + } + + func testAsDriver_distinctUntilChanged2() { + let hotObservable = MainThreadPrimitiveHotObservable() + + let driver = hotObservable.asDriver(onErrorJustReturn: -1).distinctUntilChanged({ $0 }) + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(1)) + hotObservable.on(.Next(2)) + hotObservable.on(.Next(2)) + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [1, 2, -1]) + } + + func testAsDriver_distinctUntilChanged3() { + let hotObservable = MainThreadPrimitiveHotObservable() + + let driver = hotObservable.asDriver(onErrorJustReturn: -1).distinctUntilChanged({ $0 == $1 }) + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(1)) + hotObservable.on(.Next(2)) + hotObservable.on(.Next(2)) + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [1, 2, -1]) + } + + + func testAsDriver_distinctUntilChanged4() { + let hotObservable = MainThreadPrimitiveHotObservable() + + let driver = hotObservable.asDriver(onErrorJustReturn: -1).distinctUntilChanged({ $0 }) { $0 == $1 } + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(1)) + hotObservable.on(.Next(2)) + hotObservable.on(.Next(2)) + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [1, 2, -1]) + } + +} + +// flat map +extension DriverTest { + func testAsDriver_flatMap() { + let hotObservable = MainThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorJustReturn: -1).flatMap { (n: Int) -> Driver in + XCTAssertTrue(NSThread.isMainThread()) + return Drive.just(n + 1) + } + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(1)) + hotObservable.on(.Next(2)) + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [2, 3, 0]) + } + +} + +// merge +extension DriverTest { + func testAsDriver_merge() { + let hotObservable = MainThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorJustReturn: -1).map { (n: Int) -> Driver in + XCTAssertTrue(NSThread.isMainThread()) + return Drive.just(n + 1) + }.merge() + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(1)) + hotObservable.on(.Next(2)) + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [2, 3, 0]) + } + + func testAsDriver_merge2() { + let hotObservable = MainThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorJustReturn: -1).map { (n: Int) -> Driver in + XCTAssertTrue(NSThread.isMainThread()) + return Drive.just(n + 1) + }.merge(maxConcurrent: 1) + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(1)) + hotObservable.on(.Next(2)) + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [2, 3, 0]) + } +} + +// debounce +extension DriverTest { + func testAsDriver_debounce() { + let hotObservable = MainThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorJustReturn: -1).debounce(0.0, MainScheduler.sharedInstance) + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [-1]) + } + + func testAsDriver_throttle() { + let hotObservable = MainThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorJustReturn: -1).throttle(0.0, MainScheduler.sharedInstance) + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(1)) + hotObservable.on(.Next(2)) + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [-1]) + } + +} + +// scan +extension DriverTest { + func testAsDriver_scan() { + let hotObservable = MainThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorJustReturn: -1).scan(0) { (a: Int, n: Int) -> Int in + XCTAssertTrue(NSThread.isMainThread()) + return a + n + } + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(1)) + hotObservable.on(.Next(2)) + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [1, 3, 2]) + } + +} + +// concat +extension DriverTest { + func testAsDriver_concat() { + let hotObservable1 = MainThreadPrimitiveHotObservable() + let hotObservable2 = MainThreadPrimitiveHotObservable() + + let driver = [hotObservable1.asDriver(onErrorJustReturn: -1), hotObservable2.asDriver(onErrorJustReturn: -2)].concat() + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable1.subscriptions == [SubscribedToHotObservable]) + + hotObservable1.on(.Next(1)) + hotObservable1.on(.Next(2)) + hotObservable1.on(.Error(testError)) + + XCTAssertTrue(hotObservable1.subscriptions == [UnsunscribedFromHotObservable]) + XCTAssertTrue(hotObservable2.subscriptions == [SubscribedToHotObservable]) + + hotObservable2.on(.Next(4)) + hotObservable2.on(.Next(5)) + hotObservable2.on(.Error(testError)) + + XCTAssertTrue(hotObservable2.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [1, 2, -1, 4, 5, -2]) + } +} + +// combine latest +extension DriverTest { + func testAsDriver_combineLatest_array() { + let hotObservable1 = MainThreadPrimitiveHotObservable() + let hotObservable2 = MainThreadPrimitiveHotObservable() + + let driver = [hotObservable1.asDriver(onErrorJustReturn: -1), hotObservable2.asDriver(onErrorJustReturn: -2)].combineLatest { a in a.reduce(0, combine: +) } + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable1.subscriptions == [SubscribedToHotObservable]) + XCTAssertTrue(hotObservable2.subscriptions == [SubscribedToHotObservable]) + + hotObservable1.on(.Next(1)) + hotObservable2.on(.Next(4)) + + hotObservable1.on(.Next(2)) + hotObservable2.on(.Next(5)) + + hotObservable1.on(.Error(testError)) + hotObservable2.on(.Error(testError)) + + XCTAssertTrue(hotObservable1.subscriptions == [UnsunscribedFromHotObservable]) + XCTAssertTrue(hotObservable2.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [5, 6, 7, 4, -3]) + } + + func testAsDriver_combineLatest() { + let hotObservable1 = MainThreadPrimitiveHotObservable() + let hotObservable2 = MainThreadPrimitiveHotObservable() + + let driver = combineLatest(hotObservable1.asDriver(onErrorJustReturn: -1), hotObservable2.asDriver(onErrorJustReturn: -2), resultSelector: +) + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable1.subscriptions == [SubscribedToHotObservable]) + XCTAssertTrue(hotObservable2.subscriptions == [SubscribedToHotObservable]) + + hotObservable1.on(.Next(1)) + hotObservable2.on(.Next(4)) + + hotObservable1.on(.Next(2)) + hotObservable2.on(.Next(5)) + + hotObservable1.on(.Error(testError)) + hotObservable2.on(.Error(testError)) + + XCTAssertTrue(hotObservable1.subscriptions == [UnsunscribedFromHotObservable]) + XCTAssertTrue(hotObservable2.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [5, 6, 7, 4, -3]) + } +} + +// zip +extension DriverTest { + func testAsDriver_zip_array() { + let hotObservable1 = MainThreadPrimitiveHotObservable() + let hotObservable2 = MainThreadPrimitiveHotObservable() + + let driver = [hotObservable1.asDriver(onErrorJustReturn: -1), hotObservable2.asDriver(onErrorJustReturn: -2)].zip { a in a.reduce(0, combine: +) } + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable1.subscriptions == [SubscribedToHotObservable]) + XCTAssertTrue(hotObservable2.subscriptions == [SubscribedToHotObservable]) + + hotObservable1.on(.Next(1)) + hotObservable2.on(.Next(4)) + + hotObservable1.on(.Next(2)) + hotObservable2.on(.Next(5)) + + hotObservable1.on(.Error(testError)) + hotObservable2.on(.Error(testError)) + + XCTAssertTrue(hotObservable1.subscriptions == [UnsunscribedFromHotObservable]) + XCTAssertTrue(hotObservable2.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [5, 7, -3]) + } + + func testAsDriver_zip() { + let hotObservable1 = MainThreadPrimitiveHotObservable() + let hotObservable2 = MainThreadPrimitiveHotObservable() + + let driver = zip(hotObservable1.asDriver(onErrorJustReturn: -1), hotObservable2.asDriver(onErrorJustReturn: -2), resultSelector: +) + + let results = subscribeTwiceOnBackgroundSchedulerAndExpectResultsOnMainScheduler(driver) { + XCTAssertTrue(hotObservable1.subscriptions == [SubscribedToHotObservable]) + XCTAssertTrue(hotObservable2.subscriptions == [SubscribedToHotObservable]) + + hotObservable1.on(.Next(1)) + hotObservable2.on(.Next(4)) + + hotObservable1.on(.Next(2)) + hotObservable2.on(.Next(5)) + + hotObservable1.on(.Error(testError)) + hotObservable2.on(.Error(testError)) + + XCTAssertTrue(hotObservable1.subscriptions == [UnsunscribedFromHotObservable]) + XCTAssertTrue(hotObservable2.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [5, 7, -3]) + } +} \ No newline at end of file diff --git a/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift b/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift index 11358e16..a736e6c9 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift @@ -12,10 +12,6 @@ import RxBlocking import XCTest class ObservableBlockingTest : RxTest { - override func tearDown() { - sleep(0.1) - super.tearDown() - } } // toArray diff --git a/RxTests/RxSwiftTests/Tests/Observable+ConcurrencyTest.swift b/RxTests/RxSwiftTests/Tests/Observable+ConcurrencyTest.swift index cbea87f2..785aadf9 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+ConcurrencyTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+ConcurrencyTest.swift @@ -21,9 +21,6 @@ class ObservableConcurrencyTestBase : RxTest { } override func tearDown() { -#if TRACE_RESOURCES - sleep(0.1) // wait 100 ms for proper scheduler disposal -#endif super.tearDown() } } diff --git a/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift b/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift index ddbc3d35..77c2fc44 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift @@ -16,14 +16,6 @@ class ObservableTimeTest : RxTest { override func setUp() { super.setUp() } - - override func tearDown() { - #if TRACE_RESOURCES - sleep(0.1) // wait 100 ms for proper scheduler disposal - #endif - - super.tearDown() - } } // throttle diff --git a/RxTests/RxTest.swift b/RxTests/RxTest.swift index 6939dc30..2f661520 100644 --- a/RxTests/RxTest.swift +++ b/RxTests/RxTest.swift @@ -29,6 +29,11 @@ func XCTAssertErrorEqual(lhs: ErrorType, _ rhs: ErrorType) { XCTAssertTrue(lhs as NSError === rhs as NSError) } +func XCTAssertEqual(lhs: [T], _ rhs: [T], _ comparison: (T, T) -> Bool) { + XCTAssertEqual(lhs.count, rhs.count) + XCTAssertTrue(zip(lhs, rhs).reduce(true) { (a: Bool, z: (T, T)) in a && comparison(z.0, z.1) }) +} + let testError = NSError(domain: "dummyError", code: -232, userInfo: nil) let testError1 = NSError(domain: "dummyError1", code: -233, userInfo: nil) let testError2 = NSError(domain: "dummyError2", code: -234, userInfo: nil) @@ -65,7 +70,7 @@ class RxTest: XCTestCase { } func sleep(time: NSTimeInterval) { - NSThread.sleepForTimeInterval(time) + NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate(timeIntervalSinceNow: time)) } private var startResourceCount: Int32 = 0 @@ -100,6 +105,18 @@ class RxTest: XCTestCase { super.tearDown() #if TRACE_RESOURCES + + // give 5 sec to clean up resources + for var i = 0; i < 100; ++i { + if self.startResourceCount < resourceCount { + // main schedulers need to finish work + NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate(timeIntervalSinceNow: 0.05)) + } + else { + break + } + } + XCTAssertEqual(self.startResourceCount, resourceCount) let (endNumberOfAllocatedBytes, endNumberOfAllocations) = getMemoryInfo() diff --git a/RxTests/RxTests.xcodeproj/project.pbxproj b/RxTests/RxTests.xcodeproj/project.pbxproj index 7b6f4428..4cad61fa 100644 --- a/RxTests/RxTests.xcodeproj/project.pbxproj +++ b/RxTests/RxTests.xcodeproj/project.pbxproj @@ -12,6 +12,12 @@ C69B65011BA8957C00A7FA73 /* ObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69B64FF1BA88FAC00A7FA73 /* ObserverTests.swift */; }; C801EB5A1B97951100C4D8C4 /* Observable+CreationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C801EB591B97951100C4D8C4 /* Observable+CreationTest.swift */; }; C801EB5B1B97951100C4D8C4 /* Observable+CreationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C801EB591B97951100C4D8C4 /* Observable+CreationTest.swift */; }; + C80DDEDC1BCE9A03006A1832 /* Driver+Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEDB1BCE9A03006A1832 /* Driver+Test.swift */; }; + C80DDEDD1BCE9A03006A1832 /* Driver+Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEDB1BCE9A03006A1832 /* Driver+Test.swift */; }; + C80DDEDE1BCE9A03006A1832 /* Driver+Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEDB1BCE9A03006A1832 /* Driver+Test.swift */; }; + C80DDEE01BCEE898006A1832 /* MainThreadPrimitiveHotObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEDF1BCEE898006A1832 /* MainThreadPrimitiveHotObservable.swift */; }; + C80DDEE11BCEE898006A1832 /* MainThreadPrimitiveHotObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEDF1BCEE898006A1832 /* MainThreadPrimitiveHotObservable.swift */; }; + C80DDEE21BCEE898006A1832 /* MainThreadPrimitiveHotObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEDF1BCEE898006A1832 /* MainThreadPrimitiveHotObservable.swift */; }; C811084D1AF50E2A001C13E4 /* NSNotificationCenterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C811082B1AF50E2A001C13E4 /* NSNotificationCenterTests.swift */; }; C811084F1AF50E2A001C13E4 /* ColdObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81108301AF50E2A001C13E4 /* ColdObservable.swift */; }; C81108501AF50E2A001C13E4 /* TestConnectableObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81108311AF50E2A001C13E4 /* TestConnectableObservable.swift */; }; @@ -25,7 +31,6 @@ C81108591AF50E2A001C13E4 /* TestScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C811083B1AF50E2A001C13E4 /* TestScheduler.swift */; }; C811085A1AF50E2A001C13E4 /* VirtualTimeSchedulerBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C811083C1AF50E2A001C13E4 /* VirtualTimeSchedulerBase.swift */; }; C811085B1AF50E2A001C13E4 /* Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = C811083D1AF50E2A001C13E4 /* Subscription.swift */; }; - C811085C1AF50E2A001C13E4 /* TestExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C811083E1AF50E2A001C13E4 /* TestExtensions.swift */; }; C811085D1AF50E2A001C13E4 /* AssumptionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81108401AF50E2A001C13E4 /* AssumptionsTest.swift */; }; C811085E1AF50E2A001C13E4 /* ConcurrencyTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81108411AF50E2A001C13E4 /* ConcurrencyTest.swift */; }; C811085F1AF50E2A001C13E4 /* DisposableTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81108421AF50E2A001C13E4 /* DisposableTest.swift */; }; @@ -79,7 +84,6 @@ C88BB8A81B07E64B0064D411 /* TestScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C811083B1AF50E2A001C13E4 /* TestScheduler.swift */; }; C88BB8A91B07E64B0064D411 /* Observable+AggregateTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81108431AF50E2A001C13E4 /* Observable+AggregateTest.swift */; }; C88BB8AA1B07E64B0064D411 /* MockObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81108331AF50E2A001C13E4 /* MockObserver.swift */; }; - C88BB8AB1B07E64B0064D411 /* TestExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C811083E1AF50E2A001C13E4 /* TestExtensions.swift */; }; C88BB8AC1B07E64B0064D411 /* AssumptionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81108401AF50E2A001C13E4 /* AssumptionsTest.swift */; }; C88BB8AD1B07E64B0064D411 /* TestConnectableObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81108311AF50E2A001C13E4 /* TestConnectableObservable.swift */; }; C88BB8AE1B07E64B0064D411 /* VariableTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C814CEA21AF5622600E98087 /* VariableTest.swift */; }; @@ -108,6 +112,9 @@ C8E3812B1B2083C2008CDC33 /* PrimitiveMockObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E3812A1B2083C2008CDC33 /* PrimitiveMockObserver.swift */; }; C8E3812C1B2083C2008CDC33 /* PrimitiveMockObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E3812A1B2083C2008CDC33 /* PrimitiveMockObserver.swift */; }; C8E3813A1B21B77E008CDC33 /* Observable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E381221B2063CC008CDC33 /* Observable+Extensions.swift */; }; + C8EA2D371BD02E1900FB22AC /* EquatableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EA2D361BD02E1900FB22AC /* EquatableArray.swift */; }; + C8EA2D381BD02E1900FB22AC /* EquatableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EA2D361BD02E1900FB22AC /* EquatableArray.swift */; }; + C8EA2D391BD02E1900FB22AC /* EquatableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EA2D361BD02E1900FB22AC /* EquatableArray.swift */; }; C8FDC5F81B2B5B7E0065F8D9 /* ElementIndexPair.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8FDC5F71B2B5B7E0065F8D9 /* ElementIndexPair.swift */; }; C8FDC5F91B2B5B7E0065F8D9 /* ElementIndexPair.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8FDC5F71B2B5B7E0065F8D9 /* ElementIndexPair.swift */; }; D203C4EB1BB9C22800D02D00 /* NSNotificationCenterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C811082B1AF50E2A001C13E4 /* NSNotificationCenterTests.swift */; }; @@ -131,7 +138,6 @@ D2EBEB5D1BB9B7D6003A27DC /* TestScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C811083B1AF50E2A001C13E4 /* TestScheduler.swift */; }; D2EBEB5E1BB9B7D8003A27DC /* VirtualTimeSchedulerBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C811083C1AF50E2A001C13E4 /* VirtualTimeSchedulerBase.swift */; }; D2EBEB5F1BB9B7DB003A27DC /* Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = C811083D1AF50E2A001C13E4 /* Subscription.swift */; }; - D2EBEB601BB9B7DF003A27DC /* TestExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C811083E1AF50E2A001C13E4 /* TestExtensions.swift */; }; D2EBEB611BB9B7E2003A27DC /* Observable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E381221B2063CC008CDC33 /* Observable+Extensions.swift */; }; D2EBEB621BB9B7E5003A27DC /* ElementIndexPair.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8FDC5F71B2B5B7E0065F8D9 /* ElementIndexPair.swift */; }; D2EBEB631BB9B7EA003A27DC /* AnonymousObservable+Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B605EB1B6260A10044410E /* AnonymousObservable+Test.swift */; }; @@ -166,6 +172,8 @@ 5E5D10BA1B48355200432B25 /* UIControl+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIControl+RxTests.swift"; sourceTree = ""; }; C69B64FF1BA88FAC00A7FA73 /* ObserverTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserverTests.swift; sourceTree = ""; }; C801EB591B97951100C4D8C4 /* Observable+CreationTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+CreationTest.swift"; sourceTree = ""; }; + C80DDEDB1BCE9A03006A1832 /* Driver+Test.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Driver+Test.swift"; sourceTree = ""; }; + C80DDEDF1BCEE898006A1832 /* MainThreadPrimitiveHotObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainThreadPrimitiveHotObservable.swift; sourceTree = ""; }; C81108201AF50E11001C13E4 /* RxTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RxTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C81108241AF50E11001C13E4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C811082B1AF50E2A001C13E4 /* NSNotificationCenterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSNotificationCenterTests.swift; sourceTree = ""; }; @@ -181,7 +189,6 @@ C811083B1AF50E2A001C13E4 /* TestScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestScheduler.swift; sourceTree = ""; }; C811083C1AF50E2A001C13E4 /* VirtualTimeSchedulerBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VirtualTimeSchedulerBase.swift; sourceTree = ""; }; C811083D1AF50E2A001C13E4 /* Subscription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Subscription.swift; sourceTree = ""; }; - C811083E1AF50E2A001C13E4 /* TestExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestExtensions.swift; sourceTree = ""; }; C81108401AF50E2A001C13E4 /* AssumptionsTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssumptionsTest.swift; sourceTree = ""; }; C81108411AF50E2A001C13E4 /* ConcurrencyTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConcurrencyTest.swift; sourceTree = ""; }; C81108421AF50E2A001C13E4 /* DisposableTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisposableTest.swift; sourceTree = ""; }; @@ -221,6 +228,7 @@ C8E381221B2063CC008CDC33 /* Observable+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Extensions.swift"; sourceTree = ""; }; C8E381271B207D03008CDC33 /* PrimitiveHotObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrimitiveHotObservable.swift; sourceTree = ""; }; C8E3812A1B2083C2008CDC33 /* PrimitiveMockObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrimitiveMockObserver.swift; sourceTree = ""; }; + C8EA2D361BD02E1900FB22AC /* EquatableArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EquatableArray.swift; sourceTree = ""; }; C8FDC5F71B2B5B7E0065F8D9 /* ElementIndexPair.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElementIndexPair.swift; sourceTree = ""; }; D2EBEB491BB9B7AE003A27DC /* RxTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RxTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -338,9 +346,9 @@ C81108381AF50E2A001C13E4 /* Recorded.swift */, C811083A1AF50E2A001C13E4 /* Schedulers */, C811083D1AF50E2A001C13E4 /* Subscription.swift */, - C811083E1AF50E2A001C13E4 /* TestExtensions.swift */, C8E381221B2063CC008CDC33 /* Observable+Extensions.swift */, C8FDC5F71B2B5B7E0065F8D9 /* ElementIndexPair.swift */, + C8EA2D361BD02E1900FB22AC /* EquatableArray.swift */, ); path = TestImplementations; sourceTree = ""; @@ -358,6 +366,7 @@ C81108371AF50E2A001C13E4 /* TestObserver.swift */, C8E381271B207D03008CDC33 /* PrimitiveHotObservable.swift */, C8E3812A1B2083C2008CDC33 /* PrimitiveMockObserver.swift */, + C80DDEDF1BCEE898006A1832 /* MainThreadPrimitiveHotObservable.swift */, ); path = Mocks; sourceTree = ""; @@ -399,6 +408,7 @@ C814CEA21AF5622600E98087 /* VariableTest.swift */, C69B64FF1BA88FAC00A7FA73 /* ObserverTests.swift */, C89CDB951BCDA1F1002063D9 /* Observable+SubscriptionTest.swift */, + C80DDEDB1BCE9A03006A1832 /* Driver+Test.swift */, ); path = Tests; sourceTree = ""; @@ -568,6 +578,7 @@ C81108551AF50E2A001C13E4 /* TestObservable.swift in Sources */, C8E3812B1B2083C2008CDC33 /* PrimitiveMockObserver.swift in Sources */, C811085B1AF50E2A001C13E4 /* Subscription.swift in Sources */, + C80DDEDC1BCE9A03006A1832 /* Driver+Test.swift in Sources */, C81108531AF50E2A001C13E4 /* MySubject.swift in Sources */, C814CEA71AF642D600E98087 /* UI+RxTests.swift in Sources */, C81108611AF50E2A001C13E4 /* Observable+BindingTest.swift in Sources */, @@ -584,6 +595,7 @@ C8AF26EF1B499E5C00131C03 /* DelegateProxyTest.swift in Sources */, C8E381281B207D03008CDC33 /* PrimitiveHotObservable.swift in Sources */, C81108661AF50E2A001C13E4 /* Observable+StandardSequenceOperatorsTest.swift in Sources */, + C80DDEE01BCEE898006A1832 /* MainThreadPrimitiveHotObservable.swift in Sources */, C81108641AF50E2A001C13E4 /* Observable+MultipleTest.swift in Sources */, C811084F1AF50E2A001C13E4 /* ColdObservable.swift in Sources */, C8FDC5F81B2B5B7E0065F8D9 /* ElementIndexPair.swift in Sources */, @@ -602,8 +614,8 @@ C81108601AF50E2A001C13E4 /* Observable+AggregateTest.swift in Sources */, C81108521AF50E2A001C13E4 /* MockObserver.swift in Sources */, C8633AE51B0A9FF300375D60 /* KVOObservableTests.swift in Sources */, - C811085C1AF50E2A001C13E4 /* TestExtensions.swift in Sources */, C8B605EC1B6260A10044410E /* AnonymousObservable+Test.swift in Sources */, + C8EA2D371BD02E1900FB22AC /* EquatableArray.swift in Sources */, C811085D1AF50E2A001C13E4 /* AssumptionsTest.swift in Sources */, C81108501AF50E2A001C13E4 /* TestConnectableObservable.swift in Sources */, C814CEA31AF5622600E98087 /* VariableTest.swift in Sources */, @@ -639,6 +651,7 @@ C88BB89C1B07E64B0064D411 /* ConcurrencyTest.swift in Sources */, C88BB89D1B07E64B0064D411 /* TestObserver.swift in Sources */, C88BB89E1B07E64B0064D411 /* VirtualTimeSchedulerBase.swift in Sources */, + C80DDEDD1BCE9A03006A1832 /* Driver+Test.swift in Sources */, C88BB89F1B07E64B0064D411 /* DisposableTest.swift in Sources */, C88BB8A01B07E64B0064D411 /* Observable+StandardSequenceOperatorsTest.swift in Sources */, C88BB8A11B07E64B0064D411 /* Observable+MultipleTest.swift in Sources */, @@ -662,10 +675,11 @@ C88BB8A81B07E64B0064D411 /* TestScheduler.swift in Sources */, C88BB8A91B07E64B0064D411 /* Observable+AggregateTest.swift in Sources */, C88BB8AA1B07E64B0064D411 /* MockObserver.swift in Sources */, + C80DDEE11BCEE898006A1832 /* MainThreadPrimitiveHotObservable.swift in Sources */, C8AF26F01B499E5C00131C03 /* DelegateProxyTest.swift in Sources */, C69B65011BA8957C00A7FA73 /* ObserverTests.swift in Sources */, C8633AE61B0AA0ED00375D60 /* KVOObservableTests.swift in Sources */, - C88BB8AB1B07E64B0064D411 /* TestExtensions.swift in Sources */, + C8EA2D381BD02E1900FB22AC /* EquatableArray.swift in Sources */, C88BB8AC1B07E64B0064D411 /* AssumptionsTest.swift in Sources */, C88BB8AD1B07E64B0064D411 /* TestConnectableObservable.swift in Sources */, C8E3813A1B21B77E008CDC33 /* Observable+Extensions.swift in Sources */, @@ -685,6 +699,7 @@ D203C4EC1BB9C22800D02D00 /* UI+RxTests.swift in Sources */, D2EBEB591BB9B7CC003A27DC /* TestObserver.swift in Sources */, D2EBEB5E1BB9B7D8003A27DC /* VirtualTimeSchedulerBase.swift in Sources */, + C80DDEDE1BCE9A03006A1832 /* Driver+Test.swift in Sources */, D2EBEB641BB9B7EF003A27DC /* AssumptionsTest.swift in Sources */, D2EBEB6A1BB9B7EF003A27DC /* DisposableTest.swift in Sources */, D2EBEB791BB9B7FD003A27DC /* RxTest.swift in Sources */, @@ -701,6 +716,7 @@ D2EBEB681BB9B7EF003A27DC /* ConcurrencyTest.swift in Sources */, D2EBEB721BB9B7F6003A27DC /* Observable+MultipleTest+Zip.swift in Sources */, D2EBEB731BB9B7F9003A27DC /* Observable+SingleTest.swift in Sources */, + C80DDEE21BCEE898006A1832 /* MainThreadPrimitiveHotObservable.swift in Sources */, D2EBEB6C1BB9B7EF003A27DC /* Observable+BindingTest.swift in Sources */, D2EBEB621BB9B7E5003A27DC /* ElementIndexPair.swift in Sources */, D2EBEB5F1BB9B7DB003A27DC /* Subscription.swift in Sources */, @@ -720,9 +736,9 @@ D2EBEB611BB9B7E2003A27DC /* Observable+Extensions.swift in Sources */, D203C4EE1BB9C22800D02D00 /* KVOObservableTests.swift in Sources */, D203C4F01BB9C22800D02D00 /* NSObject+RxTests.swift in Sources */, + C8EA2D391BD02E1900FB22AC /* EquatableArray.swift in Sources */, D2EBEB531BB9B7CC003A27DC /* TestConnectableObservable.swift in Sources */, D2EBEB571BB9B7CC003A27DC /* Observable.Extensions.swift in Sources */, - D2EBEB601BB9B7DF003A27DC /* TestExtensions.swift in Sources */, D2EBEB741BB9B7F9003A27DC /* Observable+StandardSequenceOperatorsTest.swift in Sources */, D2EBEB6D1BB9B7EF003A27DC /* Observable+BlockingTest.swift in Sources */, ); From 4872a62f211acd92895ca0925494742517827c5b Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Fri, 16 Oct 2015 21:19:53 +0200 Subject: [PATCH 039/210] Fixes error wording. --- RxCocoa/Common/CocoaUnits/Driver/ControlEvent+Driver.swift | 2 +- RxCocoa/Common/CocoaUnits/Driver/ControlProperty+Driver.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RxCocoa/Common/CocoaUnits/Driver/ControlEvent+Driver.swift b/RxCocoa/Common/CocoaUnits/Driver/ControlEvent+Driver.swift index b904db7b..d85eed07 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/ControlEvent+Driver.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/ControlEvent+Driver.swift @@ -20,7 +20,7 @@ extension ControlEvent { public func asDriver() -> Driver { return self.asDriver { (error) -> Driver in #if DEBUG - rxFatalError("Somehow driver received error from a pipe that was marked as ") + rxFatalError("Somehow driver received error from a source that shouldn't fail.") #else return Drive.empty() #endif diff --git a/RxCocoa/Common/CocoaUnits/Driver/ControlProperty+Driver.swift b/RxCocoa/Common/CocoaUnits/Driver/ControlProperty+Driver.swift index 88db2fd4..0f733fd7 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/ControlProperty+Driver.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/ControlProperty+Driver.swift @@ -20,7 +20,7 @@ extension ControlProperty { public func asDriver() -> Driver { return self.asDriver { (error) -> Driver in #if DEBUG - rxFatalError("Somehow driver received error from a pipe that was marked as ") + rxFatalError("Somehow driver received error from a source that shouldn't fail.") #else return Drive.empty() #endif From 0fce5c89d4f00f24c216dbe33c9c5dfc8a4f5175 Mon Sep 17 00:00:00 2001 From: Greg Pardo Date: Fri, 16 Oct 2015 15:20:13 -0400 Subject: [PATCH 040/210] #193 Implement suggestions to add some helper rx models --- RxCocoa/iOS/UICollectionView+Rx.swift | 100 +++++++++++++++----------- 1 file changed, 57 insertions(+), 43 deletions(-) diff --git a/RxCocoa/iOS/UICollectionView+Rx.swift b/RxCocoa/iOS/UICollectionView+Rx.swift index b0de1120..8c554c5f 100644 --- a/RxCocoa/iOS/UICollectionView+Rx.swift +++ b/RxCocoa/iOS/UICollectionView+Rx.swift @@ -144,9 +144,25 @@ extension UICollectionView { */ public func rx_modelSelected() -> ControlEvent { let source: Observable = rx_itemSelected .map { indexPath in - let dataSource: RxCollectionViewReactiveArrayDataSource = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_itemsWith*` methods was used.") - - return dataSource.modelAtIndex(indexPath.item)! + return self.rx_modelAtIndexPath(indexPath).asObservable() + }.switchLatest() + + return ControlEvent(source: source) + } + + public func rx_modelAtIndexPath(indexPath: NSIndexPath?) -> ControlEvent { + let source: Observable = create { observer in + if let indexPath = indexPath { + + let dataSource: RxCollectionViewReactiveArrayDataSource = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_itemsWith*` methods was used.") + + if let model = dataSource.modelAtIndex(indexPath.item) { + observer.on(.Next(model)) + } + + } + + return AnonymousDisposable { } } return ControlEvent(source: source) @@ -157,71 +173,69 @@ extension UICollectionView { #if os(tvOS) extension UICollectionView { - + + public typealias ContextAndAnimationCoordinator = (context: UIFocusUpdateContext, animationCoordinator: UIFocusAnimationCoordinator) + /** Reactive wrapper for `delegate` message `collectionView:didUpdateFocusInContext:withAnimationCoordinator:`. */ + public var rx_didUpdateFocusInContextWithAnimationCoordinator: ControlEvent { + + let source = rx_delegate.observe("collectionView:didUpdateFocusInContext:withAnimationCoordinator:") + .map { a -> ContextAndAnimationCoordinator in + let context = a[1] as! UIFocusUpdateContext + let animationCoordinator = a[2] as! UIFocusAnimationCoordinator + return (context: context, animationCoordinator: animationCoordinator) + } + + return ControlEvent(source: source) + } + + /** + Reactive wrapper for UICollectionViewFocusUpdateContext's nextFocusedIndexPath + */ public var rx_nextFocusedUpdated: ControlEvent { - let source = rx_delegate.observe("collectionView:didUpdateFocusInContext:withAnimationCoordinator:") - .map { a -> NSIndexPath? in - let context = a[1] as! UICollectionViewFocusUpdateContext - return context.nextFocusedIndexPath! + let source = rx_didUpdateFocusInContextWithAnimationCoordinator + .map { (c: ContextAndAnimationCoordinator) -> NSIndexPath? in + let context = c.context as! UICollectionViewFocusUpdateContext + return context.nextFocusedIndexPath } return ControlEvent(source: source) } /** - Reactive wrapper for `delegate` message `collectionView:didUpdateFocusInContext:withAnimationCoordinator:`. + Reactive wrapper for UICollectionViewFocusUpdateContext's previouslyFocusedIndexPath */ public var rx_previousFocusedUpdated: ControlEvent { - let source = rx_delegate.observe("collectionView:didUpdateFocusInContext:withAnimationCoordinator:") - .map { a -> NSIndexPath? in - let context = a[1] as! UICollectionViewFocusUpdateContext - return context.previouslyFocusedIndexPath! + let source = rx_didUpdateFocusInContextWithAnimationCoordinator + .map { (c: ContextAndAnimationCoordinator) -> NSIndexPath? in + let context = c.context as! UICollectionViewFocusUpdateContext + return context.previouslyFocusedIndexPath } return ControlEvent(source: source) } /** - Reactive wrapper for `delegate` message `collectionView:didUpdateFocusInContext:withAnimationCoordinator:` - that returns the next focused NSIndexPath. - - It can be only used when one of the `rx_itemsWith*` methods is used to bind observable sequence. + Reactive wrapper for the next focused model */ - public func rx_modelForNextFocusedUpdated() -> ControlEvent { - let source: Observable = rx_nextFocusedUpdated .map { indexPath in + public func rx_modelForNextFocusedUpdated() -> ControlEvent { + let source: Observable = rx_nextFocusedUpdated.map { indexPath in + return self.rx_modelAtIndexPath(indexPath).asObservable() - guard let indexPath = indexPath else { - return nil - } - - let dataSource: RxCollectionViewReactiveArrayDataSource = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_itemsWith*` methods was used.") - - return dataSource.modelAtIndex(indexPath.item)! - } - + }.switchLatest() + return ControlEvent(source: source) } /** - Reactive wrapper for `delegate` message `collectionView:didUpdateFocusInContext:withAnimationCoordinator:` - that returns the next focused NSIndexPath. - - It can be only used when one of the `rx_itemsWith*` methods is used to bind observable sequence. + Reactive wrapper for the previously focused model */ - public func rx_modelForPreviouslyFocusedUpdated() -> ControlEvent { - let source: Observable = rx_previousFocusedUpdated .map { indexPath in - - guard let indexPath = indexPath else { - return nil - } - - let dataSource: RxCollectionViewReactiveArrayDataSource = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_itemsWith*` methods was used.") - - return dataSource.modelAtIndex(indexPath.item)! - } + public func rx_modelForPreviouslyFocusedUpdated() -> ControlEvent { + let source: Observable = rx_nextFocusedUpdated.map { indexPath in + return self.rx_modelAtIndexPath(indexPath).asObservable() + }.switchLatest() return ControlEvent(source: source) } From 04c08880ce42441f9d2430c02f27c0125c131995 Mon Sep 17 00:00:00 2001 From: yury Date: Sat, 17 Oct 2015 10:58:20 +0300 Subject: [PATCH 041/210] Using operator initial port --- Rx.xcodeproj/project.pbxproj | 10 +++ .../Observables/Implementations/Using.swift | 78 +++++++++++++++++++ RxSwift/Observables/Observable+Creation.swift | 4 + 3 files changed, 92 insertions(+) create mode 100644 RxSwift/Observables/Implementations/Using.swift diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index c6e0a40e..02d77fb8 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -542,6 +542,10 @@ D2138C991BB9BEEE00339B5C /* RxTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E9C1B8A732E0088E94D /* RxTarget.swift */; }; D21C29311BC6A1C300448E70 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D285BAC31BC0231000B3F602 /* SkipUntil.swift */; }; D22B6D261BC8504A00BCE0AB /* SkipWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22B6D251BC8504A00BCE0AB /* SkipWhile.swift */; }; + D235B23E1BD003DD007E84DA /* Using.swift in Sources */ = {isa = PBXBuildFile; fileRef = D235B23D1BD003DD007E84DA /* Using.swift */; }; + D235B23F1BD003DD007E84DA /* Using.swift in Sources */ = {isa = PBXBuildFile; fileRef = D235B23D1BD003DD007E84DA /* Using.swift */; }; + D235B2401BD003DD007E84DA /* Using.swift in Sources */ = {isa = PBXBuildFile; fileRef = D235B23D1BD003DD007E84DA /* Using.swift */; }; + D235B2411BD003DD007E84DA /* Using.swift in Sources */ = {isa = PBXBuildFile; fileRef = D235B23D1BD003DD007E84DA /* Using.swift */; }; D2752D621BC5551A0070C418 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D285BAC31BC0231000B3F602 /* SkipUntil.swift */; }; D2752D631BC5551B0070C418 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D285BAC31BC0231000B3F602 /* SkipUntil.swift */; }; D285BAC41BC0231000B3F602 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D285BAC31BC0231000B3F602 /* SkipUntil.swift */; }; @@ -906,6 +910,7 @@ C8F0C0581BBBFBCE001B112F /* RxBlocking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxBlocking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D2138C751BB9BE9800339B5C /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D22B6D251BC8504A00BCE0AB /* SkipWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipWhile.swift; sourceTree = ""; }; + D235B23D1BD003DD007E84DA /* Using.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Using.swift; sourceTree = ""; }; D285BAC31BC0231000B3F602 /* SkipUntil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipUntil.swift; sourceTree = ""; }; D2EA280C1BB9B5A200880ED3 /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D2EBEB811BB9B99D003A27DC /* RxBlocking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxBlocking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1140,6 +1145,7 @@ C8093C931B8A72BE0088E94D /* Zip+arity.tt */, C8C3D9FD1B935EDF004D233E /* Zip+CollectionType.swift */, D22B6D251BC8504A00BCE0AB /* SkipWhile.swift */, + D235B23D1BD003DD007E84DA /* Using.swift */, ); path = Implementations; sourceTree = ""; @@ -2047,6 +2053,7 @@ C86409FD1BA593F500D3C4E8 /* Range.swift in Sources */, C8093D221B8A72BE0088E94D /* Map.swift in Sources */, C8093CD01B8A72BE0088E94D /* InfiniteSequence.swift in Sources */, + D235B23F1BD003DD007E84DA /* Using.swift in Sources */, C8093D661B8A72BE0088E94D /* ObservableType.swift in Sources */, C8093D541B8A72BE0088E94D /* Observable+Aggregate.swift in Sources */, C8093D2C1B8A72BE0088E94D /* ObserveOnSerialDispatchQueue.swift in Sources */, @@ -2161,6 +2168,7 @@ C86409FC1BA593F500D3C4E8 /* Range.swift in Sources */, C8093D211B8A72BE0088E94D /* Map.swift in Sources */, C8093CCF1B8A72BE0088E94D /* InfiniteSequence.swift in Sources */, + D235B23E1BD003DD007E84DA /* Using.swift in Sources */, C8093D651B8A72BE0088E94D /* ObservableType.swift in Sources */, C8093D531B8A72BE0088E94D /* Observable+Aggregate.swift in Sources */, C8093D2B1B8A72BE0088E94D /* ObserveOnSerialDispatchQueue.swift in Sources */, @@ -2275,6 +2283,7 @@ C8F0BFD51BBBFB8B001B112F /* Range.swift in Sources */, C8F0BFD61BBBFB8B001B112F /* Map.swift in Sources */, C8F0BFD71BBBFB8B001B112F /* InfiniteSequence.swift in Sources */, + D235B2411BD003DD007E84DA /* Using.swift in Sources */, C8F0BFD81BBBFB8B001B112F /* ObservableType.swift in Sources */, C8F0BFD91BBBFB8B001B112F /* Observable+Aggregate.swift in Sources */, C8F0BFDA1BBBFB8B001B112F /* ObserveOnSerialDispatchQueue.swift in Sources */, @@ -2523,6 +2532,7 @@ D2EBEB0C1BB9B6C1003A27DC /* FailWith.swift in Sources */, D2EBEB361BB9B6D2003A27DC /* TailRecursiveSink.swift in Sources */, D2EBEB311BB9B6CA003A27DC /* Observable+Single.swift in Sources */, + D235B2401BD003DD007E84DA /* Using.swift in Sources */, D2EBEAE91BB9B697003A27DC /* RxBox.swift in Sources */, D2EBEAFC1BB9B6BA003A27DC /* Amb.swift in Sources */, D2EBEB231BB9B6C1003A27DC /* Take.swift in Sources */, diff --git a/RxSwift/Observables/Implementations/Using.swift b/RxSwift/Observables/Implementations/Using.swift new file mode 100644 index 00000000..6dac4155 --- /dev/null +++ b/RxSwift/Observables/Implementations/Using.swift @@ -0,0 +1,78 @@ +// +// Using.swift +// Rx +// +// Created by Yury Korolev on 10/15/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +class UsingSink : Sink, ObserverType { + + typealias Parent = Using + typealias E = O.E + + private let _parent: Parent + + init(parent: Parent, observer: O, cancel: Disposable) { + _parent = parent + super.init(observer: observer, cancel: cancel) + } + + func run() -> Disposable { + var disposable = NopDisposable.instance + + do { + let resource = try _parent._resourceFactory() + disposable = resource + let source = try _parent._observableFactory(resource: resource) + + return StableCompositeDisposable.create( + source.subscribeSafe(self), + disposable + ) + } catch let error { + return StableCompositeDisposable.create( + failWith(error).subscribeSafe(self), + disposable + ) + } + } + + func on(event: Event) { + switch event { + case let .Next(value): + observer?.onNext(value) + case let .Error(error): + observer?.onError(error) + dispose() + case .Completed: + observer?.onComplete() + dispose() + } + } +} + +class Using: Producer { + + typealias E = SourceType + + typealias ResourceFactory = () throws -> ResourceType + typealias ObservableFactory = (resource: ResourceType) throws -> Observable + + private let _resourceFactory: ResourceFactory + private let _observableFactory: ObservableFactory + + + init(resourceFactory: ResourceFactory, observableFactory: ObservableFactory) { + _resourceFactory = resourceFactory + _observableFactory = observableFactory + } + + override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + let sink = UsingSink(parent: self, observer: observer, cancel: cancel) + setSink(sink) + return sink.run() + } +} \ No newline at end of file diff --git a/RxSwift/Observables/Observable+Creation.swift b/RxSwift/Observables/Observable+Creation.swift index 9fcb906b..b2173ef5 100644 --- a/RxSwift/Observables/Observable+Creation.swift +++ b/RxSwift/Observables/Observable+Creation.swift @@ -159,3 +159,7 @@ Generates an observable sequence that repeats the given element infinitely, usin public func repeatElement(element: E, _ scheduler: ImmediateSchedulerType) -> Observable { return RepeatElement(element: element, scheduler: scheduler) } + +public func using(resourceFactory: () throws -> R, observableFactory: R throws -> Observable) -> Observable { + return Using(resourceFactory: resourceFactory, observableFactory: observableFactory) +} From 8f64ef60018e66b1ca35495d35c3b33f8a457e3d Mon Sep 17 00:00:00 2001 From: yury Date: Sat, 17 Oct 2015 11:47:20 +0300 Subject: [PATCH 042/210] More work on private state --- RxSwift/Observables/Implementations/Amb.swift | 36 +- .../Implementations/AnonymousObservable.swift | 8 +- .../Implementations/AsObservable.swift | 10 +- .../Observables/Implementations/Buffer.swift | 65 +-- .../Observables/Implementations/Catch.swift | 32 +- .../Implementations/CombineLatest+arity.swift | 420 +++++++++--------- .../Implementations/CombineLatest.swift | 78 ++-- .../Observables/Implementations/Concat.swift | 10 +- 8 files changed, 327 insertions(+), 332 deletions(-) diff --git a/RxSwift/Observables/Implementations/Amb.swift b/RxSwift/Observables/Implementations/Amb.swift index 10751aab..b234a0c7 100644 --- a/RxSwift/Observables/Implementations/Amb.swift +++ b/RxSwift/Observables/Implementations/Amb.swift @@ -20,24 +20,24 @@ class AmbObserver : Obser typealias This = AmbObserver typealias Sink = (This, Event) -> Void - let parent: Parent - var sink: Sink - var cancel: Disposable + private let _parent: Parent + private var _sink: Sink + private var _cancel: Disposable init(parent: Parent, cancel: Disposable, sink: Sink) { #if TRACE_RESOURCES OSAtomicIncrement32(&resourceCount) #endif - self.parent = parent - self.sink = sink - self.cancel = cancel + _parent = parent + _sink = sink + _cancel = cancel } func on(event: Event) { - self.sink(self, event) + _sink(self, event) if event.isStopEvent { - cancel.dispose() + _cancel.dispose() } } @@ -52,14 +52,14 @@ class AmbSink : Sink { typealias Parent = Amb typealias AmbObserverType = AmbObserver - let parent: Parent + private let _parent: Parent private let _lock = NSRecursiveLock() // state private var _choice = AmbState.Neither init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } @@ -76,8 +76,8 @@ class AmbSink : Sink { self._lock.performLocked { if self._choice == .Neither { self._choice = me - o.sink = forwardEvent - o.cancel = disposeAll + o._sink = forwardEvent + o._cancel = disposeAll otherSubscription.dispose() } @@ -98,20 +98,20 @@ class AmbSink : Sink { decide(o, e, .Right, subscription1) } - subscription1.disposable = self.parent.left.subscribeSafe(sink1) - subscription2.disposable = self.parent.right.subscribeSafe(sink2) + subscription1.disposable = _parent._left.subscribeSafe(sink1) + subscription2.disposable = _parent._right.subscribeSafe(sink2) return disposeAll } } class Amb: Producer { - let left: Observable - let right: Observable + private let _left: Observable + private let _right: Observable init(left: Observable, right: Observable) { - self.left = left - self.right = right + _left = left + _right = right } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { diff --git a/RxSwift/Observables/Implementations/AnonymousObservable.swift b/RxSwift/Observables/Implementations/AnonymousObservable.swift index 87a59a28..58a47db7 100644 --- a/RxSwift/Observables/Implementations/AnonymousObservable.swift +++ b/RxSwift/Observables/Implementations/AnonymousObservable.swift @@ -26,7 +26,7 @@ class AnonymousObservableSink : Sink, ObserverType { if _isStopped == 1 { return } - self.observer?.on(event) + observer?.on(event) case .Error, .Completed: if OSAtomicCompareAndSwap32(0, 1, &_isStopped) { self.observer?.on(event) @@ -36,17 +36,17 @@ class AnonymousObservableSink : Sink, ObserverType { } func run(parent: Parent) -> Disposable { - return parent.subscribeHandler(ObserverOf(self)) + return parent._subscribeHandler(ObserverOf(self)) } } public class AnonymousObservable : Producer { public typealias SubscribeHandler = (ObserverOf) -> Disposable - public let subscribeHandler: SubscribeHandler + public let _subscribeHandler: SubscribeHandler public init(_ subscribeHandler: SubscribeHandler) { - self.subscribeHandler = subscribeHandler + _subscribeHandler = subscribeHandler } public override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { diff --git a/RxSwift/Observables/Implementations/AsObservable.swift b/RxSwift/Observables/Implementations/AsObservable.swift index 0f7a42c6..460adbf8 100644 --- a/RxSwift/Observables/Implementations/AsObservable.swift +++ b/RxSwift/Observables/Implementations/AsObservable.swift @@ -20,7 +20,7 @@ class AsObservableSink : Sink, ObserverType { switch event { case .Error, .Completed: - self.dispose() + dispose() default: break } } @@ -29,10 +29,10 @@ class AsObservableSink : Sink, ObserverType { class AsObservable : Producer { - let source: Observable + private let _source: Observable init(source: Observable) { - self.source = source + _source = source } func omega() -> Observable { @@ -40,12 +40,12 @@ class AsObservable : Producer { } func eval() -> Observable { - return source + return _source } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { let sink = AsObservableSink(observer: observer, cancel: cancel) setSink(sink) - return source.subscribeSafe(sink) + return _source.subscribeSafe(sink) } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Buffer.swift b/RxSwift/Observables/Implementations/Buffer.swift index 4e0a7fe0..d106222e 100644 --- a/RxSwift/Observables/Implementations/Buffer.swift +++ b/RxSwift/Observables/Implementations/Buffer.swift @@ -9,16 +9,17 @@ import Foundation class BufferTimeCount : Producer<[Element]> { - let timeSpan: S.TimeInterval - let count: Int - let scheduler: S - let source: Observable + + private let _timeSpan: S.TimeInterval + private let _count: Int + private let _scheduler: S + private let _source: Observable init(source: Observable, timeSpan: S.TimeInterval, count: Int, scheduler: S) { - self.source = source - self.timeSpan = timeSpan - self.count = count - self.scheduler = scheduler + _source = source + _timeSpan = timeSpan + _count = count + _scheduler = scheduler } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { @@ -32,32 +33,32 @@ class BufferTimeCountSink typealias E = Element - let parent: Parent + private let _parent: Parent private let _lock = NSRecursiveLock() // state - let timerD = SerialDisposable() - var buffer = [Element]() - var windowID = 0 + private let _timerD = SerialDisposable() + private var _buffer = [Element]() + private var _windowID = 0 init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { - createTimer(self.windowID) - return StableCompositeDisposable.create(timerD, self.parent.source.subscribeSafe(self)) + createTimer(_windowID) + return StableCompositeDisposable.create(_timerD, _parent._source.subscribeSafe(self)) } func startNewWindowAndSendCurrentOne() { - self.windowID = self.windowID &+ 1 - let windowID = self.windowID + _windowID = _windowID &+ 1 + let windowID = _windowID - let buffer = self.buffer - self.buffer = [] - self.observer?.on(.Next(buffer)) + let buffer = _buffer + _buffer = [] + observer?.on(.Next(buffer)) createTimer(windowID) } @@ -66,36 +67,36 @@ class BufferTimeCountSink : ObserverType { switch event { case .Next: break - case .Error: - _parent.dispose() - case .Completed: + case .Error, .Completed: _parent.dispose() } } @@ -49,7 +47,7 @@ class CatchSink : Sink, ObserverType { func run() -> Disposable { let d1 = SingleAssignmentDisposable() _subscription.disposable = d1 - d1.disposable = _parent.source.subscribeSafe(self) + d1.disposable = _parent._source.subscribeSafe(self) return _subscription } @@ -60,10 +58,10 @@ class CatchSink : Sink, ObserverType { observer?.on(event) case .Completed: observer?.on(event) - self.dispose() + dispose() case .Error(let error): do { - let catchSequence = try _parent.handler(error) + let catchSequence = try _parent._handler(error) let observer = CatchSinkProxy(parent: self) @@ -71,7 +69,7 @@ class CatchSink : Sink, ObserverType { } catch let e { observer?.on(.Error(e)) - self.dispose() + dispose() } } } @@ -80,12 +78,12 @@ class CatchSink : Sink, ObserverType { class Catch : Producer { typealias Handler = (ErrorType) throws -> Observable - let source: Observable - let handler: Handler + private let _source: Observable + private let _handler: Handler init(source: Observable, handler: Handler) { - self.source = source - self.handler = handler + _source = source + _handler = handler } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { @@ -101,7 +99,7 @@ class CatchSequenceSink - var lastError: ErrorType? + private var _lastError: ErrorType? override init(observer: O, cancel: Disposable) { super.init(observer: observer, cancel: cancel) @@ -112,16 +110,16 @@ class CatchSequenceSink : CombineLatestSink { typealias R = O.E typealias Parent = CombineLatest2 - let parent: Parent + private let _parent: Parent - var latestElement1: E1! = nil - var latestElement2: E2! = nil + private var _latestElement1: E1! = nil + private var _latestElement2: E2! = nil init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(arity: 2, observer: observer, cancel: cancel) } @@ -47,11 +47,11 @@ class CombineLatestSink2_ : CombineLatestSink { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() - let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self.latestElement1 = e }, this: subscription1) - let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self.latestElement2 = e }, this: subscription2) + let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) + let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) - subscription1.disposable = parent.source1.subscribeSafe(observer1) - subscription2.disposable = parent.source2.subscribeSafe(observer2) + subscription1.disposable = _parent._source1.subscribeSafe(observer1) + subscription2.disposable = _parent._source2.subscribeSafe(observer2) return CompositeDisposable(disposables: [ subscription1, @@ -60,23 +60,23 @@ class CombineLatestSink2_ : CombineLatestSink { } override func getResult() throws -> R { - return try self.parent.resultSelector(latestElement1, latestElement2) + return try _parent._resultSelector(_latestElement1, _latestElement2) } } class CombineLatest2 : Producer { typealias ResultSelector = (E1, E2) throws -> R - let source1: Observable - let source2: Observable + private let _source1: Observable + private let _source2: Observable - let resultSelector: ResultSelector + private let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, resultSelector: ResultSelector) { - self.source1 = source1 - self.source2 = source2 + _source1 = source1 + _source2 = source2 - self.resultSelector = resultSelector + _resultSelector = resultSelector } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { @@ -109,14 +109,14 @@ class CombineLatestSink3_ : CombineLatestSink { typealias R = O.E typealias Parent = CombineLatest3 - let parent: Parent + private let _parent: Parent - var latestElement1: E1! = nil - var latestElement2: E2! = nil - var latestElement3: E3! = nil + private var _latestElement1: E1! = nil + private var _latestElement2: E2! = nil + private var _latestElement3: E3! = nil init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(arity: 3, observer: observer, cancel: cancel) } @@ -125,13 +125,13 @@ class CombineLatestSink3_ : CombineLatestSink { let subscription2 = SingleAssignmentDisposable() let subscription3 = SingleAssignmentDisposable() - let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self.latestElement1 = e }, this: subscription1) - let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self.latestElement2 = e }, this: subscription2) - let observer3 = CombineLatestObserver(lock: lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self.latestElement3 = e }, this: subscription3) + let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) + let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) + let observer3 = CombineLatestObserver(lock: lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) - subscription1.disposable = parent.source1.subscribeSafe(observer1) - subscription2.disposable = parent.source2.subscribeSafe(observer2) - subscription3.disposable = parent.source3.subscribeSafe(observer3) + subscription1.disposable = _parent._source1.subscribeSafe(observer1) + subscription2.disposable = _parent._source2.subscribeSafe(observer2) + subscription3.disposable = _parent._source3.subscribeSafe(observer3) return CompositeDisposable(disposables: [ subscription1, @@ -141,25 +141,25 @@ class CombineLatestSink3_ : CombineLatestSink { } override func getResult() throws -> R { - return try self.parent.resultSelector(latestElement1, latestElement2, latestElement3) + return try _parent._resultSelector(_latestElement1, _latestElement2, _latestElement3) } } class CombineLatest3 : Producer { typealias ResultSelector = (E1, E2, E3) throws -> R - let source1: Observable - let source2: Observable - let source3: Observable + private let _source1: Observable + private let _source2: Observable + private let _source3: Observable - let resultSelector: ResultSelector + private let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, resultSelector: ResultSelector) { - self.source1 = source1 - self.source2 = source2 - self.source3 = source3 + _source1 = source1 + _source2 = source2 + _source3 = source3 - self.resultSelector = resultSelector + _resultSelector = resultSelector } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { @@ -192,15 +192,15 @@ class CombineLatestSink4_ : CombineLatestSink - let parent: Parent + private let _parent: Parent - var latestElement1: E1! = nil - var latestElement2: E2! = nil - var latestElement3: E3! = nil - var latestElement4: E4! = nil + private var _latestElement1: E1! = nil + private var _latestElement2: E2! = nil + private var _latestElement3: E3! = nil + private var _latestElement4: E4! = nil init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(arity: 4, observer: observer, cancel: cancel) } @@ -210,15 +210,15 @@ class CombineLatestSink4_ : CombineLatestSink Void in self.latestElement1 = e }, this: subscription1) - let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self.latestElement2 = e }, this: subscription2) - let observer3 = CombineLatestObserver(lock: lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self.latestElement3 = e }, this: subscription3) - let observer4 = CombineLatestObserver(lock: lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self.latestElement4 = e }, this: subscription4) + let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) + let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) + let observer3 = CombineLatestObserver(lock: lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) + let observer4 = CombineLatestObserver(lock: lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) - subscription1.disposable = parent.source1.subscribeSafe(observer1) - subscription2.disposable = parent.source2.subscribeSafe(observer2) - subscription3.disposable = parent.source3.subscribeSafe(observer3) - subscription4.disposable = parent.source4.subscribeSafe(observer4) + subscription1.disposable = _parent._source1.subscribeSafe(observer1) + subscription2.disposable = _parent._source2.subscribeSafe(observer2) + subscription3.disposable = _parent._source3.subscribeSafe(observer3) + subscription4.disposable = _parent._source4.subscribeSafe(observer4) return CompositeDisposable(disposables: [ subscription1, @@ -229,27 +229,27 @@ class CombineLatestSink4_ : CombineLatestSink R { - return try self.parent.resultSelector(latestElement1, latestElement2, latestElement3, latestElement4) + return try _parent._resultSelector(_latestElement1, _latestElement2, _latestElement3, _latestElement4) } } class CombineLatest4 : Producer { typealias ResultSelector = (E1, E2, E3, E4) throws -> R - let source1: Observable - let source2: Observable - let source3: Observable - let source4: Observable + private let _source1: Observable + private let _source2: Observable + private let _source3: Observable + private let _source4: Observable - let resultSelector: ResultSelector + private let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, resultSelector: ResultSelector) { - self.source1 = source1 - self.source2 = source2 - self.source3 = source3 - self.source4 = source4 + _source1 = source1 + _source2 = source2 + _source3 = source3 + _source4 = source4 - self.resultSelector = resultSelector + _resultSelector = resultSelector } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { @@ -282,16 +282,16 @@ class CombineLatestSink5_ : CombineLatestSi typealias R = O.E typealias Parent = CombineLatest5 - let parent: Parent + private let _parent: Parent - var latestElement1: E1! = nil - var latestElement2: E2! = nil - var latestElement3: E3! = nil - var latestElement4: E4! = nil - var latestElement5: E5! = nil + private var _latestElement1: E1! = nil + private var _latestElement2: E2! = nil + private var _latestElement3: E3! = nil + private var _latestElement4: E4! = nil + private var _latestElement5: E5! = nil init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(arity: 5, observer: observer, cancel: cancel) } @@ -302,17 +302,17 @@ class CombineLatestSink5_ : CombineLatestSi let subscription4 = SingleAssignmentDisposable() let subscription5 = SingleAssignmentDisposable() - let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self.latestElement1 = e }, this: subscription1) - let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self.latestElement2 = e }, this: subscription2) - let observer3 = CombineLatestObserver(lock: lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self.latestElement3 = e }, this: subscription3) - let observer4 = CombineLatestObserver(lock: lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self.latestElement4 = e }, this: subscription4) - let observer5 = CombineLatestObserver(lock: lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self.latestElement5 = e }, this: subscription5) + let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) + let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) + let observer3 = CombineLatestObserver(lock: lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) + let observer4 = CombineLatestObserver(lock: lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) + let observer5 = CombineLatestObserver(lock: lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5) - subscription1.disposable = parent.source1.subscribeSafe(observer1) - subscription2.disposable = parent.source2.subscribeSafe(observer2) - subscription3.disposable = parent.source3.subscribeSafe(observer3) - subscription4.disposable = parent.source4.subscribeSafe(observer4) - subscription5.disposable = parent.source5.subscribeSafe(observer5) + subscription1.disposable = _parent._source1.subscribeSafe(observer1) + subscription2.disposable = _parent._source2.subscribeSafe(observer2) + subscription3.disposable = _parent._source3.subscribeSafe(observer3) + subscription4.disposable = _parent._source4.subscribeSafe(observer4) + subscription5.disposable = _parent._source5.subscribeSafe(observer5) return CompositeDisposable(disposables: [ subscription1, @@ -324,29 +324,29 @@ class CombineLatestSink5_ : CombineLatestSi } override func getResult() throws -> R { - return try self.parent.resultSelector(latestElement1, latestElement2, latestElement3, latestElement4, latestElement5) + return try _parent._resultSelector(_latestElement1, _latestElement2, _latestElement3, _latestElement4, _latestElement5) } } class CombineLatest5 : Producer { typealias ResultSelector = (E1, E2, E3, E4, E5) throws -> R - let source1: Observable - let source2: Observable - let source3: Observable - let source4: Observable - let source5: Observable + private let _source1: Observable + private let _source2: Observable + private let _source3: Observable + private let _source4: Observable + private let _source5: Observable - let resultSelector: ResultSelector + private let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, resultSelector: ResultSelector) { - self.source1 = source1 - self.source2 = source2 - self.source3 = source3 - self.source4 = source4 - self.source5 = source5 + _source1 = source1 + _source2 = source2 + _source3 = source3 + _source4 = source4 + _source5 = source5 - self.resultSelector = resultSelector + _resultSelector = resultSelector } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { @@ -379,17 +379,17 @@ class CombineLatestSink6_ : CombineLate typealias R = O.E typealias Parent = CombineLatest6 - let parent: Parent + private let _parent: Parent - var latestElement1: E1! = nil - var latestElement2: E2! = nil - var latestElement3: E3! = nil - var latestElement4: E4! = nil - var latestElement5: E5! = nil - var latestElement6: E6! = nil + private var _latestElement1: E1! = nil + private var _latestElement2: E2! = nil + private var _latestElement3: E3! = nil + private var _latestElement4: E4! = nil + private var _latestElement5: E5! = nil + private var _latestElement6: E6! = nil init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(arity: 6, observer: observer, cancel: cancel) } @@ -401,19 +401,19 @@ class CombineLatestSink6_ : CombineLate let subscription5 = SingleAssignmentDisposable() let subscription6 = SingleAssignmentDisposable() - let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self.latestElement1 = e }, this: subscription1) - let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self.latestElement2 = e }, this: subscription2) - let observer3 = CombineLatestObserver(lock: lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self.latestElement3 = e }, this: subscription3) - let observer4 = CombineLatestObserver(lock: lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self.latestElement4 = e }, this: subscription4) - let observer5 = CombineLatestObserver(lock: lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self.latestElement5 = e }, this: subscription5) - let observer6 = CombineLatestObserver(lock: lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self.latestElement6 = e }, this: subscription6) + let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) + let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) + let observer3 = CombineLatestObserver(lock: lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) + let observer4 = CombineLatestObserver(lock: lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) + let observer5 = CombineLatestObserver(lock: lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5) + let observer6 = CombineLatestObserver(lock: lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6) - subscription1.disposable = parent.source1.subscribeSafe(observer1) - subscription2.disposable = parent.source2.subscribeSafe(observer2) - subscription3.disposable = parent.source3.subscribeSafe(observer3) - subscription4.disposable = parent.source4.subscribeSafe(observer4) - subscription5.disposable = parent.source5.subscribeSafe(observer5) - subscription6.disposable = parent.source6.subscribeSafe(observer6) + subscription1.disposable = _parent._source1.subscribeSafe(observer1) + subscription2.disposable = _parent._source2.subscribeSafe(observer2) + subscription3.disposable = _parent._source3.subscribeSafe(observer3) + subscription4.disposable = _parent._source4.subscribeSafe(observer4) + subscription5.disposable = _parent._source5.subscribeSafe(observer5) + subscription6.disposable = _parent._source6.subscribeSafe(observer6) return CompositeDisposable(disposables: [ subscription1, @@ -426,31 +426,31 @@ class CombineLatestSink6_ : CombineLate } override func getResult() throws -> R { - return try self.parent.resultSelector(latestElement1, latestElement2, latestElement3, latestElement4, latestElement5, latestElement6) + return try _parent._resultSelector(_latestElement1, _latestElement2, _latestElement3, _latestElement4, _latestElement5, _latestElement6) } } class CombineLatest6 : Producer { typealias ResultSelector = (E1, E2, E3, E4, E5, E6) throws -> R - let source1: Observable - let source2: Observable - let source3: Observable - let source4: Observable - let source5: Observable - let source6: Observable + private let _source1: Observable + private let _source2: Observable + private let _source3: Observable + private let _source4: Observable + private let _source5: Observable + private let _source6: Observable - let resultSelector: ResultSelector + private let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, source6: Observable, resultSelector: ResultSelector) { - self.source1 = source1 - self.source2 = source2 - self.source3 = source3 - self.source4 = source4 - self.source5 = source5 - self.source6 = source6 + _source1 = source1 + _source2 = source2 + _source3 = source3 + _source4 = source4 + _source5 = source5 + _source6 = source6 - self.resultSelector = resultSelector + _resultSelector = resultSelector } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { @@ -483,18 +483,18 @@ class CombineLatestSink7_ : Combine typealias R = O.E typealias Parent = CombineLatest7 - let parent: Parent + private let _parent: Parent - var latestElement1: E1! = nil - var latestElement2: E2! = nil - var latestElement3: E3! = nil - var latestElement4: E4! = nil - var latestElement5: E5! = nil - var latestElement6: E6! = nil - var latestElement7: E7! = nil + private var _latestElement1: E1! = nil + private var _latestElement2: E2! = nil + private var _latestElement3: E3! = nil + private var _latestElement4: E4! = nil + private var _latestElement5: E5! = nil + private var _latestElement6: E6! = nil + private var _latestElement7: E7! = nil init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(arity: 7, observer: observer, cancel: cancel) } @@ -507,21 +507,21 @@ class CombineLatestSink7_ : Combine let subscription6 = SingleAssignmentDisposable() let subscription7 = SingleAssignmentDisposable() - let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self.latestElement1 = e }, this: subscription1) - let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self.latestElement2 = e }, this: subscription2) - let observer3 = CombineLatestObserver(lock: lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self.latestElement3 = e }, this: subscription3) - let observer4 = CombineLatestObserver(lock: lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self.latestElement4 = e }, this: subscription4) - let observer5 = CombineLatestObserver(lock: lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self.latestElement5 = e }, this: subscription5) - let observer6 = CombineLatestObserver(lock: lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self.latestElement6 = e }, this: subscription6) - let observer7 = CombineLatestObserver(lock: lock, parent: self, index: 6, setLatestValue: { (e: E7) -> Void in self.latestElement7 = e }, this: subscription7) + let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) + let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) + let observer3 = CombineLatestObserver(lock: lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) + let observer4 = CombineLatestObserver(lock: lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) + let observer5 = CombineLatestObserver(lock: lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5) + let observer6 = CombineLatestObserver(lock: lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6) + let observer7 = CombineLatestObserver(lock: lock, parent: self, index: 6, setLatestValue: { (e: E7) -> Void in self._latestElement7 = e }, this: subscription7) - subscription1.disposable = parent.source1.subscribeSafe(observer1) - subscription2.disposable = parent.source2.subscribeSafe(observer2) - subscription3.disposable = parent.source3.subscribeSafe(observer3) - subscription4.disposable = parent.source4.subscribeSafe(observer4) - subscription5.disposable = parent.source5.subscribeSafe(observer5) - subscription6.disposable = parent.source6.subscribeSafe(observer6) - subscription7.disposable = parent.source7.subscribeSafe(observer7) + subscription1.disposable = _parent._source1.subscribeSafe(observer1) + subscription2.disposable = _parent._source2.subscribeSafe(observer2) + subscription3.disposable = _parent._source3.subscribeSafe(observer3) + subscription4.disposable = _parent._source4.subscribeSafe(observer4) + subscription5.disposable = _parent._source5.subscribeSafe(observer5) + subscription6.disposable = _parent._source6.subscribeSafe(observer6) + subscription7.disposable = _parent._source7.subscribeSafe(observer7) return CompositeDisposable(disposables: [ subscription1, @@ -535,33 +535,33 @@ class CombineLatestSink7_ : Combine } override func getResult() throws -> R { - return try self.parent.resultSelector(latestElement1, latestElement2, latestElement3, latestElement4, latestElement5, latestElement6, latestElement7) + return try _parent._resultSelector(_latestElement1, _latestElement2, _latestElement3, _latestElement4, _latestElement5, _latestElement6, _latestElement7) } } class CombineLatest7 : Producer { typealias ResultSelector = (E1, E2, E3, E4, E5, E6, E7) throws -> R - let source1: Observable - let source2: Observable - let source3: Observable - let source4: Observable - let source5: Observable - let source6: Observable - let source7: Observable + private let _source1: Observable + private let _source2: Observable + private let _source3: Observable + private let _source4: Observable + private let _source5: Observable + private let _source6: Observable + private let _source7: Observable - let resultSelector: ResultSelector + private let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, source6: Observable, source7: Observable, resultSelector: ResultSelector) { - self.source1 = source1 - self.source2 = source2 - self.source3 = source3 - self.source4 = source4 - self.source5 = source5 - self.source6 = source6 - self.source7 = source7 + _source1 = source1 + _source2 = source2 + _source3 = source3 + _source4 = source4 + _source5 = source5 + _source6 = source6 + _source7 = source7 - self.resultSelector = resultSelector + _resultSelector = resultSelector } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { @@ -594,19 +594,19 @@ class CombineLatestSink8_ : Com typealias R = O.E typealias Parent = CombineLatest8 - let parent: Parent + private let _parent: Parent - var latestElement1: E1! = nil - var latestElement2: E2! = nil - var latestElement3: E3! = nil - var latestElement4: E4! = nil - var latestElement5: E5! = nil - var latestElement6: E6! = nil - var latestElement7: E7! = nil - var latestElement8: E8! = nil + private var _latestElement1: E1! = nil + private var _latestElement2: E2! = nil + private var _latestElement3: E3! = nil + private var _latestElement4: E4! = nil + private var _latestElement5: E5! = nil + private var _latestElement6: E6! = nil + private var _latestElement7: E7! = nil + private var _latestElement8: E8! = nil init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(arity: 8, observer: observer, cancel: cancel) } @@ -620,23 +620,23 @@ class CombineLatestSink8_ : Com let subscription7 = SingleAssignmentDisposable() let subscription8 = SingleAssignmentDisposable() - let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self.latestElement1 = e }, this: subscription1) - let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self.latestElement2 = e }, this: subscription2) - let observer3 = CombineLatestObserver(lock: lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self.latestElement3 = e }, this: subscription3) - let observer4 = CombineLatestObserver(lock: lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self.latestElement4 = e }, this: subscription4) - let observer5 = CombineLatestObserver(lock: lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self.latestElement5 = e }, this: subscription5) - let observer6 = CombineLatestObserver(lock: lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self.latestElement6 = e }, this: subscription6) - let observer7 = CombineLatestObserver(lock: lock, parent: self, index: 6, setLatestValue: { (e: E7) -> Void in self.latestElement7 = e }, this: subscription7) - let observer8 = CombineLatestObserver(lock: lock, parent: self, index: 7, setLatestValue: { (e: E8) -> Void in self.latestElement8 = e }, this: subscription8) + let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) + let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) + let observer3 = CombineLatestObserver(lock: lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) + let observer4 = CombineLatestObserver(lock: lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) + let observer5 = CombineLatestObserver(lock: lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5) + let observer6 = CombineLatestObserver(lock: lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6) + let observer7 = CombineLatestObserver(lock: lock, parent: self, index: 6, setLatestValue: { (e: E7) -> Void in self._latestElement7 = e }, this: subscription7) + let observer8 = CombineLatestObserver(lock: lock, parent: self, index: 7, setLatestValue: { (e: E8) -> Void in self._latestElement8 = e }, this: subscription8) - subscription1.disposable = parent.source1.subscribeSafe(observer1) - subscription2.disposable = parent.source2.subscribeSafe(observer2) - subscription3.disposable = parent.source3.subscribeSafe(observer3) - subscription4.disposable = parent.source4.subscribeSafe(observer4) - subscription5.disposable = parent.source5.subscribeSafe(observer5) - subscription6.disposable = parent.source6.subscribeSafe(observer6) - subscription7.disposable = parent.source7.subscribeSafe(observer7) - subscription8.disposable = parent.source8.subscribeSafe(observer8) + subscription1.disposable = _parent._source1.subscribeSafe(observer1) + subscription2.disposable = _parent._source2.subscribeSafe(observer2) + subscription3.disposable = _parent._source3.subscribeSafe(observer3) + subscription4.disposable = _parent._source4.subscribeSafe(observer4) + subscription5.disposable = _parent._source5.subscribeSafe(observer5) + subscription6.disposable = _parent._source6.subscribeSafe(observer6) + subscription7.disposable = _parent._source7.subscribeSafe(observer7) + subscription8.disposable = _parent._source8.subscribeSafe(observer8) return CompositeDisposable(disposables: [ subscription1, @@ -651,35 +651,35 @@ class CombineLatestSink8_ : Com } override func getResult() throws -> R { - return try self.parent.resultSelector(latestElement1, latestElement2, latestElement3, latestElement4, latestElement5, latestElement6, latestElement7, latestElement8) + return try _parent._resultSelector(_latestElement1, _latestElement2, _latestElement3, _latestElement4, _latestElement5, _latestElement6, _latestElement7, _latestElement8) } } class CombineLatest8 : Producer { typealias ResultSelector = (E1, E2, E3, E4, E5, E6, E7, E8) throws -> R - let source1: Observable - let source2: Observable - let source3: Observable - let source4: Observable - let source5: Observable - let source6: Observable - let source7: Observable - let source8: Observable + private let _source1: Observable + private let _source2: Observable + private let _source3: Observable + private let _source4: Observable + private let _source5: Observable + private let _source6: Observable + private let _source7: Observable + private let _source8: Observable - let resultSelector: ResultSelector + private let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, source6: Observable, source7: Observable, source8: Observable, resultSelector: ResultSelector) { - self.source1 = source1 - self.source2 = source2 - self.source3 = source3 - self.source4 = source4 - self.source5 = source5 - self.source6 = source6 - self.source7 = source7 - self.source8 = source8 + _source1 = source1 + _source2 = source2 + _source3 = source3 + _source4 = source4 + _source5 = source5 + _source6 = source6 + _source7 = source7 + _source8 = source8 - self.resultSelector = resultSelector + _resultSelector = resultSelector } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { diff --git a/RxSwift/Observables/Implementations/CombineLatest.swift b/RxSwift/Observables/Implementations/CombineLatest.swift index 50e4311f..8ed9b752 100644 --- a/RxSwift/Observables/Implementations/CombineLatest.swift +++ b/RxSwift/Observables/Implementations/CombineLatest.swift @@ -19,16 +19,16 @@ class CombineLatestSink : Sink, CombineLatestProtocol { let lock = NSRecursiveLock() - let arity: Int - var numberOfValues = 0 - var numberOfDone = 0 - var hasValue: [Bool] - var isDone: [Bool] + private let _arity: Int + private var _numberOfValues = 0 + private var _numberOfDone = 0 + private var _hasValue: [Bool] + private var _isDone: [Bool] init(arity: Int, observer: O, cancel: Disposable) { - self.arity = arity - self.hasValue = [Bool](count: arity, repeatedValue: false) - self.isDone = [Bool](count: arity, repeatedValue: false) + _arity = arity + _hasValue = [Bool](count: arity, repeatedValue: false) + _isDone = [Bool](count: arity, repeatedValue: false) super.init(observer: observer, cancel: cancel) } @@ -38,26 +38,26 @@ class CombineLatestSink : Sink, CombineLatestProtocol { } func next(index: Int) { - if !hasValue[index] { - hasValue[index] = true - numberOfValues++ + if !_hasValue[index] { + _hasValue[index] = true + _numberOfValues++ } - if numberOfValues == arity { + if _numberOfValues == _arity { do { let result = try getResult() observer?.on(.Next(result)) } catch let e { observer?.on(.Error(e)) - self.dispose() + dispose() } } else { var allOthersDone = true - for var i = 0; i < arity; ++i { - if i != index && !isDone[i] { + for var i = 0; i < _arity; ++i { + if i != index && !_isDone[i] { allOthersDone = false break } @@ -65,7 +65,7 @@ class CombineLatestSink : Sink, CombineLatestProtocol { if allOthersDone { observer?.on(.Completed) - self.dispose() + dispose() } } } @@ -76,55 +76,51 @@ class CombineLatestSink : Sink, CombineLatestProtocol { } func done(index: Int) { - if isDone[index] { + if _isDone[index] { return } - isDone[index] = true - numberOfDone++ + _isDone[index] = true + _numberOfDone++ - if numberOfDone == self.arity { + if _numberOfDone == _arity { observer?.on(.Completed) dispose() } } - - deinit { - - } } class CombineLatestObserver : ObserverType { typealias Element = ElementType typealias ValueSetter = (Element) -> Void - let parent: CombineLatestProtocol + private let _parent: CombineLatestProtocol - let lock: NSRecursiveLock - let index: Int - let this: Disposable - let setLatestValue: ValueSetter + private let _lock: NSRecursiveLock + private let _index: Int + private let _this: Disposable + private let _setLatestValue: ValueSetter init(lock: NSRecursiveLock, parent: CombineLatestProtocol, index: Int, setLatestValue: ValueSetter, this: Disposable) { - self.lock = lock - self.parent = parent - self.index = index - self.this = this - self.setLatestValue = setLatestValue + _lock = lock + _parent = parent + _index = index + _this = this + _setLatestValue = setLatestValue } func on(event: Event) { - lock.performLocked { + _lock.performLocked { switch event { case .Next(let value): - setLatestValue(value) - parent.next(index) + _setLatestValue(value) + _parent.next(_index) case .Error(let error): - this.dispose() - parent.fail(error) + _this.dispose() + _parent.fail(error) case .Completed: - this.dispose() - parent.done(index) + _this.dispose() + _parent.done(_index) } } } diff --git a/RxSwift/Observables/Implementations/Concat.swift b/RxSwift/Observables/Implementations/Concat.swift index 042bf4df..c39d50f7 100644 --- a/RxSwift/Observables/Implementations/Concat.swift +++ b/RxSwift/Observables/Implementations/Concat.swift @@ -18,7 +18,7 @@ class ConcatSink){ switch event { - case .Next(_): + case .Next: observer?.on(event) case .Error: observer?.on(event) @@ -30,7 +30,7 @@ class ConcatSink) -> S.Generator? { if let source = observable as? Concat { - return source.sources.generate() + return source._sources.generate() } else { return nil @@ -41,10 +41,10 @@ class ConcatSink : Producer { typealias Element = S.Generator.Element.E - let sources: S + private let _sources: S init(sources: S) { - self.sources = sources + _sources = sources } override func run @@ -52,6 +52,6 @@ class Concat : Produ let sink = ConcatSink(observer: observer, cancel: cancel) setSink(sink) - return sink.run(sources.generate()) + return sink.run(_sources.generate()) } } \ No newline at end of file From 16e851fe4000cbf07a6d5f62876233c0471906de Mon Sep 17 00:00:00 2001 From: yury Date: Sat, 17 Oct 2015 12:38:45 +0300 Subject: [PATCH 043/210] More private vars --- .../Observables/Implementations/Debug.swift | 20 ++--- RxSwift/Observables/Implementations/Do.swift | 16 ++-- .../Observables/Implementations/Empty.swift | 4 - .../Observables/Implementations/Filter.swift | 20 ++--- .../Observables/Implementations/FlatMap.swift | 86 +++++++++---------- .../Implementations/Generate.swift | 36 ++++---- RxSwift/Observables/Implementations/Map.swift | 38 ++++---- .../Implementations/Multicast.swift | 22 ++--- .../Observables/Implementations/Range.swift | 22 ++--- .../Observables/Implementations/Reduce.swift | 39 ++++----- 10 files changed, 149 insertions(+), 154 deletions(-) diff --git a/RxSwift/Observables/Implementations/Debug.swift b/RxSwift/Observables/Implementations/Debug.swift index 3609436f..e0edaf70 100644 --- a/RxSwift/Observables/Implementations/Debug.swift +++ b/RxSwift/Observables/Implementations/Debug.swift @@ -12,10 +12,10 @@ class Debug_ : Sink, ObserverType { typealias Element = O.E typealias Parent = Debug - let parent: Parent + private let _parent: Parent init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } @@ -25,30 +25,30 @@ class Debug_ : Sink, ObserverType { let eventNormalized = eventText.characters.count > maxEventTextLength ? String(eventText.characters.prefix(maxEventTextLength / 2)) + "..." + String(eventText.characters.suffix(maxEventTextLength / 2)) : eventText - print("[\(parent.identifier)] -> Event \(eventNormalized)") + print("[\(_parent._identifier)] -> Event \(eventNormalized)") observer?.on(event) } override func dispose() { - print("[\(parent.identifier)] dispose") + print("[\(_parent._identifier)] dispose") super.dispose() } } class Debug : Producer { - let identifier: String + private let _identifier: String - let source: Observable + private let _source: Observable init(source: Observable, identifier: String) { - self.identifier = identifier - self.source = source + _identifier = identifier + _source = source } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - print("[\(identifier)] subscribed") + print("[\(_identifier)] subscribed") let sink = Debug_(parent: self, observer: observer, cancel: cancel) setSink(sink) - return self.source.subscribeSafe(sink) + return _source.subscribeSafe(sink) } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Do.swift b/RxSwift/Observables/Implementations/Do.swift index bfd15bdd..1c02425f 100644 --- a/RxSwift/Observables/Implementations/Do.swift +++ b/RxSwift/Observables/Implementations/Do.swift @@ -21,15 +21,15 @@ class DoSink : Sink, ObserverType { func on(event: Event) { do { - try _parent.eventHandler(event) + try _parent._eventHandler(event) observer?.on(event) if event.isStopEvent { - self.dispose() + dispose() } } catch let error { observer?.on(.Error(error)) - self.dispose() + dispose() } } } @@ -37,12 +37,12 @@ class DoSink : Sink, ObserverType { class Do : Producer { typealias EventHandler = Event throws -> Void - let source: Observable - let eventHandler: EventHandler + private let _source: Observable + private let _eventHandler: EventHandler init(source: Observable, eventHandler: EventHandler) { - self.source = source - self.eventHandler = eventHandler + _source = source + _eventHandler = eventHandler } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { @@ -50,6 +50,6 @@ class Do : Producer { setSink(sink) - return self.source.subscribeSafe(sink) + return _source.subscribeSafe(sink) } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Empty.swift b/RxSwift/Observables/Implementations/Empty.swift index c9025ead..6b9fcde7 100644 --- a/RxSwift/Observables/Implementations/Empty.swift +++ b/RxSwift/Observables/Implementations/Empty.swift @@ -9,10 +9,6 @@ import Foundation class Empty : Producer { - override init() { - - } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { observer.on(.Completed) return NopDisposable.instance diff --git a/RxSwift/Observables/Implementations/Filter.swift b/RxSwift/Observables/Implementations/Filter.swift index 784cc03e..a9ed5713 100644 --- a/RxSwift/Observables/Implementations/Filter.swift +++ b/RxSwift/Observables/Implementations/Filter.swift @@ -13,10 +13,10 @@ class FilterSink: Sink, ObserverType { typealias Parent = Filter - let parent: Parent + private let _parent: Parent init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } @@ -24,18 +24,18 @@ class FilterSink: Sink, ObserverType { switch event { case .Next(let value): do { - let satisfies = try self.parent.predicate(value) + let satisfies = try _parent._predicate(value) if satisfies { observer?.on(.Next(value)) } } catch let e { observer?.on(.Error(e)) - self.dispose() + dispose() } case .Completed, .Error: observer?.on(event) - self.dispose() + dispose() } } } @@ -43,17 +43,17 @@ class FilterSink: Sink, ObserverType { class Filter : Producer { typealias Predicate = (Element) throws -> Bool - let source: Observable - let predicate: Predicate + private let _source: Observable + private let _predicate: Predicate init(source: Observable, predicate: Predicate) { - self.source = source - self.predicate = predicate + _source = source + _predicate = predicate } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { let sink = FilterSink(parent: self, observer: observer, cancel: cancel) setSink(sink) - return source.subscribeSafe(sink) + return _source.subscribeSafe(sink) } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/FlatMap.swift b/RxSwift/Observables/Implementations/FlatMap.swift index 34715a7c..6965cc90 100644 --- a/RxSwift/Observables/Implementations/FlatMap.swift +++ b/RxSwift/Observables/Implementations/FlatMap.swift @@ -16,36 +16,36 @@ class FlatMapSinkIter) { switch event { case .Next(let value): - parent.lock.performLocked { - parent.observer?.on(.Next(value)) + _parent._lock.performLocked { + _parent.observer?.on(.Next(value)) } case .Error(let error): - parent.lock.performLocked { - parent.observer?.on(.Error(error)) - self.parent.dispose() + _parent._lock.performLocked { + _parent.observer?.on(.Error(error)) + _parent.dispose() } case .Completed: - parent.group.removeDisposable(disposeKey) + _parent._group.removeDisposable(_disposeKey) // If this has returned true that means that `Completed` should be sent. // In case there is a race who will sent first completed, // lock will sort it out. When first Completed message is sent // it will set observer to nil, and thus prevent further complete messages // to be sent, and thus preserving the sequence grammar. - if parent.stopped && parent.group.count == FlatMapNoIterators { - parent.lock.performLocked { - parent.observer?.on(.Completed) - self.parent.dispose() + if _parent._stopped && _parent._group.count == FlatMapNoIterators { + _parent._lock.performLocked { + _parent.observer?.on(.Completed) + _parent.dispose() } } } @@ -57,18 +57,18 @@ class FlatMapSink - let parent: Parent + private let _parent: Parent - let lock = NSRecursiveLock() + private let _lock = NSRecursiveLock() // state - let group = CompositeDisposable() - let sourceSubscription = SingleAssignmentDisposable() + private let _group = CompositeDisposable() + private let _sourceSubscription = SingleAssignmentDisposable() - var stopped = false + private var _stopped = false init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } @@ -90,33 +90,33 @@ class FlatMapSink) { let iterDisposable = SingleAssignmentDisposable() - if let disposeKey = group.addDisposable(iterDisposable) { + if let disposeKey = _group.addDisposable(iterDisposable) { let iter = FlatMapSinkIter(parent: self, disposeKey: disposeKey) let subscription = source.subscribeSafe(iter) iterDisposable.disposable = subscription @@ -124,12 +124,12 @@ class FlatMapSink Disposable { - group.addDisposable(sourceSubscription) + _group.addDisposable(_sourceSubscription) - let subscription = self.parent.source.subscribeSafe(self) - sourceSubscription.disposable = subscription + let subscription = _parent._source.subscribeSafe(self) + _sourceSubscription.disposable = subscription - return group + return _group } } @@ -139,7 +139,7 @@ class FlatMapSink1 S { - return try self.parent.selector1!(element) + return try _parent._selector1!(element) } } @@ -151,7 +151,7 @@ class FlatMapSink2 S { - return try parent.selector2!(element, try incrementChecked(&_index)) + return try _parent._selector2!(element, try incrementChecked(&_index)) } } @@ -159,25 +159,25 @@ class FlatMap: Producer { typealias Selector1 = (SourceType) throws -> S typealias Selector2 = (SourceType, Int) throws -> S - let source: Observable + private let _source: Observable - let selector1: Selector1? - let selector2: Selector2? + private let _selector1: Selector1? + private let _selector2: Selector2? init(source: Observable, selector: Selector1) { - self.source = source - self.selector1 = selector - self.selector2 = nil + _source = source + _selector1 = selector + _selector2 = nil } init(source: Observable, selector: Selector2) { - self.source = source - self.selector2 = selector - self.selector1 = nil + _source = source + _selector2 = selector + _selector1 = nil } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - if let _ = self.selector1 { + if let _ = _selector1 { let sink = FlatMapSink1(parent: self, observer: observer, cancel: cancel) setSink(sink) return sink.run() diff --git a/RxSwift/Observables/Implementations/Generate.swift b/RxSwift/Observables/Implementations/Generate.swift index 11f996bf..4e6a51c1 100644 --- a/RxSwift/Observables/Implementations/Generate.swift +++ b/RxSwift/Observables/Implementations/Generate.swift @@ -11,25 +11,25 @@ import Foundation class GenerateSink : Sink { typealias Parent = Generate - let parent: Parent + private let _parent: Parent - var state: S + private var _state: S init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent - self.state = parent.initialState + _parent = parent + _state = parent._initialState super.init(observer: observer, cancel: cancel) } func run() -> Disposable { - return parent.scheduler.scheduleRecursive(true) { (isFirst, recurse) -> Void in + return _parent._scheduler.scheduleRecursive(true) { (isFirst, recurse) -> Void in do { if !isFirst { - self.state = try self.parent.iterate(self.state) + self._state = try self._parent._iterate(self._state) } - if try self.parent.condition(self.state) { - let result = try self.parent.resultSelector(self.state) + if try self._parent._condition(self._state) { + let result = try self._parent._resultSelector(self._state) self.observer?.on(.Next(result)) recurse(false) @@ -48,18 +48,18 @@ class GenerateSink : Sink { } class Generate : Producer { - let initialState: S - let condition: S throws -> Bool - let iterate: S throws -> S - let resultSelector: S throws -> E - let scheduler: ImmediateSchedulerType + private let _initialState: S + private let _condition: S throws -> Bool + private let _iterate: S throws -> S + private let _resultSelector: S throws -> E + private let _scheduler: ImmediateSchedulerType init(initialState: S, condition: S throws -> Bool, iterate: S throws -> S, resultSelector: S throws -> E, scheduler: ImmediateSchedulerType) { - self.initialState = initialState - self.condition = condition - self.iterate = iterate - self.resultSelector = resultSelector - self.scheduler = scheduler + _initialState = initialState + _condition = condition + _iterate = iterate + _resultSelector = resultSelector + _scheduler = scheduler super.init() } diff --git a/RxSwift/Observables/Implementations/Map.swift b/RxSwift/Observables/Implementations/Map.swift index 4d559e8b..5a0d8b1e 100644 --- a/RxSwift/Observables/Implementations/Map.swift +++ b/RxSwift/Observables/Implementations/Map.swift @@ -13,10 +13,10 @@ class MapSink : Sink, ObserverType { typealias Element = SourceType typealias Parent = Map - let parent: Parent + private let _parent: Parent init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } @@ -35,14 +35,14 @@ class MapSink : Sink, ObserverType { } catch let e { observer?.on(.Error(e)) - self.dispose() + dispose() } case .Error(let error): observer?.on(.Error(error)) - self.dispose() + dispose() case .Completed: observer?.on(.Completed) - self.dispose() + dispose() } } } @@ -55,7 +55,7 @@ class MapSink1 : MapSink { } override func performMap(element: SourceType) throws -> ResultType { - return try self.parent.selector1!(element) + return try _parent._selector1!(element) } } @@ -68,7 +68,7 @@ class MapSink2 : MapSink { super.init(parent: parent, observer: observer, cancel: cancel) } override func performMap(element: SourceType) throws -> ResultType { - return try self.parent.selector2!(element, try incrementChecked(&_index)) + return try _parent._selector2!(element, try incrementChecked(&_index)) } } @@ -76,33 +76,33 @@ class Map: Producer { typealias Selector1 = (SourceType) throws -> ResultType typealias Selector2 = (SourceType, Int) throws -> ResultType - let source: Observable + private let _source: Observable - let selector1: Selector1? - let selector2: Selector2? + private let _selector1: Selector1? + private let _selector2: Selector2? init(source: Observable, selector: Selector1) { - self.source = source - self.selector1 = selector - self.selector2 = nil + _source = source + _selector1 = selector + _selector2 = nil } init(source: Observable, selector: Selector2) { - self.source = source - self.selector2 = selector - self.selector1 = nil + _source = source + _selector2 = selector + _selector1 = nil } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - if let _ = self.selector1 { + if let _ = _selector1 { let sink = MapSink1(parent: self, observer: observer, cancel: cancel) setSink(sink) - return self.source.subscribeSafe(sink) + return _source.subscribeSafe(sink) } else { let sink = MapSink2(parent: self, observer: observer, cancel: cancel) setSink(sink) - return self.source.subscribeSafe(sink) + return _source.subscribeSafe(sink) } } diff --git a/RxSwift/Observables/Implementations/Multicast.swift b/RxSwift/Observables/Implementations/Multicast.swift index 25ff6915..1a8cccde 100644 --- a/RxSwift/Observables/Implementations/Multicast.swift +++ b/RxSwift/Observables/Implementations/Multicast.swift @@ -13,19 +13,19 @@ class MulticastSink: Sink, ObserverType { typealias ResultType = Element typealias MutlicastType = Multicast - let parent: MutlicastType + private let _parent: MutlicastType init(parent: MutlicastType, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { do { - let subject = try parent.subjectSelector() - let connectable = ConnectableObservable(source: self.parent.source, subject: subject) + let subject = try _parent._subjectSelector() + let connectable = ConnectableObservable(source: _parent._source, subject: subject) - let observable = try self.parent.selector(connectable) + let observable = try _parent._selector(connectable) let subscription = observable.subscribeSafe(self) let connection = connectable.connect() @@ -53,14 +53,14 @@ class Multicast: Producer { typealias SubjectSelectorType = () throws -> S typealias SelectorType = (Observable) throws -> Observable - let source: Observable - let subjectSelector: SubjectSelectorType - let selector: SelectorType + private let _source: Observable + private let _subjectSelector: SubjectSelectorType + private let _selector: SelectorType init(source: Observable, subjectSelector: SubjectSelectorType, selector: SelectorType) { - self.source = source - self.subjectSelector = subjectSelector - self.selector = selector + _source = source + _subjectSelector = subjectSelector + _selector = selector } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { diff --git a/RxSwift/Observables/Implementations/Range.swift b/RxSwift/Observables/Implementations/Range.swift index 5ef7c934..2707eaf8 100644 --- a/RxSwift/Observables/Implementations/Range.swift +++ b/RxSwift/Observables/Implementations/Range.swift @@ -9,14 +9,14 @@ import Foundation class RangeProducer<_CompilerWorkaround> : Producer { - let start: Int - let count: Int - let scheduler: ImmediateSchedulerType + private let _start: Int + private let _count: Int + private let _scheduler: ImmediateSchedulerType init(start: Int, count: Int, scheduler: ImmediateSchedulerType) { - self.start = start - self.count = count - self.scheduler = scheduler + _start = start + _count = count + _scheduler = scheduler } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { @@ -29,17 +29,17 @@ class RangeProducer<_CompilerWorkaround> : Producer { class RangeSink<_CompilerWorkaround, O: ObserverType where O.E == Int> : Sink { typealias Parent = RangeProducer<_CompilerWorkaround> - let parent: Parent + private let _parent: Parent init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { - return self.parent.scheduler.scheduleRecursive(0) { i, recurse in - if i < self.parent.count { - self.observer?.on(.Next(self.parent.start + i)) + return _parent._scheduler.scheduleRecursive(0) { i, recurse in + if i < self._parent._count { + self.observer?.on(.Next(self._parent._start + i)) recurse(i + 1) } else { diff --git a/RxSwift/Observables/Implementations/Reduce.swift b/RxSwift/Observables/Implementations/Reduce.swift index 052ca074..552fb3ae 100644 --- a/RxSwift/Observables/Implementations/Reduce.swift +++ b/RxSwift/Observables/Implementations/Reduce.swift @@ -12,13 +12,12 @@ class ReduceSink : Sink, Observe typealias ResultType = O.E typealias Parent = Reduce - let parent: Parent - var accumulation: AccumulateType + private let _parent: Parent + private var _accumulation: AccumulateType init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent - - self.accumulation = parent.seed + _parent = parent + _accumulation = parent._seed super.init(observer: observer, cancel: cancel) } @@ -27,25 +26,25 @@ class ReduceSink : Sink, Observe switch event { case .Next(let value): do { - self.accumulation = try parent.accumulator(accumulation, value) + _accumulation = try _parent._accumulator(_accumulation, value) } catch let e { observer?.on(.Error(e)) - self.dispose() + dispose() } case .Error(let e): observer?.on(.Error(e)) - self.dispose() + dispose() case .Completed: do { - let result = try parent.mapResult(self.accumulation) + let result = try _parent._mapResult(_accumulation) observer?.on(.Next(result)) observer?.on(.Completed) - self.dispose() + dispose() } catch let e { observer?.on(.Error(e)) - self.dispose() + dispose() } } } @@ -55,21 +54,21 @@ class Reduce : Producer { typealias AccumulatorType = (AccumulateType, SourceType) throws -> AccumulateType typealias ResultSelectorType = (AccumulateType) throws -> ResultType - let source: Observable - let seed: AccumulateType - let accumulator: AccumulatorType - let mapResult: ResultSelectorType + private let _source: Observable + private let _seed: AccumulateType + private let _accumulator: AccumulatorType + private let _mapResult: ResultSelectorType init(source: Observable, seed: AccumulateType, accumulator: AccumulatorType, mapResult: ResultSelectorType) { - self.source = source - self.seed = seed - self.accumulator = accumulator - self.mapResult = mapResult + _source = source + _seed = seed + _accumulator = accumulator + _mapResult = mapResult } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { let sink = ReduceSink(parent: self, observer: observer, cancel: cancel) setSink(sink) - return source.subscribeSafe(sink) + return _source.subscribeSafe(sink) } } \ No newline at end of file From 6dc7f2e4a60d6d3f45be0a33c89518a8202fecd7 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sat, 17 Oct 2015 12:15:37 +0200 Subject: [PATCH 044/210] Rename `ObserverOf` to `AnyObserver`. --- Rx.xcodeproj/project.pbxproj | 46 +++++++++---------- .../Common/CocoaUnits/ControlProperty.swift | 4 +- RxCocoa/OSX/NSControl+Rx.swift | 2 +- RxCocoa/OSX/NSImageView+Rx.swift | 6 +-- RxCocoa/OSX/NSTextField+Rx.swift | 2 +- RxCocoa/iOS/UIBarButtonItem+Rx.swift | 4 +- RxCocoa/iOS/UIControl+Rx.swift | 6 +-- RxCocoa/iOS/UIImageView+Rx.swift | 6 +-- RxCocoa/iOS/UILabel+Rx.swift | 4 +- RxCocoa/iOS/UIScrollView+Rx.swift | 2 +- RxCocoa/iOS/UISearchBar+Rx.swift | 2 +- RxCocoa/iOS/UITextView+Rx.swift | 2 +- .../RxCollectionViewSectionedDataSource.swift | 2 +- .../RxTableViewSectionedDataSource.swift | 2 +- RxExample/RxExample.xcodeproj/project.pbxproj | 28 +++++------ .../{ObserverOf.swift => AnyObserver.swift} | 8 ++-- .../Implementations/AnonymousObservable.swift | 4 +- .../CombineLatest+CollectionType.swift | 2 +- .../Implementations/ShareReplay1.swift | 4 +- .../Implementations/Zip+CollectionType.swift | 2 +- RxSwift/Observables/Observable+Creation.swift | 2 +- RxSwift/Subjects/BehaviorSubject.swift | 4 +- RxSwift/Subjects/PublishSubject.swift | 6 +-- RxSwift/Subjects/ReplaySubject.swift | 16 +++---- .../Mocks/ColdObservable.swift | 4 +- .../Mocks/HotObservable.swift | 6 +-- .../TestImplementations/Mocks/MySubject.swift | 6 +-- .../Mocks/PrimitiveHotObservable.swift | 6 +-- .../Tests/AnonymousObservable+Test.swift | 6 +-- .../RxSwiftTests/Tests/ObserverTests.swift | 6 +-- 30 files changed, 100 insertions(+), 100 deletions(-) rename RxSwift/{ObserverOf.swift => AnyObserver.swift} (90%) diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index 65e285d5..105b10c9 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -149,8 +149,8 @@ C8093D641B8A72BE0088E94D /* Observable+Time.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C9D1B8A72BE0088E94D /* Observable+Time.swift */; }; C8093D651B8A72BE0088E94D /* ObservableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C9E1B8A72BE0088E94D /* ObservableType.swift */; }; C8093D661B8A72BE0088E94D /* ObservableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C9E1B8A72BE0088E94D /* ObservableType.swift */; }; - C8093D691B8A72BE0088E94D /* ObserverOf.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CA01B8A72BE0088E94D /* ObserverOf.swift */; }; - C8093D6A1B8A72BE0088E94D /* ObserverOf.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CA01B8A72BE0088E94D /* ObserverOf.swift */; }; + C8093D691B8A72BE0088E94D /* AnyObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CA01B8A72BE0088E94D /* AnyObserver.swift */; }; + C8093D6A1B8A72BE0088E94D /* AnyObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CA01B8A72BE0088E94D /* AnyObserver.swift */; }; C8093D6B1B8A72BE0088E94D /* AnonymousObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CA21B8A72BE0088E94D /* AnonymousObserver.swift */; }; C8093D6C1B8A72BE0088E94D /* AnonymousObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CA21B8A72BE0088E94D /* AnonymousObserver.swift */; }; C8093D731B8A72BE0088E94D /* ObserverBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CA61B8A72BE0088E94D /* ObserverBase.swift */; }; @@ -356,7 +356,7 @@ C8F0BF971BBBFB8B001B112F /* SingleAssignmentDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C601B8A72BE0088E94D /* SingleAssignmentDisposable.swift */; }; C8F0BF981BBBFB8B001B112F /* FailWith.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C3DA081B93941E004D233E /* FailWith.swift */; }; C8F0BF991BBBFB8B001B112F /* SchedulerServices+Emulation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CBB1B8A72BE0088E94D /* SchedulerServices+Emulation.swift */; }; - C8F0BF9A1BBBFB8B001B112F /* ObserverOf.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CA01B8A72BE0088E94D /* ObserverOf.swift */; }; + C8F0BF9A1BBBFB8B001B112F /* AnyObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CA01B8A72BE0088E94D /* AnyObserver.swift */; }; C8F0BF9B1BBBFB8B001B112F /* Skip.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C891B8A72BE0088E94D /* Skip.swift */; }; C8F0BF9C1BBBFB8B001B112F /* StableCompositeDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C611B8A72BE0088E94D /* StableCompositeDisposable.swift */; }; C8F0BF9D1BBBFB8B001B112F /* Zip+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C921B8A72BE0088E94D /* Zip+arity.swift */; }; @@ -590,7 +590,7 @@ D2EBEAE21BB9B697003A27DC /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C681B8A72BE0088E94D /* Observable.swift */; }; D2EBEAE31BB9B697003A27DC /* Observable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C671B8A72BE0088E94D /* Observable+Extensions.swift */; }; D2EBEAE41BB9B697003A27DC /* ObservableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C9E1B8A72BE0088E94D /* ObservableType.swift */; }; - D2EBEAE51BB9B697003A27DC /* ObserverOf.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CA01B8A72BE0088E94D /* ObserverOf.swift */; }; + D2EBEAE51BB9B697003A27DC /* AnyObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CA01B8A72BE0088E94D /* AnyObserver.swift */; }; D2EBEAE61BB9B697003A27DC /* ObserverType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CAB1B8A72BE0088E94D /* ObserverType.swift */; }; D2EBEAE71BB9B697003A27DC /* ObserverType+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CAA1B8A72BE0088E94D /* ObserverType+Extensions.swift */; }; D2EBEAE81BB9B697003A27DC /* Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CAF1B8A72BE0088E94D /* Rx.swift */; }; @@ -826,14 +826,14 @@ C8093C951B8A72BE0088E94D /* Observable+Aggregate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Aggregate.swift"; sourceTree = ""; }; C8093C961B8A72BE0088E94D /* Observable+Binding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Binding.swift"; sourceTree = ""; }; C8093C971B8A72BE0088E94D /* Observable+Concurrency.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Concurrency.swift"; sourceTree = ""; }; - C8093C981B8A72BE0088E94D /* Observable+Creation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Creation.swift"; sourceTree = ""; }; + C8093C981B8A72BE0088E94D /* Observable+Creation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "Observable+Creation.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C8093C991B8A72BE0088E94D /* Observable+Debug.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Debug.swift"; sourceTree = ""; }; C8093C9A1B8A72BE0088E94D /* Observable+Multiple.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Multiple.swift"; sourceTree = ""; }; C8093C9B1B8A72BE0088E94D /* Observable+Single.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Single.swift"; sourceTree = ""; }; C8093C9C1B8A72BE0088E94D /* Observable+StandardSequenceOperators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+StandardSequenceOperators.swift"; sourceTree = ""; }; C8093C9D1B8A72BE0088E94D /* Observable+Time.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Time.swift"; sourceTree = ""; }; C8093C9E1B8A72BE0088E94D /* ObservableType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableType.swift; sourceTree = ""; }; - C8093CA01B8A72BE0088E94D /* ObserverOf.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserverOf.swift; sourceTree = ""; }; + C8093CA01B8A72BE0088E94D /* AnyObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AnyObserver.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C8093CA21B8A72BE0088E94D /* AnonymousObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnonymousObserver.swift; sourceTree = ""; }; C8093CA61B8A72BE0088E94D /* ObserverBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserverBase.swift; sourceTree = ""; }; C8093CA91B8A72BE0088E94D /* TailRecursiveSink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TailRecursiveSink.swift; sourceTree = ""; }; @@ -849,9 +849,9 @@ C8093CB91B8A72BE0088E94D /* RecursiveScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecursiveScheduler.swift; sourceTree = ""; }; C8093CBB1B8A72BE0088E94D /* SchedulerServices+Emulation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SchedulerServices+Emulation.swift"; sourceTree = ""; }; C8093CBC1B8A72BE0088E94D /* SerialDispatchQueueScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SerialDispatchQueueScheduler.swift; sourceTree = ""; }; - C8093CBE1B8A72BE0088E94D /* BehaviorSubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BehaviorSubject.swift; sourceTree = ""; }; - C8093CBF1B8A72BE0088E94D /* PublishSubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PublishSubject.swift; sourceTree = ""; }; - C8093CC01B8A72BE0088E94D /* ReplaySubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReplaySubject.swift; sourceTree = ""; }; + C8093CBE1B8A72BE0088E94D /* BehaviorSubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = BehaviorSubject.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + C8093CBF1B8A72BE0088E94D /* PublishSubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PublishSubject.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + C8093CC01B8A72BE0088E94D /* ReplaySubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ReplaySubject.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C8093CC11B8A72BE0088E94D /* SubjectType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubjectType.swift; sourceTree = ""; }; C8093CC21B8A72BE0088E94D /* Variable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Variable.swift; sourceTree = ""; }; C8093E821B8A732E0088E94D /* _RX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _RX.h; sourceTree = ""; }; @@ -880,17 +880,17 @@ C8093E9C1B8A732E0088E94D /* RxTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTarget.swift; sourceTree = ""; }; C8093E9D1B8A732E0088E94D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C8093EC61B8A732E0088E94D /* NSButton+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSButton+Rx.swift"; sourceTree = ""; }; - C8093EC71B8A732E0088E94D /* NSControl+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSControl+Rx.swift"; sourceTree = ""; }; - C8093EC81B8A732E0088E94D /* NSImageView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSImageView+Rx.swift"; sourceTree = ""; }; + C8093EC71B8A732E0088E94D /* NSControl+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "NSControl+Rx.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + C8093EC81B8A732E0088E94D /* NSImageView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "NSImageView+Rx.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C8093EC91B8A732E0088E94D /* NSSlider+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSSlider+Rx.swift"; sourceTree = ""; }; - C8093ECA1B8A732E0088E94D /* NSTextField+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSTextField+Rx.swift"; sourceTree = ""; }; + C8093ECA1B8A732E0088E94D /* NSTextField+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "NSTextField+Rx.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C8093ECB1B8A732E0088E94D /* RxCocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RxCocoa.h; sourceTree = ""; }; C8093F581B8A73A20088E94D /* Observable+Blocking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Blocking.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C8093F591B8A73A20088E94D /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; C80D338E1B91EF9E0014629D /* Observable+Bind.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Bind.swift"; sourceTree = ""; }; C80D33931B922FB00014629D /* ControlEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlEvent.swift; sourceTree = ""; }; - C80D33941B922FB00014629D /* ControlProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlProperty.swift; sourceTree = ""; }; - C80D342D1B9245A40014629D /* CombineLatest+CollectionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CombineLatest+CollectionType.swift"; sourceTree = ""; }; + C80D33941B922FB00014629D /* ControlProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ControlProperty.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + C80D342D1B9245A40014629D /* CombineLatest+CollectionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "CombineLatest+CollectionType.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C80DDE8D1BCE69BA006A1832 /* ControlEvent+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControlEvent+Driver.swift"; sourceTree = ""; }; C80DDE8E1BCE69BA006A1832 /* ControlProperty+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControlProperty+Driver.swift"; sourceTree = ""; }; C80DDE8F1BCE69BA006A1832 /* Driver+Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Driver+Operators.swift"; sourceTree = ""; }; @@ -921,10 +921,10 @@ C88254021B8A752B00B02D69 /* RxTextViewDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTextViewDelegateProxy.swift; sourceTree = ""; }; C88254031B8A752B00B02D69 /* UIActionSheet+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIActionSheet+Rx.swift"; sourceTree = ""; }; C88254041B8A752B00B02D69 /* UIAlertView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIAlertView+Rx.swift"; sourceTree = ""; }; - C88254051B8A752B00B02D69 /* UIBarButtonItem+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIBarButtonItem+Rx.swift"; sourceTree = ""; }; + C88254051B8A752B00B02D69 /* UIBarButtonItem+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "UIBarButtonItem+Rx.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C88254061B8A752B00B02D69 /* UIButton+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIButton+Rx.swift"; sourceTree = ""; }; C88254071B8A752B00B02D69 /* UICollectionView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UICollectionView+Rx.swift"; sourceTree = ""; }; - C88254081B8A752B00B02D69 /* UIControl+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIControl+Rx.swift"; sourceTree = ""; }; + C88254081B8A752B00B02D69 /* UIControl+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "UIControl+Rx.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C88254091B8A752B00B02D69 /* UIDatePicker+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIDatePicker+Rx.swift"; sourceTree = ""; }; C882540A1B8A752B00B02D69 /* UIGestureRecognizer+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Rx.swift"; sourceTree = ""; }; C882540B1B8A752B00B02D69 /* UIImageView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImageView+Rx.swift"; sourceTree = ""; }; @@ -938,7 +938,7 @@ C88254131B8A752B00B02D69 /* UITextField+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextField+Rx.swift"; sourceTree = ""; }; C88254141B8A752B00B02D69 /* UITextView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextView+Rx.swift"; sourceTree = ""; }; C88BB8711B07E5ED0064D411 /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - C89CDB351BCB0DD7002063D9 /* ShareReplay1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareReplay1.swift; sourceTree = ""; }; + C89CDB351BCB0DD7002063D9 /* ShareReplay1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ShareReplay1.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C8A56AD71AD7424700B4673B /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8C3D9FD1B935EDF004D233E /* Zip+CollectionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Zip+CollectionType.swift"; sourceTree = ""; }; C8C3DA021B9390C4004D233E /* Just.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Just.swift; sourceTree = ""; }; @@ -946,7 +946,7 @@ C8C3DA081B93941E004D233E /* FailWith.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FailWith.swift; sourceTree = ""; }; C8C3DA0B1B93959F004D233E /* Never.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Never.swift; sourceTree = ""; }; C8C3DA0E1B939767004D233E /* CurrentThreadScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurrentThreadScheduler.swift; sourceTree = ""; }; - C8C3DA111B93A3EA004D233E /* AnonymousObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnonymousObservable.swift; sourceTree = ""; }; + C8C3DA111B93A3EA004D233E /* AnonymousObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AnonymousObservable.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C8F0C0021BBBFB8B001B112F /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8F0C04B1BBBFBB9001B112F /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8F0C0581BBBFBCE001B112F /* RxBlocking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxBlocking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1060,7 +1060,7 @@ C8093C671B8A72BE0088E94D /* Observable+Extensions.swift */, C849BE2A1BAB5D070019AD27 /* ObservableConvertibleType.swift */, C8093C9E1B8A72BE0088E94D /* ObservableType.swift */, - C8093CA01B8A72BE0088E94D /* ObserverOf.swift */, + C8093CA01B8A72BE0088E94D /* AnyObserver.swift */, C8093CAB1B8A72BE0088E94D /* ObserverType.swift */, C8093CAA1B8A72BE0088E94D /* ObserverType+Extensions.swift */, C8093CAF1B8A72BE0088E94D /* Rx.swift */, @@ -2062,7 +2062,7 @@ C849BE2C1BAB5D070019AD27 /* ObservableConvertibleType.swift in Sources */, C8C3DA0A1B93941E004D233E /* FailWith.swift in Sources */, C8093D9C1B8A72BE0088E94D /* SchedulerServices+Emulation.swift in Sources */, - C8093D6A1B8A72BE0088E94D /* ObserverOf.swift in Sources */, + C8093D6A1B8A72BE0088E94D /* AnyObserver.swift in Sources */, C8093D3C1B8A72BE0088E94D /* Skip.swift in Sources */, C8093CF01B8A72BE0088E94D /* StableCompositeDisposable.swift in Sources */, C8093D4E1B8A72BE0088E94D /* Zip+arity.swift in Sources */, @@ -2178,7 +2178,7 @@ C849BE2B1BAB5D070019AD27 /* ObservableConvertibleType.swift in Sources */, C8C3DA091B93941E004D233E /* FailWith.swift in Sources */, C8093D9B1B8A72BE0088E94D /* SchedulerServices+Emulation.swift in Sources */, - C8093D691B8A72BE0088E94D /* ObserverOf.swift in Sources */, + C8093D691B8A72BE0088E94D /* AnyObserver.swift in Sources */, C8093D3B1B8A72BE0088E94D /* Skip.swift in Sources */, C8093CEF1B8A72BE0088E94D /* StableCompositeDisposable.swift in Sources */, C8093D4D1B8A72BE0088E94D /* Zip+arity.swift in Sources */, @@ -2294,7 +2294,7 @@ C89461751BC6C1210055219D /* ObservableConvertibleType.swift in Sources */, C8F0BF981BBBFB8B001B112F /* FailWith.swift in Sources */, C8F0BF991BBBFB8B001B112F /* SchedulerServices+Emulation.swift in Sources */, - C8F0BF9A1BBBFB8B001B112F /* ObserverOf.swift in Sources */, + C8F0BF9A1BBBFB8B001B112F /* AnyObserver.swift in Sources */, C8F0BF9B1BBBFB8B001B112F /* Skip.swift in Sources */, C8F0BF9C1BBBFB8B001B112F /* StableCompositeDisposable.swift in Sources */, C8F0BF9D1BBBFB8B001B112F /* Zip+arity.swift in Sources */, @@ -2636,7 +2636,7 @@ D2EBEB291BB9B6C1003A27DC /* Zip+arity.swift in Sources */, D2EBEB241BB9B6C1003A27DC /* TakeUntil.swift in Sources */, D2EBEB3B1BB9B6D8003A27DC /* OperationQueueScheduler.swift in Sources */, - D2EBEAE51BB9B697003A27DC /* ObserverOf.swift in Sources */, + D2EBEAE51BB9B697003A27DC /* AnyObserver.swift in Sources */, D2EBEB3D1BB9B6D8003A27DC /* SchedulerServices+Emulation.swift in Sources */, D2EBEB1C1BB9B6C1003A27DC /* Sample.swift in Sources */, D2EBEAFD1BB9B6BA003A27DC /* AnonymousObservable.swift in Sources */, diff --git a/RxCocoa/Common/CocoaUnits/ControlProperty.swift b/RxCocoa/Common/CocoaUnits/ControlProperty.swift index 4970fb14..ebaa59c2 100644 --- a/RxCocoa/Common/CocoaUnits/ControlProperty.swift +++ b/RxCocoa/Common/CocoaUnits/ControlProperty.swift @@ -38,9 +38,9 @@ public struct ControlProperty : ControlPropertyType { public typealias E = PropertyType let source: Observable - let observer: ObserverOf + let observer: AnyObserver - init(source: Observable, observer: ObserverOf) { + init(source: Observable, observer: AnyObserver) { self.source = source.subscribeOn(MainScheduler.sharedInstance) self.observer = observer } diff --git a/RxCocoa/OSX/NSControl+Rx.swift b/RxCocoa/OSX/NSControl+Rx.swift index f4b611ea..6a267fb6 100644 --- a/RxCocoa/OSX/NSControl+Rx.swift +++ b/RxCocoa/OSX/NSControl+Rx.swift @@ -42,7 +42,7 @@ extension NSControl { return observer }.takeUntil(rx_deallocated) - return ControlProperty(source: source, observer: ObserverOf { event in + return ControlProperty(source: source, observer: AnyObserver { event in switch event { case .Next(let value): setter(value) diff --git a/RxCocoa/OSX/NSImageView+Rx.swift b/RxCocoa/OSX/NSImageView+Rx.swift index 6b5ab622..1d1928f6 100644 --- a/RxCocoa/OSX/NSImageView+Rx.swift +++ b/RxCocoa/OSX/NSImageView+Rx.swift @@ -17,7 +17,7 @@ extension NSImageView { /** Bindable sink for `image` property. */ - public var rx_image: ObserverOf { + public var rx_image: AnyObserver { return self.rx_imageAnimated(nil) } @@ -26,8 +26,8 @@ extension NSImageView { - parameter transitionType: Optional transition type while setting the image (kCATransitionFade, kCATransitionMoveIn, ...) */ - public func rx_imageAnimated(transitionType: String?) -> ObserverOf { - return ObserverOf { [weak self] event in + public func rx_imageAnimated(transitionType: String?) -> AnyObserver { + return AnyObserver { [weak self] event in MainScheduler.ensureExecutingOnScheduler() switch event { diff --git a/RxCocoa/OSX/NSTextField+Rx.swift b/RxCocoa/OSX/NSTextField+Rx.swift index 120ffd3f..99dc0d1d 100644 --- a/RxCocoa/OSX/NSTextField+Rx.swift +++ b/RxCocoa/OSX/NSTextField+Rx.swift @@ -61,7 +61,7 @@ extension NSTextField { let source = delegate.textSubject - return ControlProperty(source: source, observer: ObserverOf { [weak self] event in + return ControlProperty(source: source, observer: AnyObserver { [weak self] event in MainScheduler.ensureExecutingOnScheduler() switch event { diff --git a/RxCocoa/iOS/UIBarButtonItem+Rx.swift b/RxCocoa/iOS/UIBarButtonItem+Rx.swift index ce8a4f01..c31bba0c 100644 --- a/RxCocoa/iOS/UIBarButtonItem+Rx.swift +++ b/RxCocoa/iOS/UIBarButtonItem+Rx.swift @@ -18,8 +18,8 @@ extension UIBarButtonItem { /** Bindable sink for `enabled` property. */ - public var rx_enabled: ObserverOf { - return ObserverOf { [weak self] event in + public var rx_enabled: AnyObserver { + return AnyObserver { [weak self] event in MainScheduler.ensureExecutingOnScheduler() switch event { diff --git a/RxCocoa/iOS/UIControl+Rx.swift b/RxCocoa/iOS/UIControl+Rx.swift index 854fb7d2..63cb2245 100644 --- a/RxCocoa/iOS/UIControl+Rx.swift +++ b/RxCocoa/iOS/UIControl+Rx.swift @@ -19,8 +19,8 @@ extension UIControl { /** Bindable sink for `enabled` property. */ - public var rx_enabled: ObserverOf { - return ObserverOf { [weak self] event in + public var rx_enabled: AnyObserver { + return AnyObserver { [weak self] event in MainScheduler.ensureExecutingOnScheduler() switch event { @@ -71,7 +71,7 @@ extension UIControl { } }.takeUntil(rx_deallocated) - return ControlProperty(source: source, observer: ObserverOf { event in + return ControlProperty(source: source, observer: AnyObserver { event in MainScheduler.ensureExecutingOnScheduler() switch event { diff --git a/RxCocoa/iOS/UIImageView+Rx.swift b/RxCocoa/iOS/UIImageView+Rx.swift index 7c112c24..a3e62a36 100644 --- a/RxCocoa/iOS/UIImageView+Rx.swift +++ b/RxCocoa/iOS/UIImageView+Rx.swift @@ -19,7 +19,7 @@ extension UIImageView { /** Bindable sink for `image` property. */ - public var rx_image: ObserverOf { + public var rx_image: AnyObserver { return self.rx_imageAnimated(nil) } @@ -28,8 +28,8 @@ extension UIImageView { - parameter transitionType: Optional transition type while setting the image (kCATransitionFade, kCATransitionMoveIn, ...) */ - public func rx_imageAnimated(transitionType: String?) -> ObserverOf { - return ObserverOf { [weak self] event in + public func rx_imageAnimated(transitionType: String?) -> AnyObserver { + return AnyObserver { [weak self] event in MainScheduler.ensureExecutingOnScheduler() switch event { diff --git a/RxCocoa/iOS/UILabel+Rx.swift b/RxCocoa/iOS/UILabel+Rx.swift index e11459a3..ea82bef9 100644 --- a/RxCocoa/iOS/UILabel+Rx.swift +++ b/RxCocoa/iOS/UILabel+Rx.swift @@ -19,8 +19,8 @@ extension UILabel { /** Bindable sink for `text` property. */ - public var rx_text: ObserverOf { - return ObserverOf { [weak self] event in + public var rx_text: AnyObserver { + return AnyObserver { [weak self] event in MainScheduler.ensureExecutingOnScheduler() switch event { diff --git a/RxCocoa/iOS/UIScrollView+Rx.swift b/RxCocoa/iOS/UIScrollView+Rx.swift index d2311197..dbdbae92 100644 --- a/RxCocoa/iOS/UIScrollView+Rx.swift +++ b/RxCocoa/iOS/UIScrollView+Rx.swift @@ -40,7 +40,7 @@ extension UIScrollView { public var rx_contentOffset: ControlProperty { let proxy = proxyForObject(self) as RxScrollViewDelegateProxy - return ControlProperty(source: proxy.contentOffsetSubject, observer: ObserverOf { [weak self] event in + return ControlProperty(source: proxy.contentOffsetSubject, observer: AnyObserver { [weak self] event in switch event { case .Next(let value): self?.contentOffset = value diff --git a/RxCocoa/iOS/UISearchBar+Rx.swift b/RxCocoa/iOS/UISearchBar+Rx.swift index 28d3c70e..e71a2027 100644 --- a/RxCocoa/iOS/UISearchBar+Rx.swift +++ b/RxCocoa/iOS/UISearchBar+Rx.swift @@ -41,7 +41,7 @@ extension UISearchBar { .startWith(text) } - return ControlProperty(source: source, observer: ObserverOf { [weak self] event in + return ControlProperty(source: source, observer: AnyObserver { [weak self] event in switch event { case .Next(let value): self?.text = value diff --git a/RxCocoa/iOS/UITextView+Rx.swift b/RxCocoa/iOS/UITextView+Rx.swift index ffb43f5a..1fa78789 100644 --- a/RxCocoa/iOS/UITextView+Rx.swift +++ b/RxCocoa/iOS/UITextView+Rx.swift @@ -38,7 +38,7 @@ extension UITextView { .startWith(text) } - return ControlProperty(source: source, observer: ObserverOf { [weak self] event in + return ControlProperty(source: source, observer: AnyObserver { [weak self] event in switch event { case .Next(let value): self?.text = value diff --git a/RxDataSourceStarterKit/DataSources/RxCollectionViewSectionedDataSource.swift b/RxDataSourceStarterKit/DataSources/RxCollectionViewSectionedDataSource.swift index 9decd80d..bef2d9b1 100644 --- a/RxDataSourceStarterKit/DataSources/RxCollectionViewSectionedDataSource.swift +++ b/RxDataSourceStarterKit/DataSources/RxCollectionViewSectionedDataSource.swift @@ -55,7 +55,7 @@ public class RxCollectionViewSectionedDataSource : _RxColle public typealias CellFactory = (UICollectionView, NSIndexPath, I) -> UICollectionViewCell public typealias SupplementaryViewFactory = (UICollectionView, String, NSIndexPath) -> UICollectionReusableView - public typealias IncrementalUpdateObserver = ObserverOf> + public typealias IncrementalUpdateObserver = AnyObserver> public typealias IncrementalUpdateDisposeKey = Bag.KeyType diff --git a/RxDataSourceStarterKit/DataSources/RxTableViewSectionedDataSource.swift b/RxDataSourceStarterKit/DataSources/RxTableViewSectionedDataSource.swift index 3422221f..0dbbe547 100644 --- a/RxDataSourceStarterKit/DataSources/RxTableViewSectionedDataSource.swift +++ b/RxDataSourceStarterKit/DataSources/RxTableViewSectionedDataSource.swift @@ -64,7 +64,7 @@ public class RxTableViewSectionedDataSource : _RxTableViewS public typealias Section = S public typealias CellFactory = (UITableView, NSIndexPath, I) -> UITableViewCell - public typealias IncrementalUpdateObserver = ObserverOf> + public typealias IncrementalUpdateObserver = AnyObserver> public typealias IncrementalUpdateDisposeKey = Bag.KeyType diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index 6b759a72..2a4ce783 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -196,7 +196,7 @@ C89464F21BC6C2B00055219D /* Observable+StandardSequenceOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464811BC6C2B00055219D /* Observable+StandardSequenceOperators.swift */; }; C89464F31BC6C2B00055219D /* Observable+Time.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464821BC6C2B00055219D /* Observable+Time.swift */; }; C89464F41BC6C2B00055219D /* ObservableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464831BC6C2B00055219D /* ObservableType.swift */; }; - C89464F51BC6C2B00055219D /* ObserverOf.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464841BC6C2B00055219D /* ObserverOf.swift */; }; + C89464F51BC6C2B00055219D /* AnyObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464841BC6C2B00055219D /* AnyObserver.swift */; }; C89464F61BC6C2B00055219D /* AnonymousObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464861BC6C2B00055219D /* AnonymousObserver.swift */; }; C89464F71BC6C2B00055219D /* ObserverBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464871BC6C2B00055219D /* ObserverBase.swift */; }; C89464F81BC6C2B00055219D /* TailRecursiveSink.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464881BC6C2B00055219D /* TailRecursiveSink.swift */; }; @@ -509,13 +509,13 @@ C89464451BC6C2B00055219D /* Observable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = ""; }; C89464461BC6C2B00055219D /* ObservableConvertibleType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableConvertibleType.swift; sourceTree = ""; }; C89464491BC6C2B00055219D /* Amb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Amb.swift; sourceTree = ""; }; - C894644A1BC6C2B00055219D /* AnonymousObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnonymousObservable.swift; sourceTree = ""; }; + C894644A1BC6C2B00055219D /* AnonymousObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AnonymousObservable.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C894644B1BC6C2B00055219D /* AsObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsObservable.swift; sourceTree = ""; }; C894644C1BC6C2B00055219D /* Buffer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Buffer.swift; sourceTree = ""; }; C894644D1BC6C2B00055219D /* Catch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Catch.swift; sourceTree = ""; }; C894644E1BC6C2B00055219D /* CombineLatest+arity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CombineLatest+arity.swift"; sourceTree = ""; }; C894644F1BC6C2B00055219D /* CombineLatest+arity.tt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "CombineLatest+arity.tt"; sourceTree = ""; }; - C89464501BC6C2B00055219D /* CombineLatest+CollectionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CombineLatest+CollectionType.swift"; sourceTree = ""; }; + C89464501BC6C2B00055219D /* CombineLatest+CollectionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "CombineLatest+CollectionType.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C89464511BC6C2B00055219D /* CombineLatest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombineLatest.swift; sourceTree = ""; }; C89464521BC6C2B00055219D /* Concat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Concat.swift; sourceTree = ""; }; C89464531BC6C2B00055219D /* ConnectableObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectableObservable.swift; sourceTree = ""; }; @@ -560,14 +560,14 @@ C894647A1BC6C2B00055219D /* Observable+Aggregate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Aggregate.swift"; sourceTree = ""; }; C894647B1BC6C2B00055219D /* Observable+Binding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Binding.swift"; sourceTree = ""; }; C894647C1BC6C2B00055219D /* Observable+Concurrency.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Concurrency.swift"; sourceTree = ""; }; - C894647D1BC6C2B00055219D /* Observable+Creation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Creation.swift"; sourceTree = ""; }; + C894647D1BC6C2B00055219D /* Observable+Creation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "Observable+Creation.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C894647E1BC6C2B00055219D /* Observable+Debug.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Debug.swift"; sourceTree = ""; }; C894647F1BC6C2B00055219D /* Observable+Multiple.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Multiple.swift"; sourceTree = ""; }; C89464801BC6C2B00055219D /* Observable+Single.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Single.swift"; sourceTree = ""; }; C89464811BC6C2B00055219D /* Observable+StandardSequenceOperators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+StandardSequenceOperators.swift"; sourceTree = ""; }; C89464821BC6C2B00055219D /* Observable+Time.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Time.swift"; sourceTree = ""; }; C89464831BC6C2B00055219D /* ObservableType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableType.swift; sourceTree = ""; }; - C89464841BC6C2B00055219D /* ObserverOf.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserverOf.swift; sourceTree = ""; }; + C89464841BC6C2B00055219D /* AnyObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AnyObserver.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C89464861BC6C2B00055219D /* AnonymousObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnonymousObserver.swift; sourceTree = ""; }; C89464871BC6C2B00055219D /* ObserverBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserverBase.swift; sourceTree = ""; }; C89464881BC6C2B00055219D /* TailRecursiveSink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TailRecursiveSink.swift; sourceTree = ""; }; @@ -585,9 +585,9 @@ C89464951BC6C2B00055219D /* SchedulerServices+Emulation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SchedulerServices+Emulation.swift"; sourceTree = ""; }; C89464961BC6C2B00055219D /* SerialDispatchQueueScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SerialDispatchQueueScheduler.swift; sourceTree = ""; }; C89464971BC6C2B00055219D /* SchedulerType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchedulerType.swift; sourceTree = ""; }; - C89464991BC6C2B00055219D /* BehaviorSubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BehaviorSubject.swift; sourceTree = ""; }; - C894649A1BC6C2B00055219D /* PublishSubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PublishSubject.swift; sourceTree = ""; }; - C894649B1BC6C2B00055219D /* ReplaySubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReplaySubject.swift; sourceTree = ""; }; + C89464991BC6C2B00055219D /* BehaviorSubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = BehaviorSubject.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + C894649A1BC6C2B00055219D /* PublishSubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PublishSubject.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + C894649B1BC6C2B00055219D /* ReplaySubject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ReplaySubject.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C894649C1BC6C2B00055219D /* SubjectType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubjectType.swift; sourceTree = ""; }; C894649D1BC6C2B00055219D /* Variable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Variable.swift; sourceTree = ""; }; C894650E1BC6C2BC0055219D /* _RX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _RX.h; sourceTree = ""; }; @@ -600,7 +600,7 @@ C89465151BC6C2BC0055219D /* _RXSwizzling.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _RXSwizzling.m; sourceTree = ""; }; C89465161BC6C2BC0055219D /* CLLocationManager+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CLLocationManager+Rx.swift"; sourceTree = ""; }; C89465191BC6C2BC0055219D /* ControlEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlEvent.swift; sourceTree = ""; }; - C894651B1BC6C2BC0055219D /* ControlProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlProperty.swift; sourceTree = ""; }; + C894651B1BC6C2BC0055219D /* ControlProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ControlProperty.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C89465201BC6C2BC0055219D /* DelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxy.swift; sourceTree = ""; }; C89465211BC6C2BC0055219D /* DelegateProxyType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateProxyType.swift; sourceTree = ""; }; C89465221BC6C2BC0055219D /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = ""; }; @@ -634,10 +634,10 @@ C89465461BC6C2BC0055219D /* RxTextViewDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTextViewDelegateProxy.swift; sourceTree = ""; }; C89465471BC6C2BC0055219D /* UIActionSheet+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIActionSheet+Rx.swift"; sourceTree = ""; }; C89465481BC6C2BC0055219D /* UIAlertView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIAlertView+Rx.swift"; sourceTree = ""; }; - C89465491BC6C2BC0055219D /* UIBarButtonItem+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIBarButtonItem+Rx.swift"; sourceTree = ""; }; + C89465491BC6C2BC0055219D /* UIBarButtonItem+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "UIBarButtonItem+Rx.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C894654A1BC6C2BC0055219D /* UIButton+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIButton+Rx.swift"; sourceTree = ""; }; C894654B1BC6C2BC0055219D /* UICollectionView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UICollectionView+Rx.swift"; sourceTree = ""; }; - C894654C1BC6C2BC0055219D /* UIControl+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIControl+Rx.swift"; sourceTree = ""; }; + C894654C1BC6C2BC0055219D /* UIControl+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "UIControl+Rx.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C894654D1BC6C2BC0055219D /* UIDatePicker+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIDatePicker+Rx.swift"; sourceTree = ""; }; C894654E1BC6C2BC0055219D /* UIGestureRecognizer+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Rx.swift"; sourceTree = ""; }; C894654F1BC6C2BC0055219D /* UIImageView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImageView+Rx.swift"; sourceTree = ""; }; @@ -652,7 +652,7 @@ C89465581BC6C2BC0055219D /* UITextField+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextField+Rx.swift"; sourceTree = ""; }; C89465591BC6C2BC0055219D /* UITextView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextView+Rx.swift"; sourceTree = ""; }; C89465601BC6C2BC0055219D /* RxCocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RxCocoa.h; sourceTree = ""; }; - C89CDB611BCC45DC002063D9 /* ShareReplay1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareReplay1.swift; sourceTree = ""; }; + C89CDB611BCC45DC002063D9 /* ShareReplay1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ShareReplay1.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C89CDB621BCC45DC002063D9 /* SkipUntil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipUntil.swift; sourceTree = ""; }; C8A2A2C71B4049E300F11F09 /* PseudoRandomGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PseudoRandomGenerator.swift; sourceTree = ""; }; C8A2A2CA1B404A1200F11F09 /* Randomizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Randomizer.swift; sourceTree = ""; }; @@ -995,7 +995,7 @@ C89464461BC6C2B00055219D /* ObservableConvertibleType.swift */, C89464471BC6C2B00055219D /* Observables */, C89464831BC6C2B00055219D /* ObservableType.swift */, - C89464841BC6C2B00055219D /* ObserverOf.swift */, + C89464841BC6C2B00055219D /* AnyObserver.swift */, C89464851BC6C2B00055219D /* Observers */, C89464891BC6C2B00055219D /* ObserverType+Extensions.swift */, C894648A1BC6C2B00055219D /* ObserverType.swift */, @@ -1708,7 +1708,7 @@ C89465611BC6C2BC0055219D /* _RX.m in Sources */, C80DDED41BCE9046006A1832 /* Driver+Operators+arity.swift in Sources */, C89465841BC6C2BC0055219D /* RxActionSheetDelegateProxy.swift in Sources */, - C89464F51BC6C2B00055219D /* ObserverOf.swift in Sources */, + C89464F51BC6C2B00055219D /* AnyObserver.swift in Sources */, C89464D71BC6C2B00055219D /* Range.swift in Sources */, C8297E431B6CF905000589EA /* GitHubAPI.swift in Sources */, C89464EB1BC6C2B00055219D /* Observable+Aggregate.swift in Sources */, diff --git a/RxSwift/ObserverOf.swift b/RxSwift/AnyObserver.swift similarity index 90% rename from RxSwift/ObserverOf.swift rename to RxSwift/AnyObserver.swift index cb8d8c6b..a22101bc 100644 --- a/RxSwift/ObserverOf.swift +++ b/RxSwift/AnyObserver.swift @@ -1,5 +1,5 @@ // -// ObserverOf.swift +// AnyObserver.swift // Rx // // Created by Krunoslav Zaher on 2/28/15. @@ -13,7 +13,7 @@ A type-erased `ObserverType`. Forwards operations to an arbitrary underlying observer with the same `Element` type, hiding the specifics of the underlying observer type. */ -public struct ObserverOf : ObserverType { +public struct AnyObserver : ObserverType { /** The type of elements in sequence that observer can observe. */ @@ -62,7 +62,7 @@ extension ObserverType { - returns: type erased observer. */ - func asObserver() -> ObserverOf { - return ObserverOf(self) + func asObserver() -> AnyObserver { + return AnyObserver(self) } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/AnonymousObservable.swift b/RxSwift/Observables/Implementations/AnonymousObservable.swift index 55f62bdc..48a7fc94 100644 --- a/RxSwift/Observables/Implementations/AnonymousObservable.swift +++ b/RxSwift/Observables/Implementations/AnonymousObservable.swift @@ -36,12 +36,12 @@ class AnonymousObservableSink : Sink, ObserverType { } func run(parent: Parent) -> Disposable { - return parent.subscribeHandler(ObserverOf(self)) + return parent.subscribeHandler(AnyObserver(self)) } } public class AnonymousObservable : Producer { - public typealias SubscribeHandler = (ObserverOf) -> Disposable + public typealias SubscribeHandler = (AnyObserver) -> Disposable public let subscribeHandler: SubscribeHandler diff --git a/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift b/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift index 4da0028b..8f5ad6e8 100644 --- a/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift +++ b/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift @@ -92,7 +92,7 @@ class CombineLatestCollectionTypeSink : Observable, ObserverType { private var _subscription: Disposable? private var _element: Element? private var _stopEvent = nil as Event? - private var _observers = Bag>() + private var _observers = Bag>() init(source: Observable) { self._source = source @@ -36,7 +36,7 @@ class ShareReplay1 : Observable, ObserverType { let initialCount = self._observers.count - let observerKey = self._observers.insert(ObserverOf(observer)) + let observerKey = self._observers.insert(AnyObserver(observer)) if initialCount == 0 { self._subscription = self._source.subscribe(self) diff --git a/RxSwift/Observables/Implementations/Zip+CollectionType.swift b/RxSwift/Observables/Implementations/Zip+CollectionType.swift index b51b0044..15d3160b 100644 --- a/RxSwift/Observables/Implementations/Zip+CollectionType.swift +++ b/RxSwift/Observables/Implementations/Zip+CollectionType.swift @@ -105,7 +105,7 @@ class ZipCollectionTypeSink(subscribe: (ObserverOf) -> Disposable) -> Observable { +public func create(subscribe: (AnyObserver) -> Disposable) -> Observable { return AnonymousObservable(subscribe) } diff --git a/RxSwift/Subjects/BehaviorSubject.swift b/RxSwift/Subjects/BehaviorSubject.swift index 6d6a6970..b9d870c1 100644 --- a/RxSwift/Subjects/BehaviorSubject.swift +++ b/RxSwift/Subjects/BehaviorSubject.swift @@ -10,7 +10,7 @@ import Foundation private class BehaviorSubjectSubscription : Disposable { typealias Parent = BehaviorSubject - typealias DisposeKey = Bag>.KeyType + typealias DisposeKey = Bag>.KeyType let parent: Parent var disposeKey: DisposeKey? @@ -43,7 +43,7 @@ public final class BehaviorSubject : Observable, SubjectType, // state private var _disposed = false private var _value: Element - private var observers = Bag>() + private var observers = Bag>() private var stoppedEvent: Event? /** diff --git a/RxSwift/Subjects/PublishSubject.swift b/RxSwift/Subjects/PublishSubject.swift index 143d1869..c6331039 100644 --- a/RxSwift/Subjects/PublishSubject.swift +++ b/RxSwift/Subjects/PublishSubject.swift @@ -9,7 +9,7 @@ import Foundation class Subscription : Disposable { - typealias KeyType = Bag>.KeyType + typealias KeyType = Bag>.KeyType private var lock = SpinLock() @@ -48,13 +48,13 @@ Each notification is broadcasted to all subscribed observers. public class PublishSubject : Observable, SubjectType, Cancelable, ObserverType { public typealias SubjectObserverType = PublishSubject - typealias DisposeKey = Bag>.KeyType + typealias DisposeKey = Bag>.KeyType private let lock = NSRecursiveLock() // state var _disposed = false - var observers = Bag>() + var observers = Bag>() var stoppedEvent = nil as Event? /** diff --git a/RxSwift/Subjects/ReplaySubject.swift b/RxSwift/Subjects/ReplaySubject.swift index a21ff787..dd487d13 100644 --- a/RxSwift/Subjects/ReplaySubject.swift +++ b/RxSwift/Subjects/ReplaySubject.swift @@ -16,7 +16,7 @@ Each notification is broadcasted to all subscribed and future observers, subject public class ReplaySubject : Observable, SubjectType, ObserverType, Disposable { public typealias SubjectObserverType = ReplaySubject - typealias DisposeKey = Bag>.KeyType + typealias DisposeKey = Bag>.KeyType func unsubscribe(key: DisposeKey) { abstractMethod() @@ -66,7 +66,7 @@ class ReplayBufferBase : ReplaySubject { // state var disposed = false var stoppedEvent = nil as Event? - var observers = Bag>() + var observers = Bag>() override init() { @@ -80,7 +80,7 @@ class ReplayBufferBase : ReplaySubject { abstractMethod() } - func replayBuffer(observer: ObserverOf) { + func replayBuffer(observer: AnyObserver) { abstractMethod() } @@ -116,15 +116,15 @@ class ReplayBufferBase : ReplaySubject { return NopDisposable.instance } - let observerOf = observer.asObserver() + let AnyObserver = observer.asObserver() - replayBuffer(observerOf) + replayBuffer(AnyObserver) if let stoppedEvent = self.stoppedEvent { observer.on(stoppedEvent) return NopDisposable.instance } else { - let key = self.observers.insert(observerOf) + let key = self.observers.insert(AnyObserver) return ReplaySubscription(subject: self, disposeKey: key) } } @@ -170,7 +170,7 @@ class ReplayOne : ReplayBufferBase { self.value = value } - override func replayBuffer(observer: ObserverOf) { + override func replayBuffer(observer: AnyObserver) { if let value = self.value { observer.on(.Next(value)) } @@ -194,7 +194,7 @@ class ReplayManyBase : ReplayBufferBase { queue.enqueue(value) } - override func replayBuffer(observer: ObserverOf) { + override func replayBuffer(observer: AnyObserver) { for item in queue { observer.on(.Next(item)) } diff --git a/RxTests/RxSwiftTests/TestImplementations/Mocks/ColdObservable.swift b/RxTests/RxSwiftTests/TestImplementations/Mocks/ColdObservable.swift index d0be91f7..51888890 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Mocks/ColdObservable.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Mocks/ColdObservable.swift @@ -12,7 +12,7 @@ class ColdObservable : ObservableType, ObservableConvertible typealias E = Element typealias Events = Recorded - typealias Observer = ObserverOf + typealias Observer = AnyObserver let testScheduler: TestScheduler @@ -29,7 +29,7 @@ class ColdObservable : ObservableType, ObservableConvertible } func subscribe(observer: O) -> Disposable { - let key = observers.insert(ObserverOf(observer)) + let key = observers.insert(AnyObserver(observer)) subscriptions.append(Subscription(self.testScheduler.now)) let i = self.subscriptions.count - 1 diff --git a/RxTests/RxSwiftTests/TestImplementations/Mocks/HotObservable.swift b/RxTests/RxSwiftTests/TestImplementations/Mocks/HotObservable.swift index fc1b644c..45ecb62d 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Mocks/HotObservable.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Mocks/HotObservable.swift @@ -13,13 +13,13 @@ class HotObservable : ObservableType, ObservableConvertible typealias E = Element typealias Events = Recorded - typealias Observer = ObserverOf + typealias Observer = AnyObserver let testScheduler: TestScheduler var subscriptions: [Subscription] var recordedEvents: [Events] - var observers: Bag> + var observers: Bag> init(testScheduler: TestScheduler, recordedEvents: [Events]) { self.testScheduler = testScheduler @@ -37,7 +37,7 @@ class HotObservable : ObservableType, ObservableConvertible } func subscribe(observer: O) -> Disposable { - let key = observers.insert(ObserverOf(observer)) + let key = observers.insert(AnyObserver(observer)) subscriptions.append(Subscription(self.testScheduler.now)) let i = self.subscriptions.count - 1 diff --git a/RxTests/RxSwiftTests/TestImplementations/Mocks/MySubject.swift b/RxTests/RxSwiftTests/TestImplementations/Mocks/MySubject.swift index e1e35c76..8b403855 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Mocks/MySubject.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Mocks/MySubject.swift @@ -14,7 +14,7 @@ class MySubject : SubjectType, ObserverType { typealias SubjectObserverType = MySubject var _disposeOn: [Element : Disposable] = [:] - var _observer: ObserverOf! = nil + var _observer: AnyObserver! = nil var _subscribeCount: Int = 0 var _disposed: Bool = false @@ -47,10 +47,10 @@ class MySubject : SubjectType, ObserverType { func subscribe(observer: O) -> Disposable { _subscribeCount++ - _observer = ObserverOf(observer) + _observer = AnyObserver(observer) return AnonymousDisposable { - self._observer = ObserverOf { _ -> Void in () } + self._observer = AnyObserver { _ -> Void in () } self._disposed = true } } diff --git a/RxTests/RxSwiftTests/TestImplementations/Mocks/PrimitiveHotObservable.swift b/RxTests/RxSwiftTests/TestImplementations/Mocks/PrimitiveHotObservable.swift index f094f193..5fb9c977 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Mocks/PrimitiveHotObservable.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Mocks/PrimitiveHotObservable.swift @@ -16,10 +16,10 @@ class PrimitiveHotObservable : ObservableType { typealias E = ElementType typealias Events = Recorded - typealias Observer = ObserverOf + typealias Observer = AnyObserver var subscriptions: [Subscription] - var observers: Bag> + var observers: Bag> init() { self.subscriptions = [] @@ -31,7 +31,7 @@ class PrimitiveHotObservable : ObservableType { } func subscribe(observer: O) -> Disposable { - let key = observers.insert(ObserverOf(observer)) + let key = observers.insert(AnyObserver(observer)) subscriptions.append(SubscribedToHotObservable) let i = self.subscriptions.count - 1 diff --git a/RxTests/RxSwiftTests/Tests/AnonymousObservable+Test.swift b/RxTests/RxSwiftTests/Tests/AnonymousObservable+Test.swift index 4e5b8fe4..1837c348 100644 --- a/RxTests/RxSwiftTests/Tests/AnonymousObservable+Test.swift +++ b/RxTests/RxSwiftTests/Tests/AnonymousObservable+Test.swift @@ -16,7 +16,7 @@ class AnonymousObservableTests : RxTest { extension AnonymousObservableTests { func testAnonymousObservable_detachesOnDispose() { - var observer: ObserverOf! + var observer: AnyObserver! let a = create { o in observer = o return NopDisposable.instance @@ -40,7 +40,7 @@ extension AnonymousObservableTests { } func testAnonymousObservable_detachesOnComplete() { - var observer: ObserverOf! + var observer: AnyObserver! let a = create { o in observer = o return NopDisposable.instance @@ -64,7 +64,7 @@ extension AnonymousObservableTests { } func testAnonymousObservable_detachesOnError() { - var observer: ObserverOf! + var observer: AnyObserver! let a = create { o in observer = o return NopDisposable.instance diff --git a/RxTests/RxSwiftTests/Tests/ObserverTests.swift b/RxTests/RxSwiftTests/Tests/ObserverTests.swift index cb3db335..d3c23823 100644 --- a/RxTests/RxSwiftTests/Tests/ObserverTests.swift +++ b/RxTests/RxSwiftTests/Tests/ObserverTests.swift @@ -14,7 +14,7 @@ class ObserverTests: RxTest { } extension ObserverTests { func testConvenienceOn_Next() { - var observer: ObserverOf! + var observer: AnyObserver! let a: Observable = create { o in observer = o return NopDisposable.instance @@ -34,7 +34,7 @@ extension ObserverTests { } func testConvenienceOn_Error() { - var observer: ObserverOf! + var observer: AnyObserver! let a: Observable = create { o in observer = o return NopDisposable.instance @@ -63,7 +63,7 @@ extension ObserverTests { } func testConvenienceOn_Complete() { - var observer: ObserverOf! + var observer: AnyObserver! let a: Observable = create { o in observer = o return NopDisposable.instance From 01304bcff7d30ea82fbdd518e48d8665c26b3942 Mon Sep 17 00:00:00 2001 From: yury Date: Sat, 17 Oct 2015 18:19:47 +0300 Subject: [PATCH 045/210] More work on private vars --- .../Observables/Implementations/Switch.swift | 76 +++++++++---------- .../Observables/Implementations/Take.swift | 64 ++++++++-------- .../Implementations/TakeUntil.swift | 57 +++++++------- .../Implementations/Throttle.swift | 54 ++++++------- .../Observables/Implementations/Timer.swift | 26 +++---- 5 files changed, 137 insertions(+), 140 deletions(-) diff --git a/RxSwift/Observables/Implementations/Switch.swift b/RxSwift/Observables/Implementations/Switch.swift index 8465b71e..10fa9071 100644 --- a/RxSwift/Observables/Implementations/Switch.swift +++ b/RxSwift/Observables/Implementations/Switch.swift @@ -12,58 +12,58 @@ class SwitchSink typealias E = S typealias Parent = Switch - let subscriptions: SingleAssignmentDisposable = SingleAssignmentDisposable() - let innerSubscription: SerialDisposable = SerialDisposable() - let parent: Parent + private let _subscriptions: SingleAssignmentDisposable = SingleAssignmentDisposable() + private let _innerSubscription: SerialDisposable = SerialDisposable() + private let _parent: Parent - let lock = NSRecursiveLock() + private let _lock = NSRecursiveLock() // state - var stopped = false - var latest = 0 - var hasLatest = false + private var _stopped = false + private var _latest = 0 + private var _hasLatest = false init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { - let subscription = self.parent.sources.subscribeSafe(self) - subscriptions.disposable = subscription - return CompositeDisposable(subscriptions, innerSubscription) + let subscription = _parent._sources.subscribeSafe(self) + _subscriptions.disposable = subscription + return CompositeDisposable(_subscriptions, _innerSubscription) } func on(event: Event) { switch event { case .Next(let observable): - let latest: Int = self.lock.calculateLocked { - hasLatest = true - self.latest = self.latest &+ 1 - return self.latest + let latest: Int = _lock.calculateLocked { + _hasLatest = true + _latest = _latest &+ 1 + return _latest } let d = SingleAssignmentDisposable() - innerSubscription.disposable = d + _innerSubscription.disposable = d let observer = SwitchSinkIter(parent: self, id: latest, _self: d) let disposable = observable.asObservable().subscribeSafe(observer) d.disposable = disposable case .Error(let error): - self.lock.performLocked { + _lock.performLocked { observer?.on(.Error(error)) - self.dispose() + dispose() } case .Completed: - self.lock.performLocked { - self.stopped = true + _lock.performLocked { + _stopped = true - self.subscriptions.dispose() + _subscriptions.dispose() - if !self.hasLatest { + if !_hasLatest { observer?.on(.Completed) - self.dispose() + dispose() } } } @@ -74,42 +74,42 @@ class SwitchSinkIter - let parent: Parent - let id: Int - let _self: Disposable + private let _parent: Parent + private let _id: Int + private let _self: Disposable init(parent: Parent, id: Int, _self: Disposable) { - self.parent = parent - self.id = id + _parent = parent + _id = id self._self = _self } func on(event: Event) { - return parent.lock.calculateLocked { + return _parent._lock.calculateLocked { switch event { case .Next: break case .Error, .Completed: - self._self.dispose() + _self.dispose() } - if parent.latest != self.id { + if _parent._latest != _id { return } - let observer = self.parent.observer + let observer = _parent.observer switch event { case .Next: observer?.on(event) case .Error: observer?.on(event) - self.parent.dispose() + _parent.dispose() case .Completed: - parent.hasLatest = false - if parent.stopped { + _parent._hasLatest = false + if _parent._stopped { observer?.on(event) - self.parent.dispose() + _parent.dispose() } } } @@ -117,10 +117,10 @@ class SwitchSinkIter : Producer { - let sources: Observable + private let _sources: Observable init(sources: Observable) { - self.sources = sources + _sources = sources } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { diff --git a/RxSwift/Observables/Implementations/Take.swift b/RxSwift/Observables/Implementations/Take.swift index 98d60db6..ca9054d1 100644 --- a/RxSwift/Observables/Implementations/Take.swift +++ b/RxSwift/Observables/Implementations/Take.swift @@ -14,13 +14,13 @@ class TakeCountSink : Sin typealias Parent = TakeCount typealias E = ElementType - let parent: Parent + private let _parent: Parent - var remaining: Int + private var _remaining: Int init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent - self.remaining = parent.count + _parent = parent + _remaining = parent._count super.init(observer: observer, cancel: cancel) } @@ -28,40 +28,40 @@ class TakeCountSink : Sin switch event { case .Next(let value): - if remaining > 0 { - remaining-- + if _remaining > 0 { + _remaining-- observer?.on(.Next(value)) - if remaining == 0 { + if _remaining == 0 { observer?.on(.Completed) - self.dispose() + dispose() } } case .Error: observer?.on(event) - self.dispose() + dispose() case .Completed: observer?.on(event) - self.dispose() + dispose() } } } class TakeCount: Producer { - let source: Observable - let count: Int + private let _source: Observable + private let _count: Int init(source: Observable, count: Int) { - self.source = source - self.count = count + _source = source + _count = count } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { let sink = TakeCountSink(parent: self, observer: observer, cancel: cancel) setSink(sink) - return source.subscribeSafe(sink) + return _source.subscribeSafe(sink) } } @@ -71,44 +71,44 @@ class TakeTimeSink typealias E = ElementType - let parent: Parent + private let _parent: Parent - let lock = NSRecursiveLock() + private let _lock = NSRecursiveLock() init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } func on(event: Event) { - lock.performLocked { + _lock.performLocked { switch event { case .Next(let value): observer?.on(.Next(value)) case .Error: observer?.on(event) - self.dispose() + dispose() case .Completed: observer?.on(event) - self.dispose() + dispose() } } } func tick() { - lock.performLocked { - self.observer?.on(.Completed) - self.dispose() + _lock.performLocked { + observer?.on(.Completed) + dispose() } } func run() -> Disposable { - let disposeTimer = parent.scheduler.scheduleRelative((), dueTime: self.parent.duration) { + let disposeTimer = _parent._scheduler.scheduleRelative((), dueTime: _parent._duration) { self.tick() return NopDisposable.instance } - let disposeSubscription = parent.source.subscribeSafe(self) + let disposeSubscription = _parent._source.subscribeSafe(self) return BinaryDisposable(disposeTimer, disposeSubscription) } @@ -117,14 +117,14 @@ class TakeTimeSink: Producer { typealias TimeInterval = S.TimeInterval - let source: Observable - let duration: TimeInterval - let scheduler: S + private let _source: Observable + private let _duration: TimeInterval + private let _scheduler: S init(source: Observable, duration: TimeInterval, scheduler: S) { - self.source = source - self.scheduler = scheduler - self.duration = duration + _source = source + _scheduler = scheduler + _duration = duration } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { diff --git a/RxSwift/Observables/Implementations/TakeUntil.swift b/RxSwift/Observables/Implementations/TakeUntil.swift index f791a4c2..77b273b3 100644 --- a/RxSwift/Observables/Implementations/TakeUntil.swift +++ b/RxSwift/Observables/Implementations/TakeUntil.swift @@ -12,38 +12,38 @@ class TakeUntilSinkOther typealias E = Other - let parent: Parent + private let _parent: Parent - let singleAssignmentDisposable = SingleAssignmentDisposable() + private let _singleAssignmentDisposable = SingleAssignmentDisposable() var disposable: Disposable { get { abstractMethod() } set(value) { - singleAssignmentDisposable.disposable = value + _singleAssignmentDisposable.disposable = value } } init(parent: Parent) { - self.parent = parent + _parent = parent #if TRACE_RESOURCES OSAtomicIncrement32(&resourceCount) #endif } func on(event: Event) { - parent.lock.performLocked { + _parent._lock.performLocked { switch event { case .Next: - parent.observer?.on(.Completed) - parent.dispose() + _parent.observer?.on(.Completed) + _parent.dispose() case .Error(let e): - parent.observer?.on(.Error(e)) - parent.dispose() + _parent.observer?.on(.Error(e)) + _parent.dispose() case .Completed: - parent.open = true - singleAssignmentDisposable.dispose() + _parent._open = true + _singleAssignmentDisposable.dispose() } } } @@ -59,50 +59,47 @@ class TakeUntilSink - let parent: Parent + private let _parent: Parent - let lock = NSRecursiveLock() + private let _lock = NSRecursiveLock() // state - var open = false + private var _open = false init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } func on(event: Event) { switch event { case .Next: - if open { + if _open { observer?.on(event) } else { - lock.performLocked { + _lock.performLocked { observer?.on(event) } } - break case .Error: - lock.performLocked { + _lock.performLocked { observer?.on(event) - self.dispose() + dispose() } - break case .Completed: - lock.performLocked { + _lock.performLocked { observer?.on(event) - self.dispose() + dispose() } - break } } func run() -> Disposable { let otherObserver = TakeUntilSinkOther(parent: self) - let otherSubscription = parent.other.subscribeSafe(otherObserver) + let otherSubscription = _parent._other.subscribeSafe(otherObserver) otherObserver.disposable = otherSubscription - let sourceSubscription = parent.source.subscribeSafe(self) + let sourceSubscription = _parent._source.subscribeSafe(self) return CompositeDisposable(sourceSubscription, otherSubscription) } @@ -110,12 +107,12 @@ class TakeUntilSink: Producer { - let source: Observable - let other: Observable + private let _source: Observable + private let _other: Observable init(source: Observable, other: Observable) { - self.source = source - self.other = other + _source = source + _other = other } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { diff --git a/RxSwift/Observables/Implementations/Throttle.swift b/RxSwift/Observables/Implementations/Throttle.swift index cbe16da4..4d881c3e 100644 --- a/RxSwift/Observables/Implementations/Throttle.swift +++ b/RxSwift/Observables/Implementations/Throttle.swift @@ -12,24 +12,24 @@ class ThrottleSink : Sink, Observe typealias Element = O.E typealias ParentType = Throttle - let parent: ParentType + private let _parent: ParentType - let lock = NSRecursiveLock() + private let _lock = NSRecursiveLock() // state - var id = 0 as UInt64 - var value: Element? = nil + private var _id = 0 as UInt64 + private var _value: Element? = nil let cancellable = SerialDisposable() init(parent: ParentType, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { - let subscription = parent.source.subscribeSafe(self) + let subscription = _parent._source.subscribeSafe(self) return CompositeDisposable(subscription, cancellable) } @@ -42,40 +42,40 @@ class ThrottleSink : Sink, Observe cancellable.dispose() } - let latestId = self.lock.calculateLocked { () -> UInt64 in + let latestId = _lock.calculateLocked { () -> UInt64 in let observer = self.observer - let oldValue = self.value + let oldValue = _value - self.id = self.id &+ 1 + _id = _id &+ 1 switch event { case .Next(let element): - self.value = element + _value = element case .Error: - self.value = nil + _value = nil observer?.on(event) - self.dispose() + dispose() case .Completed: - self.value = nil + _value = nil if let value = oldValue { observer?.on(.Next(value)) } observer?.on(.Completed) - self.dispose() + dispose() } - return id + return _id } switch event { - case .Next(_): + case .Next: let d = SingleAssignmentDisposable() self.cancellable.disposable = d - let scheduler = self.parent.scheduler - let dueTime = self.parent.dueTime + let scheduler = _parent._scheduler + let dueTime = _parent._dueTime let disposeTimer = scheduler.scheduleRelative(latestId, dueTime: dueTime) { (id) in self.propagate() @@ -88,9 +88,9 @@ class ThrottleSink : Sink, Observe } func propagate() { - let originalValue: Element? = self.lock.calculateLocked { - let originalValue = self.value - self.value = nil + let originalValue: Element? = _lock.calculateLocked { + let originalValue = _value + _value = nil return originalValue } @@ -102,14 +102,14 @@ class ThrottleSink : Sink, Observe class Throttle : Producer { - let source: Observable - let dueTime: Scheduler.TimeInterval - let scheduler: Scheduler + private let _source: Observable + private let _dueTime: Scheduler.TimeInterval + private let _scheduler: Scheduler init(source: Observable, dueTime: Scheduler.TimeInterval, scheduler: Scheduler) { - self.source = source - self.dueTime = dueTime - self.scheduler = scheduler + _source = source + _dueTime = dueTime + _scheduler = scheduler } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { diff --git a/RxSwift/Observables/Implementations/Timer.swift b/RxSwift/Observables/Implementations/Timer.swift index 98a76a6e..35f2839f 100644 --- a/RxSwift/Observables/Implementations/Timer.swift +++ b/RxSwift/Observables/Implementations/Timer.swift @@ -11,15 +11,15 @@ import Foundation class TimerSink : Sink { typealias Parent = Timer - let parent: Parent + private let _parent: Parent init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { - return self.parent.scheduler.schedulePeriodic(0 as Int64, startAfter: self.parent.dueTime, period: self.parent.period!) { state in + return _parent._scheduler.schedulePeriodic(0 as Int64, startAfter: _parent._dueTime, period: _parent._period!) { state in self.observer?.on(.Next(state)) return state &+ 1 } @@ -29,15 +29,15 @@ class TimerSink : Sink class TimerOneOffSink : Sink { typealias Parent = Timer - let parent: Parent + private let _parent: Parent init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { - return self.parent.scheduler.scheduleRelative((), dueTime: self.parent.dueTime) { (_) -> Disposable in + return _parent._scheduler.scheduleRelative((), dueTime: _parent._dueTime) { (_) -> Disposable in self.observer?.on(.Next(0)) self.observer?.on(.Completed) @@ -49,18 +49,18 @@ class TimerOneOffSink : Si class Timer: Producer { typealias TimeInterval = S.TimeInterval - let scheduler: S - let dueTime: TimeInterval - let period: TimeInterval? + private let _scheduler: S + private let _dueTime: TimeInterval + private let _period: TimeInterval? init(dueTime: TimeInterval, period: TimeInterval?, scheduler: S) { - self.scheduler = scheduler - self.dueTime = dueTime - self.period = period + _scheduler = scheduler + _dueTime = dueTime + _period = period } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - if let _ = period { + if let _ = _period { let sink = TimerSink(parent: self, observer: observer, cancel: cancel) setSink(sink) return sink.run() From c2f91660f985d58399e8e3efde0592a3181f60ca Mon Sep 17 00:00:00 2001 From: yury Date: Sat, 17 Oct 2015 19:05:49 +0300 Subject: [PATCH 046/210] More private vars --- .../Implementations/RefCount.swift | 42 +++++----- .../Observables/Implementations/Repeat.swift | 14 ++-- .../Observables/Implementations/Sample.swift | 76 +++++++++---------- .../Observables/Implementations/Scan.swift | 34 ++++----- RxSwift/Observers/AnonymousObserver.swift | 6 +- RxSwift/Observers/ObserverBase.swift | 11 +-- RxSwift/Observers/TailRecursiveSink.swift | 38 +++++----- 7 files changed, 109 insertions(+), 112 deletions(-) diff --git a/RxSwift/Observables/Implementations/RefCount.swift b/RxSwift/Observables/Implementations/RefCount.swift index f6bd3354..eacd4ac0 100644 --- a/RxSwift/Observables/Implementations/RefCount.swift +++ b/RxSwift/Observables/Implementations/RefCount.swift @@ -12,36 +12,36 @@ class RefCountSink - let parent: Parent + private let _parent: Parent init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { - let subscription = self.parent.source.subscribeSafe(self) + let subscription = _parent._source.subscribeSafe(self) - self.parent.lock.performLocked { - if parent.count == 0 { - parent.count = 1 - parent.connectableSubscription = self.parent.source.connect() + _parent._lock.performLocked { + if _parent._count == 0 { + _parent._count = 1 + _parent._connectableSubscription = _parent._source.connect() } else { - parent.count = parent.count + 1 + _parent._count = _parent._count + 1 } } return AnonymousDisposable { subscription.dispose() - self.parent.lock.performLocked { - if self.parent.count == 1 { - self.parent.connectableSubscription!.dispose() - self.parent.count = 0 - self.parent.connectableSubscription = nil + self._parent._lock.performLocked { + if self._parent._count == 1 { + self._parent._connectableSubscription!.dispose() + self._parent._count = 0 + self._parent._connectableSubscription = nil } - else if self.parent.count > 1 { - self.parent.count = self.parent.count - 1 + else if self._parent._count > 1 { + self._parent._count = self._parent._count - 1 } else { rxFatalError("Something went wrong with RefCount disposing mechanism") @@ -56,22 +56,22 @@ class RefCountSink: Producer { - let lock = NSRecursiveLock() + private let _lock = NSRecursiveLock() // state - var count = 0 - var connectableSubscription = nil as Disposable? + private var _count = 0 + private var _connectableSubscription = nil as Disposable? - let source: CO + private let _source: CO init(source: CO) { - self.source = source + _source = source } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { diff --git a/RxSwift/Observables/Implementations/Repeat.swift b/RxSwift/Observables/Implementations/Repeat.swift index e7030497..33621d68 100644 --- a/RxSwift/Observables/Implementations/Repeat.swift +++ b/RxSwift/Observables/Implementations/Repeat.swift @@ -9,12 +9,12 @@ import Foundation class RepeatElement : Producer { - let element: Element - let scheduler: ImmediateSchedulerType + private let _element: Element + private let _scheduler: ImmediateSchedulerType init(element: Element, scheduler: ImmediateSchedulerType) { - self.element = element - self.scheduler = scheduler + _element = element + _scheduler = scheduler } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { @@ -27,15 +27,15 @@ class RepeatElement : Producer { class RepeatElementSink : Sink { typealias Parent = RepeatElement - let parent: Parent + private let _parent: Parent init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { - return self.parent.scheduler.scheduleRecursive(self.parent.element) { e, recurse in + return _parent._scheduler.scheduleRecursive(_parent._element) { e, recurse in self.observer?.on(.Next(e)) recurse(e) } diff --git a/RxSwift/Observables/Implementations/Sample.swift b/RxSwift/Observables/Implementations/Sample.swift index 6aede052..47ecaa92 100644 --- a/RxSwift/Observables/Implementations/Sample.swift +++ b/RxSwift/Observables/Implementations/Sample.swift @@ -13,39 +13,39 @@ class SamplerSink - let parent: Parent + private let _parent: Parent init(parent: Parent) { - self.parent = parent + _parent = parent } func on(event: Event) { - parent.lock.performLocked { + _parent._lock.performLocked { switch event { case .Next: - if let element = parent.element { - if self.parent.parent.onlyNew { - parent.element = nil + if let element = _parent._element { + if _parent._parent._onlyNew { + _parent._element = nil } - parent.observer?.on(.Next(element)) + _parent.observer?.on(.Next(element)) } - if parent.atEnd { - parent.observer?.on(.Completed) - parent.dispose() + if _parent._atEnd { + _parent.observer?.on(.Completed) + _parent.dispose() } case .Error(let e): - parent.observer?.on(.Error(e)) - parent.dispose() + _parent.observer?.on(.Error(e)) + _parent.dispose() case .Completed: - if let element = parent.element { - parent.element = nil - parent.observer?.on(.Next(element)) + if let element = _parent._element { + _parent._element = nil + _parent.observer?.on(.Next(element)) } - if parent.atEnd { - parent.observer?.on(.Completed) - parent.dispose() + if _parent._atEnd { + _parent.observer?.on(.Completed) + _parent.dispose() } } } @@ -56,39 +56,39 @@ class SampleSequenceSink : Sink, ObserverType { typealias Element = O.E typealias Parent = Sample - let parent: Parent + private let _parent: Parent - let lock = NSRecursiveLock() + private let _lock = NSRecursiveLock() // state - var element = nil as Element? - var atEnd = false + private var _element = nil as Element? + private var _atEnd = false - let sourceSubscription = SingleAssignmentDisposable() + private let _sourceSubscription = SingleAssignmentDisposable() init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { - sourceSubscription.disposable = parent.source.subscribeSafe(self) - let samplerSubscription = parent.sampler.subscribeSafe(SamplerSink(parent: self)) + _sourceSubscription.disposable = _parent._source.subscribeSafe(self) + let samplerSubscription = _parent._sampler.subscribeSafe(SamplerSink(parent: self)) - return CompositeDisposable(sourceSubscription, samplerSubscription) + return CompositeDisposable(_sourceSubscription, samplerSubscription) } func on(event: Event) { - self.lock.performLocked { + _lock.performLocked { switch event { case .Next(let element): - self.element = element + _element = element case .Error: observer?.on(event) - self.dispose() + dispose() case .Completed: - atEnd = true - sourceSubscription.dispose() + _atEnd = true + _sourceSubscription.dispose() } } } @@ -96,14 +96,14 @@ class SampleSequenceSink : Sink, ObserverType { } class Sample : Producer { - let source: Observable - let sampler: Observable - let onlyNew: Bool + private let _source: Observable + private let _sampler: Observable + private let _onlyNew: Bool init(source: Observable, sampler: Observable, onlyNew: Bool) { - self.source = source - self.sampler = sampler - self.onlyNew = onlyNew + _source = source + _sampler = sampler + _onlyNew = onlyNew } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { diff --git a/RxSwift/Observables/Implementations/Scan.swift b/RxSwift/Observables/Implementations/Scan.swift index 05ba7bfd..49e06cd5 100644 --- a/RxSwift/Observables/Implementations/Scan.swift +++ b/RxSwift/Observables/Implementations/Scan.swift @@ -12,12 +12,12 @@ class ScanSink typealias Parent = Scan typealias E = ElementType - let parent: Parent - var accumulate: Accumulate + private let _parent: Parent + private var _accumulate: Accumulate init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent - self.accumulate = parent.seed + _parent = parent + _accumulate = parent._seed super.init(observer: observer, cancel: cancel) } @@ -25,19 +25,19 @@ class ScanSink switch event { case .Next(let element): do { - self.accumulate = try self.parent.accumulator(self.accumulate, element) - observer?.on(.Next(self.accumulate)) + _accumulate = try _parent._accumulator(_accumulate, element) + observer?.on(.Next(_accumulate)) } catch let error { - self.observer?.on(.Error(error)) - self.dispose() + observer?.on(.Error(error)) + dispose() } case .Error(let error): observer?.on(.Error(error)) - self.dispose() + dispose() case .Completed: observer?.on(.Completed) - self.dispose() + dispose() } } @@ -46,19 +46,19 @@ class ScanSink class Scan: Producer { typealias Accumulator = (Accumulate, Element) throws -> Accumulate - let source: Observable - let seed: Accumulate - let accumulator: Accumulator + private let _source: Observable + private let _seed: Accumulate + private let _accumulator: Accumulator init(source: Observable, seed: Accumulate, accumulator: Accumulator) { - self.source = source - self.seed = seed - self.accumulator = accumulator + _source = source + _seed = seed + _accumulator = accumulator } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { let sink = ScanSink(parent: self, observer: observer, cancel: cancel) setSink(sink) - return source.subscribeSafe(sink) + return _source.subscribeSafe(sink) } } \ No newline at end of file diff --git a/RxSwift/Observers/AnonymousObserver.swift b/RxSwift/Observers/AnonymousObserver.swift index dac6243d..840706aa 100644 --- a/RxSwift/Observers/AnonymousObserver.swift +++ b/RxSwift/Observers/AnonymousObserver.swift @@ -13,17 +13,17 @@ class AnonymousObserver : ObserverBase { typealias EventHandler = Event -> Void - private let eventHandler : EventHandler + private let _eventHandler : EventHandler init(_ eventHandler: EventHandler) { #if TRACE_RESOURCES OSAtomicIncrement32(&resourceCount) #endif - self.eventHandler = eventHandler + _eventHandler = eventHandler } override func onCore(event: Event) { - return self.eventHandler(event) + return _eventHandler(event) } #if TRACE_RESOURCES diff --git a/RxSwift/Observers/ObserverBase.swift b/RxSwift/Observers/ObserverBase.swift index 912f6876..38ce0032 100644 --- a/RxSwift/Observers/ObserverBase.swift +++ b/RxSwift/Observers/ObserverBase.swift @@ -11,20 +11,17 @@ import Foundation class ObserverBase : Disposable, ObserverType { typealias E = ElementType - var isStopped: Int32 = 0 - - init() { - } + private var _isStopped: Int32 = 0 func on(event: Event) { switch event { case .Next: - if isStopped == 0 { + if _isStopped == 0 { onCore(event) } case .Error, .Completed: - if !OSAtomicCompareAndSwap32(0, 1, &isStopped) { + if !OSAtomicCompareAndSwap32(0, 1, &_isStopped) { return } @@ -37,6 +34,6 @@ class ObserverBase : Disposable, ObserverType { } func dispose() { - isStopped = 1 + _isStopped = 1 } } \ No newline at end of file diff --git a/RxSwift/Observers/TailRecursiveSink.swift b/RxSwift/Observers/TailRecursiveSink.swift index 1061c2c8..5d7c992e 100644 --- a/RxSwift/Observers/TailRecursiveSink.swift +++ b/RxSwift/Observers/TailRecursiveSink.swift @@ -12,19 +12,19 @@ import Foundation class TailRecursiveSink : Sink, ObserverType { typealias E = O.E - var generators: [S.Generator] = [] - var disposed: Bool = false - var subscription = SerialDisposable() + private var _generators: [S.Generator] = [] + private var _disposed: Bool = false + private var _subscription = SerialDisposable() - // this is thread safe object - var gate: AsyncLock = AsyncLock() + // this is thread safe objec + private var _gate: AsyncLock = AsyncLock() override init(observer: O, cancel: Disposable) { super.init(observer: observer, cancel: cancel) } func run(sources: S.Generator) -> Disposable { - self.generators.append(sources) + _generators.append(sources) scheduleMoveNext() @@ -34,7 +34,7 @@ class TailRecursiveSink Void) { - self.gate.wait(action) + _gate.wait(action) } func done() { observer?.on(.Completed) - self.dispose() + dispose() } func extract(observable: Observable) -> S.Generator? { @@ -67,29 +67,29 @@ class TailRecursiveSink? = nil; repeat { - if self.generators.count == 0 { + if _generators.count == 0 { break } - if disposed { + if _disposed { return } - var e = generators.last! + var e = _generators.last! let nextCandidate = e.next()?.asObservable() - generators.removeLast() - generators.append(e) + _generators.removeLast() + _generators.append(e) if nextCandidate == nil { - generators.removeLast() + _generators.removeLast() continue; } let nextGenerator = extract(nextCandidate!) if let nextGenerator = nextGenerator { - self.generators.append(nextGenerator) + self._generators.append(nextGenerator) } else { next = nextCandidate @@ -102,12 +102,12 @@ class TailRecursiveSink Date: Sat, 17 Oct 2015 20:28:28 +0300 Subject: [PATCH 047/210] Work on data structures --- RxSwift/DataStructures/Bag.swift | 14 ++-- RxSwift/DataStructures/InfiniteSequence.swift | 6 +- RxSwift/DataStructures/Queue.swift | 70 +++++++++---------- RxSwift/Schedulers/RecursiveScheduler.swift | 8 +-- .../SchedulerServices+Emulation.swift | 39 +++++------ 5 files changed, 67 insertions(+), 70 deletions(-) diff --git a/RxSwift/DataStructures/Bag.swift b/RxSwift/DataStructures/Bag.swift index f8d144e4..0386e2f8 100644 --- a/RxSwift/DataStructures/Bag.swift +++ b/RxSwift/DataStructures/Bag.swift @@ -50,8 +50,8 @@ public struct Bag : CustomStringConvertible { typealias Entry = (key: BagKey, value: T) - private var uniqueIdentity: Identity? - private var nextKey: ScopeUniqueTokenType = 0 + private var _uniqueIdentity: Identity? + private var _nextKey: ScopeUniqueTokenType = 0 var pairs = [Entry]() @@ -77,17 +77,17 @@ public struct Bag : CustomStringConvertible { - returns: Key that can be used to remove element from bag. */ public mutating func insert(element: T) -> BagKey { - nextKey = nextKey &+ 1 + _nextKey = _nextKey &+ 1 #if DEBUG - nextKey = nextKey % 20 + _nextKey = _nextKey % 20 #endif - if nextKey == 0 { - uniqueIdentity = Identity() + if _nextKey == 0 { + _uniqueIdentity = Identity() } - let key = BagKey(uniqueIdentity: uniqueIdentity, key: nextKey) + let key = BagKey(uniqueIdentity: _uniqueIdentity, key: _nextKey) pairs.append(key: key, value: element) diff --git a/RxSwift/DataStructures/InfiniteSequence.swift b/RxSwift/DataStructures/InfiniteSequence.swift index 530a4fb8..b0b08307 100644 --- a/RxSwift/DataStructures/InfiniteSequence.swift +++ b/RxSwift/DataStructures/InfiniteSequence.swift @@ -15,14 +15,14 @@ class InfiniteSequence : SequenceType { typealias Element = E typealias Generator = AnyGenerator - let repeatedValue: E + private let _repeatedValue: E init(repeatedValue: E) { - self.repeatedValue = repeatedValue + _repeatedValue = repeatedValue } func generate() -> Generator { - let repeatedValue = self.repeatedValue + let repeatedValue = _repeatedValue return anyGenerator { return repeatedValue } diff --git a/RxSwift/DataStructures/Queue.swift b/RxSwift/DataStructures/Queue.swift index 8545acb4..e85224a2 100644 --- a/RxSwift/DataStructures/Queue.swift +++ b/RxSwift/DataStructures/Queue.swift @@ -24,11 +24,11 @@ public struct Queue: SequenceType { let resizeFactor = 2 - private var storage: [T?] + private var _storage: [T?] private var _count: Int - private var pushNextIndex: Int - private var initialCapacity: Int - private var version: Int + private var _pushNextIndex: Int + private var _initialCapacity: Int + private var _version: Int /** Creates new queue. @@ -36,24 +36,24 @@ public struct Queue: SequenceType { - parameter capacity: Capacity of newly created queue. */ public init(capacity: Int) { - initialCapacity = capacity + _initialCapacity = capacity - version = 0 + _version = 0 _count = 0 - pushNextIndex = 0 + _pushNextIndex = 0 if capacity > 0 { - storage = [T?](count: capacity, repeatedValue: nil) + _storage = [T?](count: capacity, repeatedValue: nil) } else { - storage = [] + _storage = [] } } private var dequeueIndex: Int { get { - let index = pushNextIndex - count - return index < 0 ? index + self.storage.count : index + let index = _pushNextIndex - count + return index < 0 ? index + _storage.count : index } } @@ -81,7 +81,7 @@ public struct Queue: SequenceType { public func peek() -> T { precondition(count > 0) - return storage[dequeueIndex]! + return _storage[dequeueIndex]! } mutating private func resizeTo(size: Int) { @@ -90,19 +90,19 @@ public struct Queue: SequenceType { let count = _count let dequeueIndex = self.dequeueIndex - let spaceToEndOfQueue = self.storage.count - dequeueIndex + let spaceToEndOfQueue = _storage.count - dequeueIndex // first batch is from dequeue index to end of array let countElementsInFirstBatch = min(count, spaceToEndOfQueue) // second batch is wrapped from start of array to end of queue let numberOfElementsInSecondBatch = count - countElementsInFirstBatch - newStorage[0 ..< countElementsInFirstBatch] = self.storage[dequeueIndex ..< (dequeueIndex + countElementsInFirstBatch)] - newStorage[countElementsInFirstBatch ..< (countElementsInFirstBatch + numberOfElementsInSecondBatch)] = self.storage[0 ..< numberOfElementsInSecondBatch] + newStorage[0 ..< countElementsInFirstBatch] = _storage[dequeueIndex ..< (dequeueIndex + countElementsInFirstBatch)] + newStorage[countElementsInFirstBatch ..< (countElementsInFirstBatch + numberOfElementsInSecondBatch)] = _storage[0 ..< numberOfElementsInSecondBatch] _count = count - pushNextIndex = count - storage = newStorage + _pushNextIndex = count + _storage = newStorage } /** @@ -111,30 +111,30 @@ public struct Queue: SequenceType { - parameter element: Element to enqueue. */ public mutating func enqueue(element: T) { - version++ + _version++ - if count == storage.count { - resizeTo(max(storage.count, 1) * resizeFactor) + if count == _storage.count { + resizeTo(max(_storage.count, 1) * resizeFactor) } - storage[pushNextIndex] = element - pushNextIndex++ + _storage[_pushNextIndex] = element + _pushNextIndex++ _count = _count + 1 - if pushNextIndex >= storage.count { - pushNextIndex -= storage.count + if _pushNextIndex >= _storage.count { + _pushNextIndex -= _storage.count } } private mutating func dequeueElementOnly() -> T { precondition(count > 0) - version++ + _version++ let index = dequeueIndex - let value = storage[index]! + let value = _storage[index]! - storage[index] = nil + _storage[index] = nil _count = _count - 1 @@ -162,9 +162,9 @@ public struct Queue: SequenceType { public mutating func dequeue() -> T { let value = dequeueElementOnly() - let downsizeLimit = storage.count / (resizeFactor * resizeFactor) - if _count < downsizeLimit && downsizeLimit >= initialCapacity { - resizeTo(storage.count / resizeFactor) + let downsizeLimit = _storage.count / (resizeFactor * resizeFactor) + if _count < downsizeLimit && downsizeLimit >= _initialCapacity { + resizeTo(_storage.count / resizeFactor) } return value @@ -177,10 +177,10 @@ public struct Queue: SequenceType { var i = dequeueIndex var count = _count - let lastVersion = version + let lastVersion = _version return anyGenerator { - if lastVersion != self.version { + if lastVersion != self._version { rxFatalError("Collection was modified while enumerated") } @@ -189,11 +189,11 @@ public struct Queue: SequenceType { } count-- - if i >= self.storage.count { - i -= self.storage.count + if i >= self._storage.count { + i -= self._storage.count } - return self.storage[i++] + return self._storage[i++] } } } \ No newline at end of file diff --git a/RxSwift/Schedulers/RecursiveScheduler.swift b/RxSwift/Schedulers/RecursiveScheduler.swift index 8aa72c02..d7862151 100644 --- a/RxSwift/Schedulers/RecursiveScheduler.swift +++ b/RxSwift/Schedulers/RecursiveScheduler.swift @@ -9,19 +9,19 @@ import Foundation class RecursiveScheduler: RecursiveSchedulerOf { - let scheduler: S + private let _scheduler: S init(scheduler: S, action: Action) { - self.scheduler = scheduler + _scheduler = scheduler super.init(action: action) } override func scheduleRelativeAdapter(state: State, dueTime: S.TimeInterval, action: State -> Disposable) -> Disposable { - return scheduler.scheduleRelative(state, dueTime: dueTime, action: action) + return _scheduler.scheduleRelative(state, dueTime: dueTime, action: action) } override func scheduleAdapter(state: State, action: State -> Disposable) -> Disposable { - return scheduler.schedule(state, action: action) + return _scheduler.schedule(state, action: action) } } diff --git a/RxSwift/Schedulers/SchedulerServices+Emulation.swift b/RxSwift/Schedulers/SchedulerServices+Emulation.swift index 9fab3e25..630ecd82 100644 --- a/RxSwift/Schedulers/SchedulerServices+Emulation.swift +++ b/RxSwift/Schedulers/SchedulerServices+Emulation.swift @@ -19,44 +19,41 @@ class SchedulePeriodicRecursive { typealias TimeInterval = S.TimeInterval typealias RecursiveScheduler = RecursiveSchedulerOf - let scheduler: S - let startAfter: TimeInterval - let period: TimeInterval - let action: RecursiveAction + private let _scheduler: S + private let _startAfter: TimeInterval + private let _period: TimeInterval + private let _action: RecursiveAction - var state: State - var pendingTickCount: Int32 = 0 + private var _state: State + private var _pendingTickCount: Int32 = 0 init(scheduler: S, startAfter: TimeInterval, period: TimeInterval, action: RecursiveAction, state: State) { - self.scheduler = scheduler - self.startAfter = startAfter - self.period = period - self.action = action - self.state = state + _scheduler = scheduler + _startAfter = startAfter + _period = period + _action = action + _state = state } func start() -> Disposable { - return scheduler.scheduleRecursive(SchedulePeriodicRecursiveCommand.Tick, dueTime: self.startAfter, action: self.tick) + return _scheduler.scheduleRecursive(SchedulePeriodicRecursiveCommand.Tick, dueTime: _startAfter, action: tick) } - func tick(command: SchedulePeriodicRecursiveCommand, scheduler: RecursiveScheduler) -> Void { + func tick(command: SchedulePeriodicRecursiveCommand, scheduler: RecursiveScheduler) { switch command { case .Tick: - scheduler.schedule(.Tick, dueTime: self.period) + scheduler.schedule(.Tick, dueTime: _period) - if OSAtomicIncrement32(&pendingTickCount) == 1 { - self.tick(.DispatchStart, scheduler: scheduler) + if OSAtomicIncrement32(&_pendingTickCount) == 1 { + tick(.DispatchStart, scheduler: scheduler) } - break case .DispatchStart: - self.state = action(state) + _state = _action(_state) scheduler.schedule(SchedulePeriodicRecursiveCommand.DispatchEnd) - break case .DispatchEnd: - if OSAtomicDecrement32(&pendingTickCount) > 0 { + if OSAtomicDecrement32(&_pendingTickCount) > 0 { scheduler.schedule(SchedulePeriodicRecursiveCommand.DispatchStart) } - break } } } \ No newline at end of file From c9ea983f3ac8415183ec37fffde706532ce0d46d Mon Sep 17 00:00:00 2001 From: yury Date: Sat, 17 Oct 2015 21:04:38 +0300 Subject: [PATCH 048/210] Add Using operator to no-module example --- RxExample/RxExample.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index 2a4ce783..881be400 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -305,6 +305,7 @@ C8DF92EA1B0B38C0009BCF9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; }; C8DF92EB1B0B38C0009BCF9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; }; C8DF92F61B0B43A4009BCF9A /* IntroductionExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DF92F51B0B43A4009BCF9A /* IntroductionExampleViewController.swift */; }; + D2AF91891BD2C51900A008C1 /* Using.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AF91881BD2C51900A008C1 /* Using.swift */; }; EC91FB951BBA144400973245 /* GitHubSearchRepositoriesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC91FB941BBA144400973245 /* GitHubSearchRepositoriesViewController.swift */; }; /* End PBXBuildFile section */ @@ -676,6 +677,7 @@ C8DF92F01B0B3E67009BCF9A /* Info-OSX.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-OSX.plist"; sourceTree = ""; }; C8DF92F21B0B3E71009BCF9A /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = ""; }; C8DF92F51B0B43A4009BCF9A /* IntroductionExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = IntroductionExampleViewController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D2AF91881BD2C51900A008C1 /* Using.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Using.swift; sourceTree = ""; }; EC91FB941BBA144400973245 /* GitHubSearchRepositoriesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubSearchRepositoriesViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -1068,6 +1070,7 @@ C89464481BC6C2B00055219D /* Implementations */ = { isa = PBXGroup; children = ( + D2AF91881BD2C51900A008C1 /* Using.swift */, C89464491BC6C2B00055219D /* Amb.swift */, C894644A1BC6C2B00055219D /* AnonymousObservable.swift */, C894644B1BC6C2B00055219D /* AsObservable.swift */, @@ -1805,6 +1808,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D2AF91891BD2C51900A008C1 /* Using.swift in Sources */, 0706E19F1B17703E00BA2D3A /* RandomUserAPI.swift in Sources */, C86E2F3E1AE5A0CA00C31024 /* SearchResultViewModel.swift in Sources */, C83367241AD029AE00C668A7 /* HtmlParsing.swift in Sources */, From fcd626f00dd5d532381ef37f47e48a75208b9f34 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sat, 17 Oct 2015 20:47:03 +0200 Subject: [PATCH 049/210] Scheduler interface lipstick. --- RxSwift/ImmediateSchedulerType.swift | 4 +-- RxSwift/SchedulerType.swift | 11 +++++++- RxSwift/Schedulers/RecursiveScheduler.swift | 14 +++++----- .../SchedulerServices+Emulation.swift | 18 +++++++------ .../Schedulers/VirtualTimeSchedulerBase.swift | 26 ------------------- 5 files changed, 29 insertions(+), 44 deletions(-) diff --git a/RxSwift/ImmediateSchedulerType.swift b/RxSwift/ImmediateSchedulerType.swift index f6703401..55ea2a36 100644 --- a/RxSwift/ImmediateSchedulerType.swift +++ b/RxSwift/ImmediateSchedulerType.swift @@ -30,8 +30,8 @@ extension ImmediateSchedulerType { - parameter action: Action to execute recursively. The last parameter passed to the action is used to trigger recursive scheduling of the action, passing in recursive invocation state. - returns: The disposable object used to cancel the scheduled action (best effort). */ - public func scheduleRecursive(state: State, action: (state: State, recurse: (State) -> Void) -> Void) -> Disposable { - let recursiveScheduler = RecursiveImmediateSchedulerOf(action: action, scheduler: self) + public func scheduleRecursive(state: State, action: (state: State, recurse: (State) -> ()) -> ()) -> Disposable { + let recursiveScheduler = RecursiveImmediateScheduler(action: action, scheduler: self) recursiveScheduler.schedule(state) diff --git a/RxSwift/SchedulerType.swift b/RxSwift/SchedulerType.swift index 26c24227..5160fb4c 100644 --- a/RxSwift/SchedulerType.swift +++ b/RxSwift/SchedulerType.swift @@ -52,13 +52,22 @@ public protocol SchedulerType: ImmediateSchedulerType { } extension SchedulerType { + + /** + Periodic task will be emulated using recursive scheduling. + + - parameter state: Initial state passed to the action upon the first iteration. + - parameter startAfter: Period after which initial work should be run. + - parameter period: Period for running the work periodically. + - returns: The disposable object used to cancel the scheduled recurring action (best effort). + */ public func schedulePeriodic(state: StateType, startAfter: TimeInterval, period: TimeInterval, action: (StateType) -> StateType) -> Disposable { let schedule = SchedulePeriodicRecursive(scheduler: self, startAfter: startAfter, period: period, action: action, state: state) return schedule.start() } - func scheduleRecursive(state: State, dueTime: TimeInterval, action: (state: State, scheduler: RecursiveSchedulerOf) -> Void) -> Disposable { + func scheduleRecursive(state: State, dueTime: TimeInterval, action: (state: State, scheduler: AnyRecursiveScheduler) -> ()) -> Disposable { let scheduler = RecursiveScheduler(scheduler: self, action: action) scheduler.schedule(state, dueTime: dueTime) diff --git a/RxSwift/Schedulers/RecursiveScheduler.swift b/RxSwift/Schedulers/RecursiveScheduler.swift index 1f3b9f61..b553dac2 100644 --- a/RxSwift/Schedulers/RecursiveScheduler.swift +++ b/RxSwift/Schedulers/RecursiveScheduler.swift @@ -8,7 +8,7 @@ import Foundation -class RecursiveScheduler: RecursiveSchedulerOf { +class RecursiveScheduler: AnyRecursiveScheduler { let scheduler: S init(scheduler: S, action: Action) { @@ -28,8 +28,8 @@ class RecursiveScheduler: RecursiveSchedulerOf { - typealias Action = (state: State, scheduler: RecursiveSchedulerOf) -> Void +class AnyRecursiveScheduler { + typealias Action = (state: State, scheduler: AnyRecursiveScheduler) -> Void let lock = NSRecursiveLock() @@ -58,7 +58,7 @@ public class RecursiveSchedulerOf { - parameter state: State passed to the action to be executed. - parameter dueTime: Relative time after which to execute the recursive action. */ - public func schedule(state: State, dueTime: TimeInterval) { + func schedule(state: State, dueTime: TimeInterval) { var isAdded = false var isDone = false @@ -101,7 +101,7 @@ public class RecursiveSchedulerOf { - parameter state: State passed to the action to be executed. */ - public func schedule(state: State) { + func schedule(state: State) { var isAdded = false var isDone = false @@ -150,7 +150,7 @@ public class RecursiveSchedulerOf { /** Type erased recursive scheduler. */ -public class RecursiveImmediateSchedulerOf { +class RecursiveImmediateScheduler { typealias Action = (state: State, recurse: State -> Void) -> Void var lock = SpinLock() @@ -171,7 +171,7 @@ public class RecursiveImmediateSchedulerOf { - parameter state: State passed to the action to be executed. */ - public func schedule(state: State) { + func schedule(state: State) { var isAdded = false var isDone = false diff --git a/RxSwift/Schedulers/SchedulerServices+Emulation.swift b/RxSwift/Schedulers/SchedulerServices+Emulation.swift index 9fab3e25..e34090f2 100644 --- a/RxSwift/Schedulers/SchedulerServices+Emulation.swift +++ b/RxSwift/Schedulers/SchedulerServices+Emulation.swift @@ -11,13 +11,12 @@ import Foundation enum SchedulePeriodicRecursiveCommand { case Tick case DispatchStart - case DispatchEnd } class SchedulePeriodicRecursive { typealias RecursiveAction = State -> State typealias TimeInterval = S.TimeInterval - typealias RecursiveScheduler = RecursiveSchedulerOf + typealias RecursiveScheduler = AnyRecursiveScheduler let scheduler: S let startAfter: TimeInterval @@ -40,23 +39,26 @@ class SchedulePeriodicRecursive { } func tick(command: SchedulePeriodicRecursiveCommand, scheduler: RecursiveScheduler) -> Void { + // Tries to emulate periodic scheduling as best as possible. + // The problem that could arise is if handling periodic ticks take too long, or + // tick interval is short. switch command { case .Tick: scheduler.schedule(.Tick, dueTime: self.period) - + + // The idea is that if on tick there wasn't any item enqueued, schedule to perform work immediatelly. + // Else work will be scheduled after previous enqueued work completes. if OSAtomicIncrement32(&pendingTickCount) == 1 { self.tick(.DispatchStart, scheduler: scheduler) } - break + case .DispatchStart: self.state = action(state) - scheduler.schedule(SchedulePeriodicRecursiveCommand.DispatchEnd) - break - case .DispatchEnd: + // Start work and schedule check is this last batch of work if OSAtomicDecrement32(&pendingTickCount) > 0 { + // This gives priority to scheduler emulation, it's not perfect, but helps scheduler.schedule(SchedulePeriodicRecursiveCommand.DispatchStart) } - break } } } \ No newline at end of file diff --git a/RxTests/RxSwiftTests/TestImplementations/Schedulers/VirtualTimeSchedulerBase.swift b/RxTests/RxSwiftTests/TestImplementations/Schedulers/VirtualTimeSchedulerBase.swift index 9ffd556a..213edac1 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Schedulers/VirtualTimeSchedulerBase.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Schedulers/VirtualTimeSchedulerBase.swift @@ -104,32 +104,6 @@ class VirtualTimeSchedulerBase : SchedulerType, CustomStringConvertible { return compositeDisposable } - func schedulePeriodic(state: StateType, startAfter: TimeInterval, period: TimeInterval, action: (StateType) -> StateType) -> Disposable { - let compositeDisposable = CompositeDisposable() - - let scheduleTime: Int - if startAfter <= 0 { - scheduleTime = self.now + 1 - } - else { - scheduleTime = self.now + startAfter - } - - let item = ScheduledItem(action: { [unowned self] state in - if compositeDisposable.disposed { - return NopDisposable.instance - } - let nextState = action(state) - return self.schedulePeriodic(nextState, startAfter: period, period: period, action: action) - }, state: state, time: scheduleTime) - - schedulerQueue.append(item) - - compositeDisposable.addDisposable(item) - - return compositeDisposable - } - func start() { if !enabled { enabled = true From aa730e665edab42764ef71464400a5f90ed15e14 Mon Sep 17 00:00:00 2001 From: yury Date: Sun, 18 Oct 2015 14:44:34 +0300 Subject: [PATCH 050/210] Add Using operator tests --- .../Observables/Implementations/Using.swift | 4 +- .../Mocks/MockDisposable.swift | 25 ++ .../Tests/Observable+CreationTest.swift | 244 ++++++++++++++++++ RxTests/RxTests.xcodeproj/project.pbxproj | 4 + 4 files changed, 275 insertions(+), 2 deletions(-) create mode 100644 RxTests/RxSwiftTests/TestImplementations/Mocks/MockDisposable.swift diff --git a/RxSwift/Observables/Implementations/Using.swift b/RxSwift/Observables/Implementations/Using.swift index 6dac4155..92f8f87b 100644 --- a/RxSwift/Observables/Implementations/Using.swift +++ b/RxSwift/Observables/Implementations/Using.swift @@ -26,7 +26,7 @@ class UsingSink: Producer { typealias E = SourceType typealias ResourceFactory = () throws -> ResourceType - typealias ObservableFactory = (resource: ResourceType) throws -> Observable + typealias ObservableFactory = ResourceType throws -> Observable private let _resourceFactory: ResourceFactory private let _observableFactory: ObservableFactory diff --git a/RxTests/RxSwiftTests/TestImplementations/Mocks/MockDisposable.swift b/RxTests/RxSwiftTests/TestImplementations/Mocks/MockDisposable.swift new file mode 100644 index 00000000..e0574136 --- /dev/null +++ b/RxTests/RxSwiftTests/TestImplementations/Mocks/MockDisposable.swift @@ -0,0 +1,25 @@ +// +// MockDisposable.swift +// RxTests +// +// Created by Yury Korolev on 10/17/15. +// +// + +import Foundation +import RxSwift + +class MockDisposable : Disposable +{ + var ticks = [Int]() + private let _scheduler: TestScheduler + + init(scheduler: TestScheduler) { + _scheduler = scheduler + ticks.append(_scheduler.now) + } + + func dispose() { + ticks.append(_scheduler.now) + } +} \ No newline at end of file diff --git a/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift b/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift index c9d30db7..26726c7a 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift @@ -148,4 +148,248 @@ extension ObservableCreationTests { next(206, 42) ]) } +} + +// using +extension ObservableCreationTests { + func testUsing_Null() { + let scheduler = TestScheduler(initialClock: 0) + + var disposeInvoked = 0 + var createInvoked = 0 + + var xs:ColdObservable = scheduler.createColdObservable([]) + var disposable = MockDisposable(scheduler: scheduler) + + var _d = MockDisposable(scheduler: scheduler) + + let res = scheduler.start { + using({ () -> MockDisposable in + disposeInvoked += 1 + disposable = MockDisposable(scheduler: scheduler) + return disposable + }, observableFactory: { d in + _d = d + createInvoked += 1 + xs = scheduler.createColdObservable([ + next(100, scheduler.clock), + completed(200) + ]) + return xs.asObservable() + }) as Observable + } + + XCTAssert(disposable === _d) + + XCTAssertEqual(res.messages, [ + next(300, 200), + completed(400) + ]) + + XCTAssertEqual(1, createInvoked) + XCTAssertEqual(1, disposeInvoked) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 400) + ]) + + // TODO: Assert.IsNull(disposable); + } + + func testUsing_Complete() { + let scheduler = TestScheduler(initialClock: 0) + + var disposeInvoked = 0 + var createInvoked = 0 + + var xs:ColdObservable = scheduler.createColdObservable([]) + var disposable = MockDisposable(scheduler: scheduler) + + var _d = MockDisposable(scheduler: scheduler) + + let res = scheduler.start { + using({ () -> MockDisposable in + disposeInvoked += 1 + disposable = MockDisposable(scheduler: scheduler) + return disposable + }, observableFactory: { d in + _d = d + createInvoked += 1 + xs = scheduler.createColdObservable([ + next(100, scheduler.clock), + completed(200) + ]) + return xs.asObservable() + }) as Observable + } + + XCTAssert(disposable === _d) + + XCTAssertEqual(res.messages, [ + next(300, 200), + completed(400) + ]) + + XCTAssertEqual(1, createInvoked) + XCTAssertEqual(1, disposeInvoked) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 400) + ]) + + XCTAssertEqual(disposable.ticks, [ + 200, + 400 + ]) + } + + func testUsing_Error() { + let scheduler = TestScheduler(initialClock: 0) + + var disposeInvoked = 0 + var createInvoked = 0 + + var xs:ColdObservable = scheduler.createColdObservable([]) + var disposable = MockDisposable(scheduler: scheduler) + + var _d = MockDisposable(scheduler: scheduler) + + let res = scheduler.start { + using({ () -> MockDisposable in + disposeInvoked += 1 + disposable = MockDisposable(scheduler: scheduler) + return disposable + }, observableFactory: { d in + _d = d + createInvoked += 1 + xs = scheduler.createColdObservable([ + next(100, scheduler.clock), + error(200, testError) + ]) + return xs.asObservable() + }) as Observable + } + + XCTAssert(disposable === _d) + + XCTAssertEqual(res.messages, [ + next(300, 200), + error(400, testError) + ]) + + XCTAssertEqual(1, createInvoked) + XCTAssertEqual(1, disposeInvoked) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 400) + ]) + + XCTAssertEqual(disposable.ticks, [ + 200, + 400 + ]) + } + + func testUsing_Dispose() { + let scheduler = TestScheduler(initialClock: 0) + + var disposeInvoked = 0 + var createInvoked = 0 + + var xs:ColdObservable = scheduler.createColdObservable([]) + var disposable = MockDisposable(scheduler: scheduler) + + var _d = MockDisposable(scheduler: scheduler) + + let res = scheduler.start { + using({ () -> MockDisposable in + disposeInvoked += 1 + disposable = MockDisposable(scheduler: scheduler) + return disposable + }, observableFactory: { d in + _d = d + createInvoked += 1 + xs = scheduler.createColdObservable([ + next(100, scheduler.clock), + next(1000, scheduler.clock + 1) + ]) + return xs.asObservable() + }) as Observable + } + + XCTAssert(disposable === _d) + + XCTAssertEqual(res.messages, [ + next(300, 200), + ]) + + XCTAssertEqual(1, createInvoked) + XCTAssertEqual(1, disposeInvoked) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 1000) + ]) + + XCTAssertEqual(disposable.ticks, [ + 200, + 1000 + ]) + } + + func testUsing_ThrowResourceSelector() { + let scheduler = TestScheduler(initialClock: 0) + + var disposeInvoked = 0 + var createInvoked = 0 + + let res = scheduler.start { + using({ () -> MockDisposable in + disposeInvoked += 1 + throw testError + }, observableFactory: { d in + createInvoked += 1 + return never() + + }) as Observable + } + + XCTAssertEqual(res.messages, [ + error(200, testError), + ]) + + XCTAssertEqual(0, createInvoked) + XCTAssertEqual(1, disposeInvoked) + } + + func testUsing_ThrowResourceUsage() { + let scheduler = TestScheduler(initialClock: 0) + + var disposeInvoked = 0 + var createInvoked = 0 + var disposable = MockDisposable(scheduler: scheduler) + + let res = scheduler.start { + using({ () -> MockDisposable in + disposeInvoked += 1 + disposable = MockDisposable(scheduler: scheduler) + return disposable + }, observableFactory: { d in + createInvoked += 1 + throw testError + + }) as Observable + } + + XCTAssertEqual(res.messages, [ + error(200, testError), + ]) + + XCTAssertEqual(1, createInvoked) + XCTAssertEqual(1, disposeInvoked) + + XCTAssertEqual(disposable.ticks, [ + 200, + 200 + ]) + } } \ No newline at end of file diff --git a/RxTests/RxTests.xcodeproj/project.pbxproj b/RxTests/RxTests.xcodeproj/project.pbxproj index 4cad61fa..cf193c36 100644 --- a/RxTests/RxTests.xcodeproj/project.pbxproj +++ b/RxTests/RxTests.xcodeproj/project.pbxproj @@ -124,6 +124,7 @@ D203C4F01BB9C22800D02D00 /* NSObject+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81CC92A1B513FD400915606 /* NSObject+RxTests.swift */; }; D203C5141BB9C54A00D02D00 /* UIControl+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E5D10BA1B48355200432B25 /* UIControl+RxTests.swift */; }; D251ED291BB9BF90002D0E36 /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468CC1B8A897800BF917B /* RxCocoa.framework */; }; + D2AF91971BD2EBB900A008C1 /* MockDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AF91961BD2EBB900A008C1 /* MockDisposable.swift */; }; D2EBEB521BB9B7CC003A27DC /* ColdObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81108301AF50E2A001C13E4 /* ColdObservable.swift */; }; D2EBEB531BB9B7CC003A27DC /* TestConnectableObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81108311AF50E2A001C13E4 /* TestConnectableObservable.swift */; }; D2EBEB541BB9B7CC003A27DC /* HotObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81108321AF50E2A001C13E4 /* HotObservable.swift */; }; @@ -230,6 +231,7 @@ C8E3812A1B2083C2008CDC33 /* PrimitiveMockObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrimitiveMockObserver.swift; sourceTree = ""; }; C8EA2D361BD02E1900FB22AC /* EquatableArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EquatableArray.swift; sourceTree = ""; }; C8FDC5F71B2B5B7E0065F8D9 /* ElementIndexPair.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElementIndexPair.swift; sourceTree = ""; }; + D2AF91961BD2EBB900A008C1 /* MockDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockDisposable.swift; sourceTree = ""; }; D2EBEB491BB9B7AE003A27DC /* RxTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RxTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -367,6 +369,7 @@ C8E381271B207D03008CDC33 /* PrimitiveHotObservable.swift */, C8E3812A1B2083C2008CDC33 /* PrimitiveMockObserver.swift */, C80DDEDF1BCEE898006A1832 /* MainThreadPrimitiveHotObservable.swift */, + D2AF91961BD2EBB900A008C1 /* MockDisposable.swift */, ); path = Mocks; sourceTree = ""; @@ -608,6 +611,7 @@ C81108651AF50E2A001C13E4 /* Observable+SingleTest.swift in Sources */, 5E5D10BB1B48355200432B25 /* UIControl+RxTests.swift in Sources */, C897EC4A1B1123DA009C2CB0 /* Observable+MultipleTest+Zip.swift in Sources */, + D2AF91971BD2EBB900A008C1 /* MockDisposable.swift in Sources */, C81108571AF50E2A001C13E4 /* Recorded.swift in Sources */, C801EB5A1B97951100C4D8C4 /* Observable+CreationTest.swift in Sources */, C81108591AF50E2A001C13E4 /* TestScheduler.swift in Sources */, From bd7d28855bdf7dd74c030833ddb031f4027529fd Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 18 Oct 2015 14:53:49 +0200 Subject: [PATCH 051/210] Removes version from `Queue`. --- RxSwift/DataStructures/Queue.swift | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/RxSwift/DataStructures/Queue.swift b/RxSwift/DataStructures/Queue.swift index 8545acb4..b02351a7 100644 --- a/RxSwift/DataStructures/Queue.swift +++ b/RxSwift/DataStructures/Queue.swift @@ -28,8 +28,7 @@ public struct Queue: SequenceType { private var _count: Int private var pushNextIndex: Int private var initialCapacity: Int - private var version: Int - + /** Creates new queue. @@ -38,7 +37,6 @@ public struct Queue: SequenceType { public init(capacity: Int) { initialCapacity = capacity - version = 0 _count = 0 pushNextIndex = 0 @@ -111,8 +109,6 @@ public struct Queue: SequenceType { - parameter element: Element to enqueue. */ public mutating func enqueue(element: T) { - version++ - if count == storage.count { resizeTo(max(storage.count, 1) * resizeFactor) } @@ -129,8 +125,6 @@ public struct Queue: SequenceType { private mutating func dequeueElementOnly() -> T { precondition(count > 0) - version++ - let index = dequeueIndex let value = storage[index]! @@ -177,13 +171,7 @@ public struct Queue: SequenceType { var i = dequeueIndex var count = _count - let lastVersion = version - return anyGenerator { - if lastVersion != self.version { - rxFatalError("Collection was modified while enumerated") - } - if count == 0 { return nil } From d959be6345f286795a9be1efba779412b9b3135c Mon Sep 17 00:00:00 2001 From: yury Date: Sun, 18 Oct 2015 15:57:05 +0300 Subject: [PATCH 052/210] Fix Using operator tests --- .../Tests/Observable+CreationTest.swift | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift b/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift index 26726c7a..14bad86b 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift @@ -158,10 +158,9 @@ extension ObservableCreationTests { var disposeInvoked = 0 var createInvoked = 0 - var xs:ColdObservable = scheduler.createColdObservable([]) - var disposable = MockDisposable(scheduler: scheduler) - - var _d = MockDisposable(scheduler: scheduler) + var xs:ColdObservable! + var disposable:MockDisposable! + var _d:MockDisposable! let res = scheduler.start { using({ () -> MockDisposable in @@ -202,10 +201,9 @@ extension ObservableCreationTests { var disposeInvoked = 0 var createInvoked = 0 - var xs:ColdObservable = scheduler.createColdObservable([]) - var disposable = MockDisposable(scheduler: scheduler) - - var _d = MockDisposable(scheduler: scheduler) + var xs:ColdObservable! + var disposable:MockDisposable! + var _d:MockDisposable! let res = scheduler.start { using({ () -> MockDisposable in @@ -249,10 +247,9 @@ extension ObservableCreationTests { var disposeInvoked = 0 var createInvoked = 0 - var xs:ColdObservable = scheduler.createColdObservable([]) - var disposable = MockDisposable(scheduler: scheduler) - - var _d = MockDisposable(scheduler: scheduler) + var xs:ColdObservable! + var disposable:MockDisposable! + var _d:MockDisposable! let res = scheduler.start { using({ () -> MockDisposable in @@ -296,10 +293,9 @@ extension ObservableCreationTests { var disposeInvoked = 0 var createInvoked = 0 - var xs:ColdObservable = scheduler.createColdObservable([]) - var disposable = MockDisposable(scheduler: scheduler) - - var _d = MockDisposable(scheduler: scheduler) + var xs:ColdObservable! + var disposable:MockDisposable! + var _d:MockDisposable! let res = scheduler.start { using({ () -> MockDisposable in @@ -366,7 +362,7 @@ extension ObservableCreationTests { var disposeInvoked = 0 var createInvoked = 0 - var disposable = MockDisposable(scheduler: scheduler) + var disposable:MockDisposable! let res = scheduler.start { using({ () -> MockDisposable in From c66723de70cdca613ad0aeb0d56cd21a6d51ba0a Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 18 Oct 2015 15:05:44 +0200 Subject: [PATCH 053/210] Updates Error documentation. --- RxSwift/Error.swift | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/RxSwift/Error.swift b/RxSwift/Error.swift index acb7afff..3c4a7305 100644 --- a/RxSwift/Error.swift +++ b/RxSwift/Error.swift @@ -13,15 +13,23 @@ let RxCompositeFailures = "RxCompositeFailures" /** Generic Rx error codes. - -- Unknown: Unknown error occured -- Cast: Error during Casting -- Disposed: Performing an action on disposed object */ public enum RxErrorCode : Int { + /** + Unknown error occured + */ case Unknown = 0 + /** + Casting error + */ case Cast = 2 + /** + Performing an action on disposed object + */ case Disposed = 3 + /** + Aritmetic overflow error. + */ case Overflow = 4 } @@ -30,20 +38,23 @@ Singleton instances of RxErrors */ public struct RxError { /** - Singleton instance of Unknown Error + Singleton instance of unknown Error */ public static let UnknownError = NSError(domain: RxErrorDomain, code: RxErrorCode.Unknown.rawValue, userInfo: nil) /** - Singleton instance of error during casting + Singleton instance of error during casting. */ public static let CastError = NSError(domain: RxErrorDomain, code: RxErrorCode.Cast.rawValue, userInfo: nil) /** - Singleton instance of doing something on a disposed object + Singleton instance of doing something on a disposed object error. */ public static let DisposedError = NSError(domain: RxErrorDomain, code: RxErrorCode.Disposed.rawValue, userInfo: nil) - + + /** + Singleton instance of aritmetic overflow error. + */ public static let OverflowError = NSError(domain: RxErrorDomain, code: RxErrorCode.Overflow.rawValue, userInfo: nil) } \ No newline at end of file From 3269f482c922aa91e4a2daf9af1028b6788a6018 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 18 Oct 2015 15:24:53 +0200 Subject: [PATCH 054/210] Adds `concat` overload. --- .../Examples/GitHubSignup/GitHubAPI/GitHubAPI.swift | 4 ++-- RxSwift/Observables/Observable+Multiple.swift | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/RxExample/RxExample/Examples/GitHubSignup/GitHubAPI/GitHubAPI.swift b/RxExample/RxExample/Examples/GitHubSignup/GitHubAPI/GitHubAPI.swift index 3d4aba2d..fae3424b 100644 --- a/RxExample/RxExample/Examples/GitHubSignup/GitHubAPI/GitHubAPI.swift +++ b/RxExample/RxExample/Examples/GitHubSignup/GitHubAPI/GitHubAPI.swift @@ -61,8 +61,8 @@ class GitHubAPI { func signup(username: String, password: String) -> Observable { // this is also just a mock let signupResult = SignupState.SignedUp(signedUp: arc4random() % 5 == 0 ? false : true) - return [just(signupResult), never()] - .concat() + return just(signupResult) + .concat(never()) .throttle(2, MainScheduler.sharedInstance) .startWith(SignupState.SigningUp) } diff --git a/RxSwift/Observables/Observable+Multiple.swift b/RxSwift/Observables/Observable+Multiple.swift index a5b6e23b..6cb14adb 100644 --- a/RxSwift/Observables/Observable+Multiple.swift +++ b/RxSwift/Observables/Observable+Multiple.swift @@ -58,6 +58,19 @@ extension ObservableType where E : ObservableConvertibleType { // concat +extension ObservableType { + + /** + Concatenates the second observable sequence to `self` upon successful termination of `self`. + + - parameter second: Second observable sequence. + - returns: An observable sequence that contains the elements of `self`, followed by those of the second sequence. + */ + public func concat(second: O) -> Observable { + return [self.asObservable(), second.asObservable()].concat() + } +} + extension SequenceType where Generator.Element : ObservableConvertibleType { /** From 8116879d0b19cf61e4b9829eabe3e2bad53110cb Mon Sep 17 00:00:00 2001 From: yury Date: Sun, 18 Oct 2015 16:33:07 +0300 Subject: [PATCH 055/210] Add docs --- RxSwift/Observables/Observable+Creation.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/RxSwift/Observables/Observable+Creation.swift b/RxSwift/Observables/Observable+Creation.swift index 86d5c35c..d4f50390 100644 --- a/RxSwift/Observables/Observable+Creation.swift +++ b/RxSwift/Observables/Observable+Creation.swift @@ -160,6 +160,13 @@ public func repeatElement(element: E, _ scheduler: ImmediateSchedulerType) -> return RepeatElement(element: element, scheduler: scheduler) } +/** +Constructs an observable sequence that depends on a resource object, whose lifetime is tied to the resulting observable sequence's lifetime. + +- parameter resourceFactory: Factory function to obtain a resource object. +- parameter observableFactory: Factory function to obtain an observable sequence that depends on the obtained resource. +- returns: An observable sequence whose lifetime controls the lifetime of the dependent resource object. +*/ public func using(resourceFactory: () throws -> R, observableFactory: R throws -> Observable) -> Observable { return Using(resourceFactory: resourceFactory, observableFactory: observableFactory) } From 8b6321e07a3165cffdb056d422dab35b7a5fa6d4 Mon Sep 17 00:00:00 2001 From: yury Date: Sun, 18 Oct 2015 16:43:53 +0300 Subject: [PATCH 056/210] Add Using to RxExample-iOS-no-module target --- RxExample/RxExample.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index 881be400..856e3513 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -305,7 +305,7 @@ C8DF92EA1B0B38C0009BCF9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; }; C8DF92EB1B0B38C0009BCF9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; }; C8DF92F61B0B43A4009BCF9A /* IntroductionExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DF92F51B0B43A4009BCF9A /* IntroductionExampleViewController.swift */; }; - D2AF91891BD2C51900A008C1 /* Using.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AF91881BD2C51900A008C1 /* Using.swift */; }; + D2AF91981BD3D95900A008C1 /* Using.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AF91881BD2C51900A008C1 /* Using.swift */; }; EC91FB951BBA144400973245 /* GitHubSearchRepositoriesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC91FB941BBA144400973245 /* GitHubSearchRepositoriesViewController.swift */; }; /* End PBXBuildFile section */ @@ -1766,6 +1766,7 @@ C89465641BC6C2BC0055219D /* _RXSwizzling.m in Sources */, C89464CA1BC6C2B00055219D /* Empty.swift in Sources */, C89464C61BC6C2B00055219D /* Deferred.swift in Sources */, + D2AF91981BD3D95900A008C1 /* Using.swift in Sources */, C8297E501B6CF905000589EA /* TableViewController.swift in Sources */, C8297E511B6CF905000589EA /* PartialUpdatesViewController.swift in Sources */, C894649F1BC6C2B00055219D /* AsyncLock.swift in Sources */, @@ -1808,7 +1809,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D2AF91891BD2C51900A008C1 /* Using.swift in Sources */, 0706E19F1B17703E00BA2D3A /* RandomUserAPI.swift in Sources */, C86E2F3E1AE5A0CA00C31024 /* SearchResultViewModel.swift in Sources */, C83367241AD029AE00C668A7 /* HtmlParsing.swift in Sources */, From 1adceaf8b86274e6c71a7ed22d174ff108742cae Mon Sep 17 00:00:00 2001 From: yury Date: Sun, 18 Oct 2015 16:44:55 +0300 Subject: [PATCH 057/210] Add MockDisposable to all test targets --- RxTests/RxTests.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RxTests/RxTests.xcodeproj/project.pbxproj b/RxTests/RxTests.xcodeproj/project.pbxproj index cf193c36..1ba1677b 100644 --- a/RxTests/RxTests.xcodeproj/project.pbxproj +++ b/RxTests/RxTests.xcodeproj/project.pbxproj @@ -125,6 +125,8 @@ D203C5141BB9C54A00D02D00 /* UIControl+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E5D10BA1B48355200432B25 /* UIControl+RxTests.swift */; }; D251ED291BB9BF90002D0E36 /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468CC1B8A897800BF917B /* RxCocoa.framework */; }; D2AF91971BD2EBB900A008C1 /* MockDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AF91961BD2EBB900A008C1 /* MockDisposable.swift */; }; + D2AF91991BD3D9C600A008C1 /* MockDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AF91961BD2EBB900A008C1 /* MockDisposable.swift */; }; + D2AF919A1BD3D9C700A008C1 /* MockDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AF91961BD2EBB900A008C1 /* MockDisposable.swift */; }; D2EBEB521BB9B7CC003A27DC /* ColdObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81108301AF50E2A001C13E4 /* ColdObservable.swift */; }; D2EBEB531BB9B7CC003A27DC /* TestConnectableObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81108311AF50E2A001C13E4 /* TestConnectableObservable.swift */; }; D2EBEB541BB9B7CC003A27DC /* HotObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81108321AF50E2A001C13E4 /* HotObservable.swift */; }; @@ -660,6 +662,7 @@ C88BB8A01B07E64B0064D411 /* Observable+StandardSequenceOperatorsTest.swift in Sources */, C88BB8A11B07E64B0064D411 /* Observable+MultipleTest.swift in Sources */, C81CC92C1B513FD400915606 /* NSObject+RxTests.swift in Sources */, + D2AF91991BD3D9C600A008C1 /* MockDisposable.swift in Sources */, C8B605ED1B6260A10044410E /* AnonymousObservable+Test.swift in Sources */, C801EB5B1B97951100C4D8C4 /* Observable+CreationTest.swift in Sources */, C88BB8A21B07E64B0064D411 /* ColdObservable.swift in Sources */, @@ -733,6 +736,7 @@ D2EBEB7A1BB9B804003A27DC /* PerformanceTools.swift in Sources */, D2EBEB651BB9B7EF003A27DC /* BagTest.swift in Sources */, D2EBEB561BB9B7CC003A27DC /* MySubject.swift in Sources */, + D2AF919A1BD3D9C700A008C1 /* MockDisposable.swift in Sources */, D2EBEB761BB9B7F9003A27DC /* QueueTests.swift in Sources */, D2EBEB711BB9B7EF003A27DC /* Observable+MultipleTest+CombineLatest.swift in Sources */, D203C4EF1BB9C22800D02D00 /* RxCocoaTests.swift in Sources */, From f68ab7426945cf7a12830015cfbecae3c497d3c5 Mon Sep 17 00:00:00 2001 From: yury Date: Sun, 18 Oct 2015 17:07:49 +0300 Subject: [PATCH 058/210] Remove Using_Null test --- .../Tests/Observable+CreationTest.swift | 43 ------------------- 1 file changed, 43 deletions(-) diff --git a/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift b/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift index 14bad86b..2e671a9e 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift @@ -152,49 +152,6 @@ extension ObservableCreationTests { // using extension ObservableCreationTests { - func testUsing_Null() { - let scheduler = TestScheduler(initialClock: 0) - - var disposeInvoked = 0 - var createInvoked = 0 - - var xs:ColdObservable! - var disposable:MockDisposable! - var _d:MockDisposable! - - let res = scheduler.start { - using({ () -> MockDisposable in - disposeInvoked += 1 - disposable = MockDisposable(scheduler: scheduler) - return disposable - }, observableFactory: { d in - _d = d - createInvoked += 1 - xs = scheduler.createColdObservable([ - next(100, scheduler.clock), - completed(200) - ]) - return xs.asObservable() - }) as Observable - } - - XCTAssert(disposable === _d) - - XCTAssertEqual(res.messages, [ - next(300, 200), - completed(400) - ]) - - XCTAssertEqual(1, createInvoked) - XCTAssertEqual(1, disposeInvoked) - - XCTAssertEqual(xs.subscriptions, [ - Subscription(200, 400) - ]) - - // TODO: Assert.IsNull(disposable); - } - func testUsing_Complete() { let scheduler = TestScheduler(initialClock: 0) From a991cad152042d4e890ebcb44d2aae0e3a3fa53b Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 18 Oct 2015 17:19:50 +0200 Subject: [PATCH 059/210] Adds `ActivityIndicator`. --- RxCocoa/Common/CocoaUnits/Driver/Driver.swift | 6 + RxExample/RxExample.xcodeproj/project.pbxproj | 12 ++ .../GitHubSearchRepositoriesAPI.swift | 175 ++++++++++++++++++ ...tHubSearchRepositoriesViewController.swift | 167 +---------------- .../Views/GitHubSignupViewController.swift | 1 + .../Services/ActivityIndicator.swift | 83 +++++++++ .../RxExample/iOS/RootViewController.swift | 5 + 7 files changed, 289 insertions(+), 160 deletions(-) create mode 100644 RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift create mode 100644 RxExample/RxExample/Services/ActivityIndicator.swift diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver.swift index 165dd601..429ac81d 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/Driver.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver.swift @@ -22,6 +22,12 @@ public protocol DriverConvertibleType : ObservableConvertibleType { func asDriver() -> Driver } +extension DriverConvertibleType { + public func asObservable() -> Observable { + return asDriver().asObservable() + } +} + /** Unit that represents observable sequence with following properties: diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index 856e3513..b79c2a5b 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -17,6 +17,10 @@ 07E300071B14995F00F00100 /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E300061B14995F00F00100 /* TableViewController.swift */; }; 07E300091B149A2A00F00100 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E300081B149A2A00F00100 /* User.swift */; }; 07E3C2331B03605B0010338D /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E3C2321B03605B0010338D /* Dependencies.swift */; }; + C803973A1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; }; + C803973B1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; }; + C80397491BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */; }; + C803974A1BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */; }; C80DDE881BCDAA0F006A1832 /* SkipWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE7A1BCDA952006A1832 /* SkipWhile.swift */; }; C80DDED21BCE9046006A1832 /* ControlEvent+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC21BCE9041006A1832 /* ControlEvent+Driver.swift */; }; C80DDED31BCE9046006A1832 /* ControlProperty+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC31BCE9041006A1832 /* ControlProperty+Driver.swift */; }; @@ -442,6 +446,8 @@ 07E300061B14995F00F00100 /* TableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; }; 07E300081B149A2A00F00100 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 07E3C2321B03605B0010338D /* Dependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Dependencies.swift; path = Examples/Dependencies.swift; sourceTree = ""; }; + C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = ""; }; + C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubSearchRepositoriesAPI.swift; sourceTree = ""; }; C80DDE7A1BCDA952006A1832 /* SkipWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipWhile.swift; sourceTree = ""; }; C80DDEC21BCE9041006A1832 /* ControlEvent+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControlEvent+Driver.swift"; sourceTree = ""; }; C80DDEC31BCE9041006A1832 /* ControlProperty+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControlProperty+Driver.swift"; sourceTree = ""; }; @@ -859,6 +865,7 @@ C83367121AD029AE00C668A7 /* ImageService.swift */, C8A2A2C71B4049E300F11F09 /* PseudoRandomGenerator.swift */, C8A2A2CA1B404A1200F11F09 /* Randomizer.swift */, + C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */, ); path = Services; sourceTree = ""; @@ -1349,6 +1356,7 @@ isa = PBXGroup; children = ( EC91FB941BBA144400973245 /* GitHubSearchRepositoriesViewController.swift */, + C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */, ); name = "08 AutoLoading"; path = AutoLoading; @@ -1713,6 +1721,7 @@ C89465841BC6C2BC0055219D /* RxActionSheetDelegateProxy.swift in Sources */, C89464F51BC6C2B00055219D /* AnyObserver.swift in Sources */, C89464D71BC6C2B00055219D /* Range.swift in Sources */, + C803974A1BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift in Sources */, C8297E431B6CF905000589EA /* GitHubAPI.swift in Sources */, C89464EB1BC6C2B00055219D /* Observable+Aggregate.swift in Sources */, C8297E441B6CF905000589EA /* PseudoRandomGenerator.swift in Sources */, @@ -1765,6 +1774,7 @@ C8297E4F1B6CF905000589EA /* Wireframe.swift in Sources */, C89465641BC6C2BC0055219D /* _RXSwizzling.m in Sources */, C89464CA1BC6C2B00055219D /* Empty.swift in Sources */, + C803973B1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */, C89464C61BC6C2B00055219D /* Deferred.swift in Sources */, D2AF91981BD3D95900A008C1 /* Using.swift in Sources */, C8297E501B6CF905000589EA /* TableViewController.swift in Sources */, @@ -1825,12 +1835,14 @@ C84B913B1B8A282000C9CCCF /* RxCollectionViewSectionedReloadDataSource.swift in Sources */, C88C78731B3EB0A00061C5AB /* SectionModel.swift in Sources */, C86E2F3F1AE5A0CA00C31024 /* SearchViewModel.swift in Sources */, + C803973A1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */, C84B913D1B8A282000C9CCCF /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */, C8A750201B94E78200D8D046 /* RxDataSourceStarterKit.swift in Sources */, 0706E1961B14AF5100BA2D3A /* DetailViewController.swift in Sources */, C88C78991B4012A90061C5AB /* SectionModelType.swift in Sources */, C83367251AD029AE00C668A7 /* ImageService.swift in Sources */, C86E2F471AE5A0CA00C31024 /* WikipediaSearchResult.swift in Sources */, + C80397491BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift in Sources */, C890A65A1AEBD28A00AFF7E6 /* GitHubAPI.swift in Sources */, EC91FB951BBA144400973245 /* GitHubSearchRepositoriesViewController.swift in Sources */, C8A2A2C81B4049E300F11F09 /* PseudoRandomGenerator.swift in Sources */, diff --git a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift new file mode 100644 index 00000000..71371cf8 --- /dev/null +++ b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift @@ -0,0 +1,175 @@ +// +// GitHubSearchRepositoriesAPI.swift +// RxExample +// +// Created by Krunoslav Zaher on 10/18/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +import RxSwift + +struct Repository: CustomStringConvertible { + var name: String + var url: String + + init(name: String, url: String) { + self.name = name + self.url = url + } + + var description: String { + return "\(name) | \(url)" + } +} + +enum SearchRepositoryResponse { + case Repositories([Repository]) + case LimitExceeded +} + + +class GitHubSearchRepositoriesAPI { + + static let sharedAPI = GitHubSearchRepositoriesAPI() + + let activityIndicator = ActivityIndicator() + + private init() { + } + + private static let parseLinksPattern = "\\s*,?\\s*<([^\\>]*)>\\s*;\\s*rel=\"([^\"]*)\"" + private static let linksRegex = try! NSRegularExpression(pattern: parseLinksPattern, options: [.AllowCommentsAndWhitespace]) + + private static func parseLinks(links: String) throws -> [String: String] { + + let length = (links as NSString).length + let matches = GitHubSearchRepositoriesAPI.linksRegex.matchesInString(links, options: NSMatchingOptions(), range: NSRange(location: 0, length: length)) + + var result: [String: String] = [:] + + for m in matches { + let matches = (1 ..< m.numberOfRanges).map { rangeIndex -> String in + let range = m.rangeAtIndex(rangeIndex) + let startIndex = links.startIndex.advancedBy(range.location) + let endIndex = startIndex.advancedBy(range.length) + let stringRange = Range(start: startIndex, end: endIndex) + return links.substringWithRange(stringRange) + } + + if matches.count != 2 { + throw exampleError("Error parsing links") + } + + result[matches[1]] = matches[0] + } + + return result + } + + private static func parseNextURL(httpResponse: NSHTTPURLResponse) throws -> NSURL? { + guard let serializedLinks = httpResponse.allHeaderFields["Link"] as? String else { + return nil + } + + let links = try GitHubSearchRepositoriesAPI.parseLinks(serializedLinks) + + guard let nextPageURL = links["next"] else { + return nil + } + + guard let nextUrl = NSURL(string: nextPageURL) else { + throw exampleError("Error parsing next url `\(nextPageURL)`") + } + + return nextUrl + } + + /** + Public fascade for search. + */ + func search(query: String, loadNextPageTrigger: Observable) -> Observable { + let escapedQuery = URLEscape(query) + let url = NSURL(string: "https://api.github.com/search/repositories?q=\(escapedQuery)")! + return recursivelySearch([], loadNextURL: url, loadNextPageTrigger: loadNextPageTrigger) + .startWith(.Repositories([])) + } + + private func recursivelySearch(loadedSoFar: [Repository], loadNextURL: NSURL, loadNextPageTrigger: Observable) -> Observable { + return loadSearchURL(loadNextURL) + .retry(3) + .flatMap { (newPageRepositoriesResponse, nextURL) -> Observable in + // in case access denied, just stop + guard case .Repositories(let newPageRepositories) = newPageRepositoriesResponse else { + return just(newPageRepositoriesResponse) + } + + var loadedRepositories = loadedSoFar + loadedRepositories.appendContentsOf(newPageRepositories) + + // if next page can't be loaded, just return what was loaded, and stop + guard let nextURL = nextURL else { + return just(.Repositories(loadedRepositories)) + } + + return [ + // return loaded immediately + just(.Repositories(loadedRepositories)), + // wait until next page can be loaded + never().takeUntil(loadNextPageTrigger), + // load next page + self.recursivelySearch(loadedRepositories, loadNextURL: nextURL, loadNextPageTrigger: loadNextPageTrigger) + ].concat() + } + } + + private func loadSearchURL(searchURL: NSURL) -> Observable<(response: SearchRepositoryResponse, nextURL: NSURL?)> { + return NSURLSession.sharedSession() + .rx_response(NSURLRequest(URL: searchURL)) + .trackActivity(self.activityIndicator) + .observeOn(Dependencies.sharedDependencies.backgroundWorkScheduler) + .map { data, response in + guard let httpResponse = response as? NSHTTPURLResponse else { + throw exampleError("not getting http response") + } + + if httpResponse.statusCode == 403 { + return (response: .LimitExceeded, nextURL: nil) + } + + let jsonRoot = try GitHubSearchRepositoriesAPI.parseJSON(httpResponse, data: data) + + guard let json = jsonRoot as? [String: AnyObject] else { + throw exampleError("Casting to dictionary failed") + } + + let repositories = try GitHubSearchRepositoriesAPI.parseRepositories(json) + + let nextURL = try GitHubSearchRepositoriesAPI.parseNextURL(httpResponse) + + return (response: .Repositories(repositories), nextURL: nextURL) + } + .observeOn(Dependencies.sharedDependencies.mainScheduler) + } + + private static func parseJSON(httpResponse: NSHTTPURLResponse, data: NSData) throws -> AnyObject { + if !(200 ..< 300 ~= httpResponse.statusCode) { + throw exampleError("Call failed") + } + + return try NSJSONSerialization.JSONObjectWithData(data ?? NSData(), options: []) + } + + private static func parseRepositories(json: [String: AnyObject]) throws -> [Repository] { + guard let items = json["items"] as? [[String: AnyObject]] else { + throw exampleError("Can't find items") + } + return try items.map { item in + guard let name = item["name"] as? String, + url = item["url"] as? String else { + throw exampleError("Can't parse repository") + } + return Repository(name: name, url: url) + } + } +} diff --git a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift index 5852a51c..16343bde 100644 --- a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift +++ b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift @@ -12,166 +12,6 @@ import RxSwift import RxCocoa #endif -struct Repository: CustomStringConvertible { - var name: String - var url: String - - init(name: String, url: String) { - self.name = name - self.url = url - } - - var description: String { - return "\(name) | \(url)" - } -} - -enum SearchRepositoryResponse { - case Repositories([Repository]) - case LimitExceeded -} - -class GitHubSearchRepositoriesAPI { - - static let sharedAPI = GitHubSearchRepositoriesAPI() - - private init() {} - - private static let parseLinksPattern = "\\s*,?\\s*<([^\\>]*)>\\s*;\\s*rel=\"([^\"]*)\"" - private static let linksRegex = try! NSRegularExpression(pattern: parseLinksPattern, options: [.AllowCommentsAndWhitespace]) - - private static func parseLinks(links: String) throws -> [String: String] { - - let length = (links as NSString).length - let matches = GitHubSearchRepositoriesAPI.linksRegex.matchesInString(links, options: NSMatchingOptions(), range: NSRange(location: 0, length: length)) - - var result: [String: String] = [:] - - for m in matches { - let matches = (1 ..< m.numberOfRanges).map { rangeIndex -> String in - let range = m.rangeAtIndex(rangeIndex) - let startIndex = links.startIndex.advancedBy(range.location) - let endIndex = startIndex.advancedBy(range.length) - let stringRange = Range(start: startIndex, end: endIndex) - return links.substringWithRange(stringRange) - } - - if matches.count != 2 { - throw exampleError("Error parsing links") - } - - result[matches[1]] = matches[0] - } - - return result - } - - private static func parseNextURL(httpResponse: NSHTTPURLResponse) throws -> NSURL? { - guard let serializedLinks = httpResponse.allHeaderFields["Link"] as? String else { - return nil - } - - let links = try GitHubSearchRepositoriesAPI.parseLinks(serializedLinks) - - guard let nextPageURL = links["next"] else { - return nil - } - - guard let nextUrl = NSURL(string: nextPageURL) else { - throw exampleError("Error parsing next url `\(nextPageURL)`") - } - - return nextUrl - } - - /** - Public fascade for search. - */ - func search(query: String, loadNextPageTrigger: Observable) -> Observable { - let escapedQuery = URLEscape(query) - let url = NSURL(string: "https://api.github.com/search/repositories?q=\(escapedQuery)")! - return recursivelySearch([], loadNextURL: url, loadNextPageTrigger: loadNextPageTrigger) - .startWith(.Repositories([])) - } - - private func recursivelySearch(loadedSoFar: [Repository], loadNextURL: NSURL, loadNextPageTrigger: Observable) -> Observable { - return loadSearchURL(loadNextURL) - .retry(3) - .flatMap { (newPageRepositoriesResponse, nextURL) -> Observable in - // in case access denied, just stop - guard case .Repositories(let newPageRepositories) = newPageRepositoriesResponse else { - return just(newPageRepositoriesResponse) - } - - var loadedRepositories = loadedSoFar - loadedRepositories.appendContentsOf(newPageRepositories) - - // if next page can't be loaded, just return what was loaded, and stop - guard let nextURL = nextURL else { - return just(.Repositories(loadedRepositories)) - } - - return [ - // return loaded immediately - just(.Repositories(loadedRepositories)), - // wait until next page can be loaded - never().takeUntil(loadNextPageTrigger), - // load next page - self.recursivelySearch(loadedRepositories, loadNextURL: nextURL, loadNextPageTrigger: loadNextPageTrigger) - ].concat() - } - } - - private func loadSearchURL(searchURL: NSURL) -> Observable<(response: SearchRepositoryResponse, nextURL: NSURL?)> { - return NSURLSession.sharedSession() - .rx_response(NSURLRequest(URL: searchURL)) - .observeOn(Dependencies.sharedDependencies.backgroundWorkScheduler) - .map { data, response in - guard let httpResponse = response as? NSHTTPURLResponse else { - throw exampleError("not getting http response") - } - - if httpResponse.statusCode == 403 { - return (response: .LimitExceeded, nextURL: nil) - } - - let jsonRoot = try GitHubSearchRepositoriesAPI.parseJSON(httpResponse, data: data) - - guard let json = jsonRoot as? [String: AnyObject] else { - throw exampleError("Casting to dictionary failed") - } - - let repositories = try GitHubSearchRepositoriesAPI.parseRepositories(json) - - let nextURL = try GitHubSearchRepositoriesAPI.parseNextURL(httpResponse) - - return (response: .Repositories(repositories), nextURL: nextURL) - } - .observeOn(Dependencies.sharedDependencies.mainScheduler) - } - - private static func parseJSON(httpResponse: NSHTTPURLResponse, data: NSData) throws -> AnyObject { - if !(200 ..< 300 ~= httpResponse.statusCode) { - throw exampleError("Call failed") - } - - return try NSJSONSerialization.JSONObjectWithData(data ?? NSData(), options: []) - } - - private static func parseRepositories(json: [String: AnyObject]) throws -> [Repository] { - guard let items = json["items"] as? [[String: AnyObject]] else { - throw exampleError("Can't find items") - } - return try items.map { item in - guard let name = item["name"] as? String, - url = item["url"] as? String else { - throw exampleError("Can't parse repository") - } - return Repository(name: name, url: url) - } - } -} - class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegate { static let startLoadingOffset: CGFloat = 20.0 @@ -259,6 +99,13 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat // so normal delegate customization can also be used tableView.rx_setDelegate(self) .addDisposableTo(disposeBag) + + GitHubSearchRepositoriesAPI.sharedAPI.activityIndicator + .asDriver() + .driveNext { active in + UIApplication.sharedApplication().networkActivityIndicatorVisible = active + } + .addDisposableTo(disposeBag) } // MARK: Table view delegate diff --git a/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift b/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift index 00ebe08b..e274091f 100644 --- a/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift +++ b/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift @@ -223,6 +223,7 @@ class GitHubSignupViewController : ViewController { } } .addDisposableTo(disposeBag) + } // This is one of the reasons why it's a good idea for disposal to be detached from allocations. diff --git a/RxExample/RxExample/Services/ActivityIndicator.swift b/RxExample/RxExample/Services/ActivityIndicator.swift new file mode 100644 index 00000000..8a4f9c54 --- /dev/null +++ b/RxExample/RxExample/Services/ActivityIndicator.swift @@ -0,0 +1,83 @@ +// +// ActivityIndicator.swift +// RxExample +// +// Created by Krunoslav Zaher on 10/18/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +import RxSwift +import RxCocoa + +struct ActivityToken : ObservableConvertibleType, Disposable { + private let _source: Observable + private let _dispose: AnonymousDisposable + + init(source: Observable, disposeAction: () -> ()) { + _source = source + _dispose = AnonymousDisposable(disposeAction) + } + + func dispose() { + _dispose.dispose() + } + + func asObservable() -> Observable { + return _source + } +} + +/** +Enables monitoring of sequence computation. + +If there is at least one sequence computation in progress, `true` will be sent. +When all activities complete `false` will be sent. +*/ +class ActivityIndicator : DriverConvertibleType { + typealias E = Bool + + private let _lock = NSRecursiveLock() + private let _variable = Variable(0) + private let _loading: Driver + + init() { + _loading = _variable + .map { $0 > 0 } + .asDriver { (error: ErrorType) -> Driver in + _ = fatalError("Loader can't fail") + return Drive.empty() + } + } + + func trackActivity(source: O) -> Observable { + return using({ () -> ActivityToken in + self.increment() + return ActivityToken(source: source.asObservable(), disposeAction: self.decrement) + }) { t in + return t.asObservable() + } + } + + private func increment() { + _lock.lock() + _variable.value = _variable.value + 1 + _lock.unlock() + } + + private func decrement() { + _lock.lock() + _variable.value = _variable.value - 1 + _lock.unlock() + } + + func asDriver() -> Driver { + return _loading + } +} + +extension ObservableConvertibleType { + func trackActivity(activityIndicator: ActivityIndicator) -> Observable { + return activityIndicator.trackActivity(self) + } +} \ No newline at end of file diff --git a/RxExample/RxExample/iOS/RootViewController.swift b/RxExample/RxExample/iOS/RootViewController.swift index 6a92fa9a..44713fe7 100644 --- a/RxExample/RxExample/iOS/RootViewController.swift +++ b/RxExample/RxExample/iOS/RootViewController.swift @@ -10,4 +10,9 @@ import Foundation import UIKit public class RootViewController : UITableViewController { + public override func viewDidLoad() { + super.viewDidLoad() + // force load + GitHubSearchRepositoriesAPI.sharedAPI.activityIndicator + } } \ No newline at end of file From 1758f9d63a8ca9bb51f02f0a747389fa2749d338 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 18 Oct 2015 17:27:09 +0200 Subject: [PATCH 060/210] Adds `ActivityIndicator` to `RxExample`. --- .../Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift | 2 ++ RxExample/RxExample/Services/ActivityIndicator.swift | 2 ++ 2 files changed, 4 insertions(+) diff --git a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift index 71371cf8..2a131e86 100644 --- a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift +++ b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift @@ -7,7 +7,9 @@ // import Foundation +#if !RX_NO_MODULE import RxSwift +#endif struct Repository: CustomStringConvertible { var name: String diff --git a/RxExample/RxExample/Services/ActivityIndicator.swift b/RxExample/RxExample/Services/ActivityIndicator.swift index 8a4f9c54..f86c5b34 100644 --- a/RxExample/RxExample/Services/ActivityIndicator.swift +++ b/RxExample/RxExample/Services/ActivityIndicator.swift @@ -7,8 +7,10 @@ // import Foundation +#if !RX_NO_MODULE import RxSwift import RxCocoa +#endif struct ActivityToken : ObservableConvertibleType, Disposable { private let _source: Observable From 51696e3a1b8acd8d0328034d1c46d8f3c859ab2a Mon Sep 17 00:00:00 2001 From: yury Date: Sun, 18 Oct 2015 19:04:11 +0300 Subject: [PATCH 061/210] Add MARKs --- RxSwift/Observables/Observable+Aggregate.swift | 2 +- RxSwift/Observables/Observable+Binding.swift | 12 ++++++------ .../Observables/Observable+Concurrency.swift | 4 ++-- RxSwift/Observables/Observable+Creation.swift | 14 +++++++------- RxSwift/Observables/Observable+Debug.swift | 2 +- RxSwift/Observables/Observable+Multiple.swift | 18 +++++++++--------- RxSwift/Observables/Observable+Single.swift | 10 +++++----- .../Observable+StandardSequenceOperators.swift | 14 +++++++------- RxSwift/Observables/Observable+Time.swift | 16 ++++++++-------- 9 files changed, 46 insertions(+), 46 deletions(-) diff --git a/RxSwift/Observables/Observable+Aggregate.swift b/RxSwift/Observables/Observable+Aggregate.swift index efe3ac90..bda2a130 100644 --- a/RxSwift/Observables/Observable+Aggregate.swift +++ b/RxSwift/Observables/Observable+Aggregate.swift @@ -8,7 +8,7 @@ import Foundation -// reduce +// MARK: reduce extension ObservableType { diff --git a/RxSwift/Observables/Observable+Binding.swift b/RxSwift/Observables/Observable+Binding.swift index a3548121..87ee99a8 100644 --- a/RxSwift/Observables/Observable+Binding.swift +++ b/RxSwift/Observables/Observable+Binding.swift @@ -8,7 +8,7 @@ import Foundation -// multicast +// MARK: multicast extension ObservableType { @@ -48,7 +48,7 @@ extension ObservableType { } } -// publish +// MARK: publish extension ObservableType { @@ -64,7 +64,7 @@ extension ObservableType { } } -// replay +// MARK: replay extension ObservableType { @@ -82,7 +82,7 @@ extension ObservableType { } } -// refcount +// MARK: refcount extension ConnectableObservableType { @@ -96,7 +96,7 @@ extension ConnectableObservableType { } } -// share +// MARK: share extension ObservableType { @@ -112,7 +112,7 @@ extension ObservableType { } } -// shareReplay +// MARK: shareReplay extension ObservableType { diff --git a/RxSwift/Observables/Observable+Concurrency.swift b/RxSwift/Observables/Observable+Concurrency.swift index b6959b37..1eab86cf 100644 --- a/RxSwift/Observables/Observable+Concurrency.swift +++ b/RxSwift/Observables/Observable+Concurrency.swift @@ -8,7 +8,7 @@ import Foundation -// observeOn +// MARK: observeOn extension ObservableType { @@ -32,7 +32,7 @@ extension ObservableType { } } -// subscribeOn +// MARK: subscribeOn extension ObservableType { diff --git a/RxSwift/Observables/Observable+Creation.swift b/RxSwift/Observables/Observable+Creation.swift index d4f50390..d91a0acc 100644 --- a/RxSwift/Observables/Observable+Creation.swift +++ b/RxSwift/Observables/Observable+Creation.swift @@ -8,7 +8,7 @@ import Foundation -// create +// MARK: create /** Creates an observable sequence from a specified subscribe method implementation. @@ -20,7 +20,7 @@ public func create(subscribe: (AnyObserver) -> Disposable) -> Observable() -> Observable { return Empty() } -// never +// MARK: never /** Returns a non-terminating observable sequence, which can be used to denote an infinite duration. @@ -42,7 +42,7 @@ public func never() -> Observable { return Never() } -// just +// MARK: just /** Returns an observable sequence that contains a single element. @@ -54,7 +54,7 @@ public func just(element: E) -> Observable { return Just(element: element) } -// of +// MARK: of /** This method creates a new Observable instance with a variable number of elements. @@ -91,7 +91,7 @@ extension SequenceType { } } -// fail +// MARK: fail /** Returns an observable sequence that terminates with an `error`. @@ -102,7 +102,7 @@ public func failWith(error: ErrorType) -> Observable { return FailWith(error: error) } -// defer +// MARK: defer /** Returns an observable sequence that invokes the specified factory function whenever a new observer subscribes. diff --git a/RxSwift/Observables/Observable+Debug.swift b/RxSwift/Observables/Observable+Debug.swift index faa40ec5..25721d56 100644 --- a/RxSwift/Observables/Observable+Debug.swift +++ b/RxSwift/Observables/Observable+Debug.swift @@ -8,7 +8,7 @@ import Foundation -// debug +// MARK: debug extension ObservableType { diff --git a/RxSwift/Observables/Observable+Multiple.swift b/RxSwift/Observables/Observable+Multiple.swift index 6cb14adb..b2508a28 100644 --- a/RxSwift/Observables/Observable+Multiple.swift +++ b/RxSwift/Observables/Observable+Multiple.swift @@ -8,7 +8,7 @@ import Foundation -// combineLatest +// MARK: combineLatest extension CollectionType where Generator.Element : ObservableConvertibleType { @@ -23,7 +23,7 @@ extension CollectionType where Generator.Element : ObservableConvertibleType { } } -// zip +// MARK: zip extension CollectionType where Generator.Element : ObservableConvertibleType { @@ -38,7 +38,7 @@ extension CollectionType where Generator.Element : ObservableConvertibleType { } } -// switch +// MARK: switch extension ObservableType where E : ObservableConvertibleType { @@ -56,7 +56,7 @@ extension ObservableType where E : ObservableConvertibleType { } } -// concat +// MARK: concat extension ObservableType { @@ -96,7 +96,7 @@ extension ObservableType where E : ObservableConvertibleType { } } -// merge +// MARK: merge extension ObservableType where E : ObservableConvertibleType { @@ -121,7 +121,7 @@ extension ObservableType where E : ObservableConvertibleType { } } -// catch +// MARK: catch extension ObservableType { @@ -161,7 +161,7 @@ extension SequenceType where Generator.Element : ObservableConvertibleType { } } -// takeUntil +// MARK: takeUntil extension ObservableType { @@ -177,7 +177,7 @@ extension ObservableType { } } -// skipUntil +// MARK: skipUntil extension ObservableType { @@ -193,7 +193,7 @@ extension ObservableType { } } -// amb +// MARK: amb extension ObservableType { diff --git a/RxSwift/Observables/Observable+Single.swift b/RxSwift/Observables/Observable+Single.swift index cfe98678..5f868bd7 100644 --- a/RxSwift/Observables/Observable+Single.swift +++ b/RxSwift/Observables/Observable+Single.swift @@ -8,7 +8,7 @@ import Foundation -// distinct until changed +// MARK: distinct until changed extension ObservableType where E: Equatable { @@ -59,7 +59,7 @@ extension ObservableType { } } -// do +// MARK: do extension ObservableType { @@ -97,7 +97,7 @@ extension ObservableType { } } -// startWith +// MARK: startWith extension ObservableType { @@ -113,7 +113,7 @@ extension ObservableType { } } -// retry +// MARK: retry extension ObservableType { @@ -142,7 +142,7 @@ extension ObservableType { } } -// scan +// MARK: scan extension ObservableType { diff --git a/RxSwift/Observables/Observable+StandardSequenceOperators.swift b/RxSwift/Observables/Observable+StandardSequenceOperators.swift index 680dd457..5b877719 100644 --- a/RxSwift/Observables/Observable+StandardSequenceOperators.swift +++ b/RxSwift/Observables/Observable+StandardSequenceOperators.swift @@ -8,7 +8,7 @@ import Foundation -// filter aka where +// MARK: filter aka where extension ObservableType { @@ -24,7 +24,7 @@ extension ObservableType { } } -// takeWhile +// MARK: takeWhile extension ObservableType { @@ -53,7 +53,7 @@ extension ObservableType { } } -// take +// MARK: take extension ObservableType { @@ -74,7 +74,7 @@ extension ObservableType { } } -// skip +// MARK: skip extension ObservableType { @@ -90,7 +90,7 @@ extension ObservableType { } } -// SkipWhile +// MARK: SkipWhile extension ObservableType { @@ -116,7 +116,7 @@ extension ObservableType { } } -// map aka select +// MARK: map aka select extension ObservableType { @@ -143,7 +143,7 @@ extension ObservableType { } } -// flatMap +// MARK: flatMap extension ObservableType { diff --git a/RxSwift/Observables/Observable+Time.swift b/RxSwift/Observables/Observable+Time.swift index e0f2fe03..3579441f 100644 --- a/RxSwift/Observables/Observable+Time.swift +++ b/RxSwift/Observables/Observable+Time.swift @@ -8,7 +8,7 @@ import Foundation -// throttle +// MARK: throttle extension ObservableType { /** @@ -40,7 +40,7 @@ extension ObservableType { } } -// sample +// MARK: sample extension ObservableType { @@ -75,7 +75,7 @@ extension ObservableType { } } -// interval +// MARK: interval /** Returns an observable sequence that produces a value after each period, using the specified scheduler to run timers and to send out observer messages. @@ -92,7 +92,7 @@ public func interval(period: S.TimeInterval, _ scheduler: S) ) } -// timer +// MARK: timer /** Returns an observable sequence that periodically produces a value after the specified initial relative due time has elapsed, using the specified scheduler to run timers. @@ -127,7 +127,7 @@ public func timer(dueTime: S.TimeInterval, _ scheduler: S) ) } -// take +// MARK: take extension ObservableType { @@ -144,7 +144,7 @@ extension ObservableType { } } -// skip +// MARK: skip extension ObservableType { @@ -162,7 +162,7 @@ extension ObservableType { } -// delaySubscription +// MARK: delaySubscription extension ObservableType { @@ -179,7 +179,7 @@ extension ObservableType { } } -// buffer +// MARK: buffer extension ObservableType { From 8251954b7b32834d1723a23aae6a9971fcc2837f Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 18 Oct 2015 18:20:21 +0200 Subject: [PATCH 062/210] Changes `Driver` extensions to `DriverConvertibleType` extensions. --- .../CocoaUnits/Driver/Driver+Operators.swift | 55 ++++++++++--------- .../Driver/Driver+Subscription.swift | 2 +- RxExample/RxExample.xcodeproj/project.pbxproj | 2 + ...tHubSearchRepositoriesViewController.swift | 5 +- .../Views/WikipediaSearchViewController.swift | 13 +++++ .../WikipediaAPI/WikipediaAPI.swift | 17 ++++-- .../RxExample/Services/ImageService.swift | 6 +- .../RxExample/iOS/RootViewController.swift | 2 + 8 files changed, 68 insertions(+), 34 deletions(-) diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift index f535a521..6099a39f 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift @@ -11,7 +11,7 @@ import Foundation import RxSwift #endif -extension Driver { +extension DriverConvertibleType { /** Projects each element of an observable sequence into a new form. @@ -20,7 +20,8 @@ extension Driver { - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source. */ public func map(selector: E -> R) -> Driver { - let source = _source + let source = self + .asObservable() .map(selector) return Driver(source) } @@ -32,13 +33,14 @@ extension Driver { - returns: An observable sequence that contains elements from the input sequence that satisfy the condition. */ public func filter(predicate: (E) -> Bool) -> Driver { - let source = _source + let source = self + .asObservable() .filter(predicate) return Driver(source) } } -extension Driver where Element : DriverConvertibleType { +extension DriverConvertibleType where E : DriverConvertibleType { /** Transforms an observable sequence of observable sequences into an observable sequence @@ -50,14 +52,15 @@ extension Driver where Element : DriverConvertibleType { - returns: The observable sequence that at any point in time produces the elements of the most recent inner observable sequence that has been received. */ public func switchLatest() -> Driver { - let source: Observable = _source + let source: Observable = self + .asObservable() .map { $0.asDriver() } .switchLatest() return Driver(source) } } -extension Driver { +extension DriverConvertibleType { /** Invokes an action for each event in the observable sequence, and propagates all observer messages through the result sequence. @@ -67,7 +70,7 @@ extension Driver { */ public func doOn(eventHandler: (Event) -> Void) -> Driver { - let source = _source + let source = self.asObservable() .doOn(eventHandler) return Driver(source) @@ -83,14 +86,14 @@ extension Driver { */ public func doOn(onNext onNext: (E -> Void)? = nil, onError: (ErrorType -> Void)? = nil, onCompleted: (() -> Void)? = nil) -> Driver { - let source = _source + let source = self.asObservable() .doOn(onNext: onNext, onError: onError, onCompleted: onCompleted) return Driver(source) } } -extension Driver { +extension DriverConvertibleType { /** Prints received events for all observers on standard output. @@ -99,13 +102,13 @@ extension Driver { - returns: An observable sequence whose events are printed to standard output. */ public func debug(identifier: String = "\(__FILE__):\(__LINE__)") -> Driver { - let source = _source + let source = self.asObservable() .debug(identifier) return Driver(source) } } -extension Driver where Element: Equatable { +extension DriverConvertibleType where E: Equatable { /** Returns an observable sequence that contains only distinct contiguous elements according to equality operator. @@ -114,14 +117,14 @@ extension Driver where Element: Equatable { */ public func distinctUntilChanged() -> Driver { - let source = _source + let source = self.asObservable() .distinctUntilChanged({ $0 }, comparer: { ($0 == $1) }) return Driver(source) } } -extension Driver { +extension DriverConvertibleType { /** Returns an observable sequence that contains only distinct contiguous elements according to the `keySelector`. @@ -130,7 +133,7 @@ extension Driver { - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value, from the source sequence. */ public func distinctUntilChanged(keySelector: (E) -> K) -> Driver { - let source = _source + let source = self.asObservable() .distinctUntilChanged(keySelector, comparer: { $0 == $1 }) return Driver(source) } @@ -142,7 +145,7 @@ extension Driver { - returns: An observable sequence only containing the distinct contiguous elements, based on `comparer`, from the source sequence. */ public func distinctUntilChanged(comparer: (lhs: E, rhs: E) -> Bool) -> Driver { - let source = _source + let source = self.asObservable() .distinctUntilChanged({ $0 }, comparer: comparer) return Driver(source) } @@ -155,14 +158,14 @@ extension Driver { - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value and the comparer, from the source sequence. */ public func distinctUntilChanged(keySelector: (E) -> K, comparer: (lhs: K, rhs: K) -> Bool) -> Driver { - let source = _source + let source = self.asObservable() .distinctUntilChanged(keySelector, comparer: comparer) return Driver(source) } } -extension Driver { +extension DriverConvertibleType { /** Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence. @@ -171,7 +174,7 @@ extension Driver { - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence. */ public func flatMap(selector: (E) -> Driver) -> Driver { - let source = _source + let source = self.asObservable() .flatMap(selector) return Driver(source) @@ -179,7 +182,7 @@ extension Driver { } // merge -extension Driver where Element : DriverConvertibleType { +extension DriverConvertibleType where E : DriverConvertibleType { /** Merges elements from all observable sequences in the given enumerable sequence into a single observable sequence. @@ -188,7 +191,7 @@ extension Driver where Element : DriverConvertibleType { - returns: The observable sequence that merges the elements of the observable sequences. */ public func merge() -> Driver { - let source = _source + let source = self.asObservable() .map { $0.asDriver() } .merge() return Driver(source) @@ -201,7 +204,7 @@ extension Driver where Element : DriverConvertibleType { */ public func merge(maxConcurrent maxConcurrent: Int) -> Driver { - let source = _source + let source = self.asObservable() .map { $0.asDriver() } .merge(maxConcurrent: maxConcurrent) return Driver(source) @@ -209,7 +212,7 @@ extension Driver where Element : DriverConvertibleType { } // throttle -extension Driver { +extension DriverConvertibleType { /** Ignores elements from an observable sequence which are followed by another element within a specified relative time duration, using the specified scheduler to run throttling timers. @@ -222,7 +225,7 @@ extension Driver { */ public func throttle(dueTime: S.TimeInterval, _ scheduler: S) -> Driver { - let source = _source + let source = self.asObservable() .throttle(dueTime, scheduler) return Driver(source) @@ -239,7 +242,7 @@ extension Driver { */ public func debounce(dueTime: S.TimeInterval, _ scheduler: S) -> Driver { - let source = _source + let source = self.asObservable() .debounce(dueTime, scheduler) return Driver(source) @@ -247,7 +250,7 @@ extension Driver { } // scan -extension Driver { +extension DriverConvertibleType { /** Applies an accumulator function over an observable sequence and returns each intermediate result. The specified seed value is used as the initial accumulator value. @@ -259,7 +262,7 @@ extension Driver { */ public func scan(seed: A, accumulator: (A, E) -> A) -> Driver { - let source = _source + let source = self.asObservable() .scan(seed, accumulator: accumulator) return Driver(source) } diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver+Subscription.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver+Subscription.swift index a75d42d6..f3a18ace 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/Driver+Subscription.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Subscription.swift @@ -11,7 +11,7 @@ import Foundation import RxSwift #endif -extension Driver { +extension DriverConvertibleType { /** Creates new subscription and sends elements to observer. diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index b79c2a5b..925d88f2 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -309,6 +309,7 @@ C8DF92EA1B0B38C0009BCF9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; }; C8DF92EB1B0B38C0009BCF9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; }; C8DF92F61B0B43A4009BCF9A /* IntroductionExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DF92F51B0B43A4009BCF9A /* IntroductionExampleViewController.swift */; }; + C8E9D2AF1BD3FD960079D0DB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; settings = {ASSET_TAGS = (); }; }; D2AF91981BD3D95900A008C1 /* Using.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AF91881BD2C51900A008C1 /* Using.swift */; }; EC91FB951BBA144400973245 /* GitHubSearchRepositoriesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC91FB941BBA144400973245 /* GitHubSearchRepositoriesViewController.swift */; }; /* End PBXBuildFile section */ @@ -1876,6 +1877,7 @@ C88BB8BB1B07E6C90064D411 /* SearchResultViewModel.swift in Sources */, C88BB8BC1B07E6C90064D411 /* HtmlParsing.swift in Sources */, C88BB8BD1B07E6C90064D411 /* SearchViewModel.swift in Sources */, + C8E9D2AF1BD3FD960079D0DB /* ActivityIndicator.swift in Sources */, C88BB8BE1B07E6C90064D411 /* ImageService.swift in Sources */, C88BB8BF1B07E6C90064D411 /* WikipediaSearchResult.swift in Sources */, C8DF92F61B0B43A4009BCF9A /* IntroductionExampleViewController.swift in Sources */, diff --git a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift index 16343bde..4bef1f77 100644 --- a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift +++ b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift @@ -100,12 +100,15 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat tableView.rx_setDelegate(self) .addDisposableTo(disposeBag) + // activity indicator in status bar + // { GitHubSearchRepositoriesAPI.sharedAPI.activityIndicator - .asDriver() + .distinctUntilChanged() .driveNext { active in UIApplication.sharedApplication().networkActivityIndicatorVisible = active } .addDisposableTo(disposeBag) + // } } // MARK: Table view delegate diff --git a/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchViewController.swift b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchViewController.swift index 3b0158f9..53ed6110 100644 --- a/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchViewController.swift +++ b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchViewController.swift @@ -60,5 +60,18 @@ class WikipediaSearchViewController: ViewController { self.viewModel = viewModel // } + + // activity indicator spinner + // { + combineLatest( + DefaultWikipediaAPI.sharedAPI.loadingWikipediaData, + DefaultImageService.sharedImageService.loadingImage + ) { $0 || $1 } + .distinctUntilChanged() + .driveNext { active in + UIApplication.sharedApplication().networkActivityIndicatorVisible = active + } + .addDisposableTo(disposeBag) + // } } } diff --git a/RxExample/RxExample/Examples/WikipediaImageSearch/WikipediaAPI/WikipediaAPI.swift b/RxExample/RxExample/Examples/WikipediaImageSearch/WikipediaAPI/WikipediaAPI.swift index dd1bb611..2d76f4c3 100644 --- a/RxExample/RxExample/Examples/WikipediaImageSearch/WikipediaAPI/WikipediaAPI.swift +++ b/RxExample/RxExample/Examples/WikipediaImageSearch/WikipediaAPI/WikipediaAPI.swift @@ -32,17 +32,24 @@ class DefaultWikipediaAPI: WikipediaAPI { static let sharedAPI = DefaultWikipediaAPI() // Singleton let $: Dependencies = Dependencies.sharedDependencies - + + let loadingWikipediaData = ActivityIndicator() + private init() {} - + + private func rx_JSON(URL: NSURL) -> Observable { + return $.URLSession + .rx_JSON(URL) + .trackActivity(loadingWikipediaData) + } + // Example wikipedia response http://en.wikipedia.org/w/api.php?action=opensearch&search=Rx func getSearchResults(query: String) -> Observable<[WikipediaSearchResult]> { let escapedQuery = URLEscape(query) let urlContent = "http://en.wikipedia.org/w/api.php?action=opensearch&search=\(escapedQuery)" let url = NSURL(string: urlContent)! - return $.URLSession - .rx_JSON(url) + return rx_JSON(url) .observeOn($.backgroundWorkScheduler) .map { json in guard let json = json as? [AnyObject] else { @@ -61,7 +68,7 @@ class DefaultWikipediaAPI: WikipediaAPI { return failWith(apiError("Can't create url")) } - return $.URLSession.rx_JSON(url) + return rx_JSON(url) .map { jsonResult in guard let json = jsonResult as? NSDictionary else { throw exampleError("Parsing error") diff --git a/RxExample/RxExample/Services/ImageService.swift b/RxExample/RxExample/Services/ImageService.swift index ceac773b..0f09a2e4 100644 --- a/RxExample/RxExample/Services/ImageService.swift +++ b/RxExample/RxExample/Services/ImageService.swift @@ -33,6 +33,8 @@ class DefaultImageService: ImageService { // 2nd level cache let imageDataCache = NSCache() + + let loadingImage = ActivityIndicator() private init() { // cost is approx memory usage @@ -78,12 +80,14 @@ class DefaultImageService: ImageService { self.imageDataCache.setObject(data, forKey: URL) }) .flatMap(self.decodeImage) + .trackActivity(self.loadingImage) } } return decodedImage.doOn(onNext: { image in self.imageCache.setObject(image, forKey: URL) }) - }.observeOn($.mainScheduler) + } + .observeOn($.mainScheduler) } } diff --git a/RxExample/RxExample/iOS/RootViewController.swift b/RxExample/RxExample/iOS/RootViewController.swift index 44713fe7..c43b1c96 100644 --- a/RxExample/RxExample/iOS/RootViewController.swift +++ b/RxExample/RxExample/iOS/RootViewController.swift @@ -14,5 +14,7 @@ public class RootViewController : UITableViewController { super.viewDidLoad() // force load GitHubSearchRepositoriesAPI.sharedAPI.activityIndicator + DefaultWikipediaAPI.sharedAPI + DefaultImageService.sharedImageService } } \ No newline at end of file From 0c33e4291ea9e714401ce20a19d509312868c8bf Mon Sep 17 00:00:00 2001 From: yury Date: Sun, 18 Oct 2015 19:51:36 +0300 Subject: [PATCH 063/210] More work on private vars --- RxSwift/DataStructures/Queue.swift | 8 +- .../DistinctUntilChanged.swift | 32 ++-- .../Observables/Implementations/Merge.swift | 148 +++++++++--------- RxSwift/Observers/TailRecursiveSink.swift | 2 +- 4 files changed, 95 insertions(+), 95 deletions(-) diff --git a/RxSwift/DataStructures/Queue.swift b/RxSwift/DataStructures/Queue.swift index 9c20eec7..124f32c7 100644 --- a/RxSwift/DataStructures/Queue.swift +++ b/RxSwift/DataStructures/Queue.swift @@ -22,7 +22,7 @@ public struct Queue: SequenceType { */ public typealias Generator = AnyGenerator - let resizeFactor = 2 + private let _resizeFactor = 2 private var _storage: [T?] private var _count: Int @@ -110,7 +110,7 @@ public struct Queue: SequenceType { */ public mutating func enqueue(element: T) { if count == _storage.count { - resizeTo(max(_storage.count, 1) * resizeFactor) + resizeTo(max(_storage.count, 1) * _resizeFactor) } _storage[_pushNextIndex] = element @@ -156,9 +156,9 @@ public struct Queue: SequenceType { public mutating func dequeue() -> T { let value = dequeueElementOnly() - let downsizeLimit = _storage.count / (resizeFactor * resizeFactor) + let downsizeLimit = _storage.count / (_resizeFactor * _resizeFactor) if _count < downsizeLimit && downsizeLimit >= _initialCapacity { - resizeTo(_storage.count / resizeFactor) + resizeTo(_storage.count / _resizeFactor) } return value diff --git a/RxSwift/Observables/Implementations/DistinctUntilChanged.swift b/RxSwift/Observables/Implementations/DistinctUntilChanged.swift index 2f88c26a..a34bb195 100644 --- a/RxSwift/Observables/Implementations/DistinctUntilChanged.swift +++ b/RxSwift/Observables/Implementations/DistinctUntilChanged.swift @@ -11,11 +11,11 @@ import Foundation class DistinctUntilChangedSink: Sink, ObserverType { typealias E = O.E - let parent: DistinctUntilChanged - var currentKey: Key? = nil + private let _parent: DistinctUntilChanged + private var _currentKey: Key? = nil init(parent: DistinctUntilChanged, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } @@ -25,27 +25,27 @@ class DistinctUntilChangedSink: Sink, ObserverType { switch event { case .Next(let value): do { - let key = try self.parent.selector(value) + let key = try _parent._selector(value) var areEqual = false - if let currentKey = self.currentKey { - areEqual = try self.parent.comparer(currentKey, key) + if let currentKey = _currentKey { + areEqual = try _parent._comparer(currentKey, key) } if areEqual { return } - self.currentKey = key + _currentKey = key observer?.on(event) } catch let error { observer?.on(.Error(error)) - self.dispose() + dispose() } case .Error, .Completed: observer?.on(event) - self.dispose() + dispose() } } } @@ -54,19 +54,19 @@ class DistinctUntilChanged: Producer { typealias KeySelector = (Element) throws -> Key typealias EqualityComparer = (Key, Key) throws -> Bool - let source: Observable - let selector: KeySelector - let comparer: EqualityComparer + private let _source: Observable + private let _selector: KeySelector + private let _comparer: EqualityComparer init(source: Observable, selector: KeySelector, comparer: EqualityComparer) { - self.source = source - self.selector = selector - self.comparer = comparer + _source = source + _selector = selector + _comparer = comparer } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { let sink = DistinctUntilChangedSink(parent: self, observer: observer, cancel: cancel) setSink(sink) - return source.subscribeSafe(sink) + return _source.subscribeSafe(sink) } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Merge.swift b/RxSwift/Observables/Implementations/Merge.swift index 52fe4701..3777abb8 100644 --- a/RxSwift/Observables/Implementations/Merge.swift +++ b/RxSwift/Observables/Implementations/Merge.swift @@ -15,28 +15,28 @@ class MergeSinkIter.KeyType typealias Parent = MergeSink - let parent: Parent - let disposeKey: DisposeKey + private let _parent: Parent + private let _disposeKey: DisposeKey init(parent: Parent, disposeKey: DisposeKey) { - self.parent = parent - self.disposeKey = disposeKey + _parent = parent + _disposeKey = disposeKey } func on(event: Event) { - parent.lock.performLocked { + _parent._lock.performLocked { switch event { case .Next: - parent.observer?.on(event) + _parent.observer?.on(event) case .Error: - parent.observer?.on(event) - parent.dispose() + _parent.observer?.on(event) + _parent.dispose() case .Completed: - parent.group.removeDisposable(disposeKey) + _parent._group.removeDisposable(_disposeKey) - if parent.stopped && parent.group.count == 1 { - parent.observer?.on(.Completed) - parent.dispose() + if _parent._stopped && _parent._group.count == 1 { + _parent.observer?.on(.Completed) + _parent.dispose() } } } @@ -47,36 +47,36 @@ class MergeSink typealias E = S typealias Parent = Merge - let parent: Parent + private let _parent: Parent - let lock = NSRecursiveLock() + private let _lock = NSRecursiveLock() // state - var stopped = false + private var _stopped = false - let group = CompositeDisposable() - let sourceSubscription = SingleAssignmentDisposable() + private let _group = CompositeDisposable() + private let _sourceSubscription = SingleAssignmentDisposable() init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(observer: observer, cancel: cancel) } func run() -> Disposable { - group.addDisposable(sourceSubscription) + _group.addDisposable(_sourceSubscription) - let disposable = self.parent.sources.subscribeSafe(self) - sourceSubscription.disposable = disposable + let disposable = _parent._sources.subscribeSafe(self) + _sourceSubscription.disposable = disposable - return group + return _group } func on(event: Event) { switch event { case .Next(let value): let innerSubscription = SingleAssignmentDisposable() - let maybeKey = group.addDisposable(innerSubscription) + let maybeKey = _group.addDisposable(innerSubscription) if let key = maybeKey { let observer = MergeSinkIter(parent: self, disposeKey: key) @@ -84,20 +84,20 @@ class MergeSink innerSubscription.disposable = disposable } case .Error(let error): - lock.performLocked { + _lock.performLocked { observer?.on(.Error(error)) - self.dispose() + dispose() } case .Completed: - lock.performLocked { - self.stopped = true + _lock.performLocked { + _stopped = true - if group.count == 1 { + if _group.count == 1 { observer?.on(.Completed) - self.dispose() + dispose() } else { - sourceSubscription.dispose() + _sourceSubscription.dispose() } } } @@ -111,35 +111,35 @@ class MergeConcurrentSinkIter.KeyType typealias Parent = MergeConcurrentSink - let parent: Parent - let disposeKey: DisposeKey + private let _parent: Parent + private let _disposeKey: DisposeKey init(parent: Parent, disposeKey: DisposeKey) { - self.parent = parent - self.disposeKey = disposeKey + _parent = parent + _disposeKey = disposeKey } func on(event: Event) { - parent.lock.performLocked { + _parent._lock.performLocked { switch event { case .Next: - parent.observer?.on(event) + _parent.observer?.on(event) case .Error: - parent.observer?.on(event) - self.parent.dispose() + _parent.observer?.on(event) + _parent.dispose() case .Completed: - parent.group.removeDisposable(disposeKey) - let queue = parent.queue + _parent._group.removeDisposable(_disposeKey) + let queue = _parent._queue if queue.value.count > 0 { let s = queue.value.dequeue() - self.parent.subscribe(s, group: parent.group) + _parent.subscribe(s, group: _parent._group) } else { - parent.activeCount = parent.activeCount - 1 + _parent._activeCount = _parent._activeCount - 1 - if parent.stopped && parent.activeCount == 0 { - parent.observer?.on(.Completed) - self.parent.dispose() + if _parent._stopped && _parent._activeCount == 0 { + _parent.observer?.on(.Completed) + _parent.dispose() } } } @@ -152,31 +152,31 @@ class MergeConcurrentSink typealias QueueType = Queue - let parent: Parent + private let _parent: Parent - let lock = NSRecursiveLock() + private let _lock = NSRecursiveLock() // state - var stopped = false - var activeCount = 0 - var queue = RxMutableBox(QueueType(capacity: 2)) + private var _stopped = false + private var _activeCount = 0 + private var _queue = RxMutableBox(QueueType(capacity: 2)) - let sourceSubscription = SingleAssignmentDisposable() - let group = CompositeDisposable() + private let _sourceSubscription = SingleAssignmentDisposable() + private let _group = CompositeDisposable() init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent - group.addDisposable(sourceSubscription) + _group.addDisposable(_sourceSubscription) super.init(observer: observer, cancel: cancel) } func run() -> Disposable { - group.addDisposable(sourceSubscription) + _group.addDisposable(_sourceSubscription) - let disposable = self.parent.sources.subscribeSafe(self) - sourceSubscription.disposable = disposable - return group + let disposable = _parent._sources.subscribeSafe(self) + _sourceSubscription.disposable = disposable + return _group } func subscribe(innerSource: E, group: CompositeDisposable) { @@ -195,52 +195,52 @@ class MergeConcurrentSink) { switch event { case .Next(let value): - let subscribe = lock.calculateLocked { () -> Bool in - if activeCount < self.parent.maxConcurrent { - self.activeCount += 1 + let subscribe = _lock.calculateLocked { () -> Bool in + if _activeCount < _parent._maxConcurrent { + _activeCount += 1 return true } else { - queue.value.enqueue(value) + _queue.value.enqueue(value) return false } } if subscribe { - self.subscribe(value, group: group) + self.subscribe(value, group: _group) } case .Error(let error): - lock.performLocked { + _lock.performLocked { observer?.on(.Error(error)) - self.dispose() + dispose() } case .Completed: - lock.performLocked { - if activeCount == 0 { + _lock.performLocked { + if _activeCount == 0 { observer?.on(.Completed) self.dispose() } else { - sourceSubscription.dispose() + _sourceSubscription.dispose() } - stopped = true + _stopped = true } } } } class Merge : Producer { - let sources: Observable - let maxConcurrent: Int + private let _sources: Observable + private let _maxConcurrent: Int init(sources: Observable, maxConcurrent: Int) { - self.sources = sources - self.maxConcurrent = maxConcurrent + _sources = sources + _maxConcurrent = maxConcurrent } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - if maxConcurrent > 0 { + if _maxConcurrent > 0 { let sink = MergeConcurrentSink(parent: self, observer: observer, cancel: cancel) setSink(sink) return sink.run() diff --git a/RxSwift/Observers/TailRecursiveSink.swift b/RxSwift/Observers/TailRecursiveSink.swift index 5d7c992e..8f91c3f9 100644 --- a/RxSwift/Observers/TailRecursiveSink.swift +++ b/RxSwift/Observers/TailRecursiveSink.swift @@ -17,7 +17,7 @@ class TailRecursiveSink Date: Sun, 18 Oct 2015 20:18:22 +0300 Subject: [PATCH 064/210] Work on schedulers --- .../CombineLatest+CollectionType.swift | 20 +++---- .../ConcurrentDispatchQueueScheduler.swift | 12 ++--- RxSwift/Schedulers/RecursiveScheduler.swift | 52 +++++++++--------- .../SchedulerServices+Emulation.swift | 12 ++--- .../SerialDispatchQueueScheduler.swift | 12 ++--- RxSwift/Subjects/BehaviorSubject.swift | 54 +++++++++---------- 6 files changed, 80 insertions(+), 82 deletions(-) diff --git a/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift b/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift index 8f5ad6e8..d635b548 100644 --- a/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift +++ b/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift @@ -50,24 +50,24 @@ class CombineLatestCollectionTypeSink(state: StateType, action: StateType -> Disposable) -> Disposable { let cancel = SingleAssignmentDisposable() - dispatch_async(self.queue) { + dispatch_async(_queue) { if cancel.disposed { return } @@ -97,7 +97,7 @@ public class ConcurrentDispatchQueueScheduler: SchedulerType { - returns: The disposable object used to cancel the scheduled action (best effort). */ public final func scheduleRelative(state: StateType, dueTime: NSTimeInterval, action: (StateType) -> Disposable) -> Disposable { - let timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.queue) + let timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _queue) let dispatchInterval = MainScheduler.convertTimeIntervalToDispatchTime(dueTime) @@ -129,7 +129,7 @@ public class ConcurrentDispatchQueueScheduler: SchedulerType { - returns: The disposable object used to cancel the scheduled action (best effort). */ public func schedulePeriodic(state: StateType, startAfter: TimeInterval, period: TimeInterval, action: (StateType) -> StateType) -> Disposable { - let timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.queue) + let timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _queue) let initial = MainScheduler.convertTimeIntervalToDispatchTime(startAfter) let dispatchInterval = MainScheduler.convertTimeIntervalToDispatchInterval(period) diff --git a/RxSwift/Schedulers/RecursiveScheduler.swift b/RxSwift/Schedulers/RecursiveScheduler.swift index c3ee529d..5e9d0a76 100644 --- a/RxSwift/Schedulers/RecursiveScheduler.swift +++ b/RxSwift/Schedulers/RecursiveScheduler.swift @@ -9,7 +9,7 @@ import Foundation class RecursiveScheduler: AnyRecursiveScheduler { - let _scheduler: S + private let _scheduler: S init(scheduler: S, action: Action) { _scheduler = scheduler @@ -34,12 +34,12 @@ class AnyRecursiveScheduler { private let _lock = NSRecursiveLock() // state - private let group = CompositeDisposable() + private let _group = CompositeDisposable() - var action: Action? + private var _action: Action? init(action: Action) { - self.action = action + _action = action } // abstract methods @@ -66,19 +66,19 @@ class AnyRecursiveScheduler { var removeKey: CompositeDisposable.DisposeKey? = nil let d = scheduleRelativeAdapter(state, dueTime: dueTime) { (state) -> Disposable in // best effort - if self.group.disposed { + if self._group.disposed { return NopDisposable.instance } let action = self._lock.calculateLocked { () -> Action? in if isAdded { - self.group.removeDisposable(removeKey!) + self._group.removeDisposable(removeKey!) } else { isDone = true } - return self.action + return self._action } if let action = action { @@ -90,7 +90,7 @@ class AnyRecursiveScheduler { _lock.performLocked { if !isDone { - removeKey = group.addDisposable(d) + removeKey = _group.addDisposable(d) isAdded = true } } @@ -109,19 +109,19 @@ class AnyRecursiveScheduler { var removeKey: CompositeDisposable.DisposeKey? = nil let d = scheduleAdapter(state) { (state) -> Disposable in // best effort - if self.group.disposed { + if self._group.disposed { return NopDisposable.instance } let action = self._lock.calculateLocked { () -> Action? in if isAdded { - self.group.removeDisposable(removeKey!) + self._group.removeDisposable(removeKey!) } else { isDone = true } - return self.action + return self._action } if let action = action { @@ -133,7 +133,7 @@ class AnyRecursiveScheduler { _lock.performLocked { if !isDone { - removeKey = group.addDisposable(d) + removeKey = _group.addDisposable(d) isAdded = true } } @@ -141,9 +141,9 @@ class AnyRecursiveScheduler { func dispose() { _lock.performLocked { - self.action = nil + _action = nil } - group.dispose() + _group.dispose() } } @@ -154,14 +154,14 @@ class RecursiveImmediateScheduler { typealias Action = (state: State, recurse: State -> Void) -> Void private var _lock = SpinLock() - let group = CompositeDisposable() + private let _group = CompositeDisposable() - var action: Action? - let scheduler: ImmediateSchedulerType + private var _action: Action? + private let _scheduler: ImmediateSchedulerType init(action: Action, scheduler: ImmediateSchedulerType) { - self.action = action - self.scheduler = scheduler + _action = action + _scheduler = scheduler } // immediate scheduling @@ -177,21 +177,21 @@ class RecursiveImmediateScheduler { var isDone = false var removeKey: CompositeDisposable.DisposeKey? = nil - let d = self.scheduler.schedule(state) { (state) -> Disposable in + let d = _scheduler.schedule(state) { (state) -> Disposable in // best effort - if self.group.disposed { + if self._group.disposed { return NopDisposable.instance } let action = self._lock.calculateLocked { () -> Action? in if isAdded { - self.group.removeDisposable(removeKey!) + self._group.removeDisposable(removeKey!) } else { isDone = true } - return self.action + return self._action } if let action = action { @@ -203,7 +203,7 @@ class RecursiveImmediateScheduler { _lock.performLocked { if !isDone { - removeKey = group.addDisposable(d) + removeKey = _group.addDisposable(d) isAdded = true } } @@ -211,8 +211,8 @@ class RecursiveImmediateScheduler { func dispose() { _lock.performLocked { - self.action = nil + _action = nil } - group.dispose() + _group.dispose() } } \ No newline at end of file diff --git a/RxSwift/Schedulers/SchedulerServices+Emulation.swift b/RxSwift/Schedulers/SchedulerServices+Emulation.swift index b1c50cfb..d503522c 100644 --- a/RxSwift/Schedulers/SchedulerServices+Emulation.swift +++ b/RxSwift/Schedulers/SchedulerServices+Emulation.swift @@ -18,13 +18,13 @@ class SchedulePeriodicRecursive { typealias TimeInterval = S.TimeInterval typealias RecursiveScheduler = AnyRecursiveScheduler - let _scheduler: S - let _startAfter: TimeInterval - let _period: TimeInterval - let _action: RecursiveAction + private let _scheduler: S + private let _startAfter: TimeInterval + private let _period: TimeInterval + private let _action: RecursiveAction - var _state: State - var _pendingTickCount: Int32 = 0 + private var _state: State + private var _pendingTickCount: Int32 = 0 init(scheduler: S, startAfter: TimeInterval, period: TimeInterval, action: RecursiveAction, state: State) { _scheduler = scheduler diff --git a/RxSwift/Schedulers/SerialDispatchQueueScheduler.swift b/RxSwift/Schedulers/SerialDispatchQueueScheduler.swift index 88dc79fd..d02691b3 100644 --- a/RxSwift/Schedulers/SerialDispatchQueueScheduler.swift +++ b/RxSwift/Schedulers/SerialDispatchQueueScheduler.swift @@ -29,7 +29,7 @@ public class SerialDispatchQueueScheduler: SchedulerType { public typealias TimeInterval = NSTimeInterval public typealias Time = NSDate - private let serialQueue : dispatch_queue_t + private let _serialQueue : dispatch_queue_t public var now : NSDate { get { @@ -38,10 +38,10 @@ public class SerialDispatchQueueScheduler: SchedulerType { } // leeway for scheduling timers - var leeway: Int64 = 0 + private var _leeway: Int64 = 0 init(serialQueue: dispatch_queue_t) { - self.serialQueue = serialQueue + _serialQueue = serialQueue } /** @@ -129,7 +129,7 @@ public class SerialDispatchQueueScheduler: SchedulerType { func scheduleInternal(state: StateType, action: (StateType) -> Disposable) -> Disposable { let cancel = SingleAssignmentDisposable() - dispatch_async(self.serialQueue) { + dispatch_async(_serialQueue) { if cancel.disposed { return } @@ -150,7 +150,7 @@ public class SerialDispatchQueueScheduler: SchedulerType { - returns: The disposable object used to cancel the scheduled action (best effort). */ public final func scheduleRelative(state: StateType, dueTime: NSTimeInterval, action: (StateType) -> Disposable) -> Disposable { - let timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.serialQueue) + let timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _serialQueue) let dispatchInterval = MainScheduler.convertTimeIntervalToDispatchTime(dueTime) @@ -182,7 +182,7 @@ public class SerialDispatchQueueScheduler: SchedulerType { - returns: The disposable object used to cancel the scheduled action (best effort). */ public func schedulePeriodic(state: StateType, startAfter: TimeInterval, period: TimeInterval, action: (StateType) -> StateType) -> Disposable { - let timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.serialQueue) + let timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _serialQueue) let initial = MainScheduler.convertTimeIntervalToDispatchTime(startAfter) let dispatchInterval = MainScheduler.convertTimeIntervalToDispatchInterval(period) diff --git a/RxSwift/Subjects/BehaviorSubject.swift b/RxSwift/Subjects/BehaviorSubject.swift index b9d870c1..d7266460 100644 --- a/RxSwift/Subjects/BehaviorSubject.swift +++ b/RxSwift/Subjects/BehaviorSubject.swift @@ -12,19 +12,19 @@ private class BehaviorSubjectSubscription : Disposable { typealias Parent = BehaviorSubject typealias DisposeKey = Bag>.KeyType - let parent: Parent - var disposeKey: DisposeKey? + private let _parent: Parent + private var _disposeKey: DisposeKey? init(parent: BehaviorSubject, disposeKey: DisposeKey) { - self.parent = parent - self.disposeKey = disposeKey + _parent = parent + _disposeKey = disposeKey } func dispose() { - self.parent.lock.performLocked { - if let disposeKey = disposeKey { - self.parent.observers.removeKey(disposeKey) - self.disposeKey = nil + _parent._lock.performLocked { + if let disposeKey = _disposeKey { + _parent._observers.removeKey(disposeKey) + _disposeKey = nil } } } @@ -38,19 +38,19 @@ Observers can subscribe to the subject to receive the last (or initial) value an public final class BehaviorSubject : Observable, SubjectType, ObserverType, Disposable { public typealias SubjectObserverType = BehaviorSubject - let lock = NSRecursiveLock() + private let _lock = NSRecursiveLock() // state private var _disposed = false private var _value: Element - private var observers = Bag>() - private var stoppedEvent: Event? + private var _observers = Bag>() + private var _stoppedEvent: Event? /** Indicates whether the subject has been disposed. */ public var disposed: Bool { - return lock.calculateLocked { + return _lock.calculateLocked { return _disposed } } @@ -70,12 +70,12 @@ public final class BehaviorSubject : Observable, SubjectType, - returns: Latest value. */ public func value() throws -> Element { - return try lock.calculateLockedOrFail { + return try _lock.calculateLockedOrFail { if _disposed { throw RxError.DisposedError } - if let error = stoppedEvent?.error { + if let error = _stoppedEvent?.error { // intentionally throw exception throw error } @@ -91,21 +91,19 @@ public final class BehaviorSubject : Observable, SubjectType, - parameter event: Event to send to the observers. */ public func on(event: Event) { - lock.performLocked { - if self.stoppedEvent != nil || _disposed { + _lock.performLocked { + if _stoppedEvent != nil || _disposed { return } switch event { case .Next(let value): - self._value = value - case .Error: - self.stoppedEvent = event - case .Completed: - self.stoppedEvent = event + _value = value + case .Error, .Completed: + _stoppedEvent = event } - self.observers.forEach { $0.on(event) } + _observers.forEach { $0.on(event) } } } @@ -116,18 +114,18 @@ public final class BehaviorSubject : Observable, SubjectType, - returns: Disposable object that can be used to unsubscribe the observer from the subject. */ public override func subscribe(observer: O) -> Disposable { - return lock.calculateLocked { + return _lock.calculateLocked { if _disposed { observer.on(.Error(RxError.DisposedError)) return NopDisposable.instance } - if let stoppedEvent = stoppedEvent { + if let stoppedEvent = _stoppedEvent { observer.on(stoppedEvent) return NopDisposable.instance } - let key = observers.insert(observer.asObserver()) + let key = _observers.insert(observer.asObserver()) observer.on(.Next(_value)) return BehaviorSubjectSubscription(parent: self, disposeKey: key) @@ -145,10 +143,10 @@ public final class BehaviorSubject : Observable, SubjectType, Unsubscribe all observers and release resources. */ public func dispose() { - lock.performLocked { + _lock.performLocked { _disposed = true - observers.removeAll() - stoppedEvent = nil + _observers.removeAll() + _stoppedEvent = nil } } } \ No newline at end of file From 0534aa983a8c3ef84e64128df29ce9eba9bc8bec Mon Sep 17 00:00:00 2001 From: Greg Pardo Date: Sun, 18 Oct 2015 10:23:53 -0700 Subject: [PATCH 065/210] #193 Only implement essential functions as per kzaher's suggestions --- RxCocoa/iOS/UICollectionView+Rx.swift | 85 +++++---------------------- RxCocoa/iOS/UITableView+Rx.swift | 33 +++++++++++ 2 files changed, 48 insertions(+), 70 deletions(-) diff --git a/RxCocoa/iOS/UICollectionView+Rx.swift b/RxCocoa/iOS/UICollectionView+Rx.swift index 8c554c5f..2bd20fd1 100644 --- a/RxCocoa/iOS/UICollectionView+Rx.swift +++ b/RxCocoa/iOS/UICollectionView+Rx.swift @@ -144,28 +144,25 @@ extension UICollectionView { */ public func rx_modelSelected() -> ControlEvent { let source: Observable = rx_itemSelected .map { indexPath in - return self.rx_modelAtIndexPath(indexPath).asObservable() - }.switchLatest() + let dataSource: RxCollectionViewReactiveArrayDataSource = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_itemsWith*` methods was used.") + + return dataSource.modelAtIndex(indexPath.item)! + } return ControlEvent(source: source) } - public func rx_modelAtIndexPath(indexPath: NSIndexPath?) -> ControlEvent { - let source: Observable = create { observer in - if let indexPath = indexPath { - - let dataSource: RxCollectionViewReactiveArrayDataSource = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_itemsWith*` methods was used.") - - if let model = dataSource.modelAtIndex(indexPath.item) { - observer.on(.Next(model)) - } - - } - - return AnonymousDisposable { } + /** + Syncronous helper method for retrieving a model at indexPath through a reactive data source + */ + public func rx_modelAtIndexPath(indexPath: NSIndexPath) throws -> T { + let dataSource: RxCollectionViewReactiveArrayDataSource = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_itemsWith*` methods was used.") + + guard let element = dataSource.modelAtIndex(indexPath.item) else { + throw rxError(.InvalidOperation, "Items not set yet.") } - return ControlEvent(source: source) + return element } } #endif @@ -174,15 +171,13 @@ extension UICollectionView { extension UICollectionView { - public typealias ContextAndAnimationCoordinator = (context: UIFocusUpdateContext, animationCoordinator: UIFocusAnimationCoordinator) - /** Reactive wrapper for `delegate` message `collectionView:didUpdateFocusInContext:withAnimationCoordinator:`. */ - public var rx_didUpdateFocusInContextWithAnimationCoordinator: ControlEvent { + public var rx_didUpdateFocusInContextWithAnimationCoordinator: ControlEvent<(context: UIFocusUpdateContext, animationCoordinator: UIFocusAnimationCoordinator)> { let source = rx_delegate.observe("collectionView:didUpdateFocusInContext:withAnimationCoordinator:") - .map { a -> ContextAndAnimationCoordinator in + .map { a -> (context: UIFocusUpdateContext, animationCoordinator: UIFocusAnimationCoordinator) in let context = a[1] as! UIFocusUpdateContext let animationCoordinator = a[2] as! UIFocusAnimationCoordinator return (context: context, animationCoordinator: animationCoordinator) @@ -190,55 +185,5 @@ extension UICollectionView { return ControlEvent(source: source) } - - /** - Reactive wrapper for UICollectionViewFocusUpdateContext's nextFocusedIndexPath - */ - public var rx_nextFocusedUpdated: ControlEvent { - let source = rx_didUpdateFocusInContextWithAnimationCoordinator - .map { (c: ContextAndAnimationCoordinator) -> NSIndexPath? in - let context = c.context as! UICollectionViewFocusUpdateContext - return context.nextFocusedIndexPath - } - - return ControlEvent(source: source) - } - - /** - Reactive wrapper for UICollectionViewFocusUpdateContext's previouslyFocusedIndexPath - */ - public var rx_previousFocusedUpdated: ControlEvent { - let source = rx_didUpdateFocusInContextWithAnimationCoordinator - .map { (c: ContextAndAnimationCoordinator) -> NSIndexPath? in - let context = c.context as! UICollectionViewFocusUpdateContext - return context.previouslyFocusedIndexPath - } - - return ControlEvent(source: source) - } - - /** - Reactive wrapper for the next focused model - */ - public func rx_modelForNextFocusedUpdated() -> ControlEvent { - let source: Observable = rx_nextFocusedUpdated.map { indexPath in - return self.rx_modelAtIndexPath(indexPath).asObservable() - - }.switchLatest() - - return ControlEvent(source: source) - } - - /** - Reactive wrapper for the previously focused model - */ - public func rx_modelForPreviouslyFocusedUpdated() -> ControlEvent { - let source: Observable = rx_nextFocusedUpdated.map { indexPath in - return self.rx_modelAtIndexPath(indexPath).asObservable() - }.switchLatest() - - return ControlEvent(source: source) - } } - #endif diff --git a/RxCocoa/iOS/UITableView+Rx.swift b/RxCocoa/iOS/UITableView+Rx.swift index 97ca683f..28d59935 100644 --- a/RxCocoa/iOS/UITableView+Rx.swift +++ b/RxCocoa/iOS/UITableView+Rx.swift @@ -196,6 +196,39 @@ extension UITableView { return ControlEvent(source: source) } + /** + Syncronous helper method for retrieving a model at indexPath through a reactive data source + */ + public func rx_modelAtIndexPath(indexPath: NSIndexPath) throws -> T { + let dataSource: RxCollectionViewReactiveArrayDataSource = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_subscribeItemsTo*` methods was used.") + + guard let element = dataSource.modelAtIndex(indexPath.item) else { + throw rxError(.InvalidOperation, "Items not set yet.") + } + + return element + } } #endif + +#if os(tvOS) + + extension UITableView { + + /** + Reactive wrapper for `delegate` message `tableView:didUpdateFocusInContext:withAnimationCoordinator:`. + */ + public var rx_didUpdateFocusInContextWithAnimationCoordinator: ControlEvent<(context: UIFocusUpdateContext, animationCoordinator: UIFocusAnimationCoordinator)> { + + let source = rx_delegate.observe("tableView:didUpdateFocusInContext:withAnimationCoordinator:") + .map { a -> (context: UIFocusUpdateContext, animationCoordinator: UIFocusAnimationCoordinator) in + let context = a[1] as! UIFocusUpdateContext + let animationCoordinator = a[2] as! UIFocusAnimationCoordinator + return (context: context, animationCoordinator: animationCoordinator) + } + + return ControlEvent(source: source) + } + } +#endif From e22cf3dfb878d5569f563b6de8eb44ef920d880d Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 19 Oct 2015 00:52:11 +0200 Subject: [PATCH 066/210] Adds RxCocoa unit tests. --- .../Implementations/ControlTarget.swift | 10 +- RxCocoa/Common/RxTarget.swift | 15 ++ RxCocoa/OSX/NSControl+Rx.swift | 76 ++++++-- RxCocoa/OSX/NSTextField+Rx.swift | 11 +- RxCocoa/iOS/UIBarButtonItem+Rx.swift | 21 ++- RxCocoa/iOS/UICollectionView+Rx.swift | 9 +- RxCocoa/iOS/UIControl+Rx.swift | 25 ++- RxCocoa/iOS/UIGestureRecognizer+Rx.swift | 17 +- RxCocoa/iOS/UITableView+Rx.swift | 10 +- .../RxCocoaTests/Control+RxTests+Cocoa.swift | 44 +++++ .../RxCocoaTests/Control+RxTests+UIKit.swift | 174 ++++++++++++++++++ RxTests/RxCocoaTests/Control+RxTests.swift | 76 ++++++++ RxTests/RxCocoaTests/UIControl+RxTests.swift | 32 ---- RxTests/RxTests.xcodeproj/project.pbxproj | 30 ++- 14 files changed, 452 insertions(+), 98 deletions(-) create mode 100644 RxTests/RxCocoaTests/Control+RxTests+Cocoa.swift create mode 100644 RxTests/RxCocoaTests/Control+RxTests+UIKit.swift create mode 100644 RxTests/RxCocoaTests/Control+RxTests.swift delete mode 100644 RxTests/RxCocoaTests/UIControl+RxTests.swift diff --git a/RxCocoa/Common/Observables/Implementations/ControlTarget.swift b/RxCocoa/Common/Observables/Implementations/ControlTarget.swift index c74c5544..da2c7459 100644 --- a/RxCocoa/Common/Observables/Implementations/ControlTarget.swift +++ b/RxCocoa/Common/Observables/Implementations/ControlTarget.swift @@ -30,7 +30,7 @@ class ControlTarget: RxTarget { let selector: Selector = "eventHandler:" - unowned let control: Control + weak var control: Control? #if os(iOS) || os(tvOS) let controlEvents: UIControlEvents #endif @@ -72,7 +72,7 @@ class ControlTarget: RxTarget { #endif func eventHandler(sender: Control!) { - if let callback = self.callback { + if let callback = self.callback, control = self.control { callback(control) } } @@ -80,10 +80,10 @@ class ControlTarget: RxTarget { override func dispose() { super.dispose() #if os(iOS) || os(tvOS) - self.control.removeTarget(self, action: self.selector, forControlEvents: self.controlEvents) + self.control?.removeTarget(self, action: self.selector, forControlEvents: self.controlEvents) #elseif os(OSX) - self.control.target = nil - self.control.action = nil + self.control?.target = nil + self.control?.action = nil #endif self.callback = nil } diff --git a/RxCocoa/Common/RxTarget.swift b/RxCocoa/Common/RxTarget.swift index c59a1424..a32e6d87 100644 --- a/RxCocoa/Common/RxTarget.swift +++ b/RxCocoa/Common/RxTarget.swift @@ -19,11 +19,26 @@ class RxTarget : NSObject override init() { super.init() self.retainSelf = self + +#if TRACE_RESOURCES + OSAtomicIncrement32(&resourceCount) +#endif + +#if DEBUG MainScheduler.ensureExecutingOnScheduler() +#endif } func dispose() { +#if DEBUG MainScheduler.ensureExecutingOnScheduler() +#endif self.retainSelf = nil } + +#if TRACE_RESOURCES + deinit { + OSAtomicDecrement32(&resourceCount) + } +#endif } \ No newline at end of file diff --git a/RxCocoa/OSX/NSControl+Rx.swift b/RxCocoa/OSX/NSControl+Rx.swift index 6a267fb6..85b79764 100644 --- a/RxCocoa/OSX/NSControl+Rx.swift +++ b/RxCocoa/OSX/NSControl+Rx.swift @@ -12,36 +12,74 @@ import Cocoa import RxSwift #endif +var rx_value_key: UInt8 = 0 +var rx_control_events_key: UInt8 = 0 + extension NSControl { /** Reactive wrapper for control event. */ public var rx_controlEvents: ControlEvent { - let source: Observable = AnonymousObservable { observer in - MainScheduler.ensureExecutingOnScheduler() - - let observer = ControlTarget(control: self) { control in - observer.on(.Next()) - } - - return observer - }.takeUntil(rx_deallocated) + MainScheduler.ensureExecutingOnScheduler() + + let source = rx_lazyInstanceObservable(&rx_control_events_key) { () -> Observable in + AnonymousObservable { [weak self] observer in + MainScheduler.ensureExecutingOnScheduler() + + guard let control = self else { + observer.on(.Completed) + return NopDisposable.instance + } + + let observer = ControlTarget(control: control) { control in + observer.on(.Next()) + } + + return observer + }.takeUntil(self.rx_deallocated) + } return ControlEvent(source: source) } - + + /** + Helper to make sure that `Observable` returned from `createCachedObservable` is only created once. + This is important because on OSX there is only one `target` and `action` properties on `NSControl`. + */ + func rx_lazyInstanceObservable(key: UnsafePointer, createCachedObservable: () -> T) -> T { + if let value = objc_getAssociatedObject(self, key) { + return value as! T + } + + let observable = createCachedObservable() + + objc_setAssociatedObject(self, key, observable, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) + + return observable + } + func rx_value(getter getter: () -> T, setter: T -> Void) -> ControlProperty { - let source: Observable = AnonymousObservable { observer in - observer.on(.Next(getter())) - - let observer = ControlTarget(control: self) { control in + MainScheduler.ensureExecutingOnScheduler() + + let source = rx_lazyInstanceObservable(&rx_value_key) { () -> Observable in + return AnonymousObservable { [weak self] observer in + guard let control = self else { + observer.on(.Completed) + return NopDisposable.instance + } + observer.on(.Next(getter())) - } - - return observer - }.takeUntil(rx_deallocated) - + + let observer = ControlTarget(control: control) { control in + observer.on(.Next(getter())) + } + + return observer + }.takeUntil(self.rx_deallocated) + } + + return ControlProperty(source: source, observer: AnyObserver { event in switch event { case .Next(let value): diff --git a/RxCocoa/OSX/NSTextField+Rx.swift b/RxCocoa/OSX/NSTextField+Rx.swift index 99dc0d1d..af6646bb 100644 --- a/RxCocoa/OSX/NSTextField+Rx.swift +++ b/RxCocoa/OSX/NSTextField+Rx.swift @@ -15,13 +15,12 @@ import RxSwift class RxTextFieldDelegate : DelegateProxy , NSTextFieldDelegate , DelegateProxyType { - let textField: NSTextField - let textSubject = ReplaySubject.create(bufferSize: 1) + let textSubject = PublishSubject() required init(parentObject: AnyObject) { - self.textField = parentObject as! NSTextField + let textField = parentObject as! NSTextField super.init(parentObject: parentObject) - self.textSubject.on(.Next(self.textField.stringValue)) + self.textSubject.on(.Next(textField.stringValue)) } override func controlTextDidChange(notification: NSNotification) { @@ -59,7 +58,9 @@ extension NSTextField { public var rx_text: ControlProperty { let delegate = proxyForObject(self) as RxTextFieldDelegate - let source = delegate.textSubject + let source = deferred { [weak self] in + delegate.textSubject.startWith(self?.stringValue ?? "") + }.takeUntil(rx_deallocated) return ControlProperty(source: source, observer: AnyObserver { [weak self] event in MainScheduler.ensureExecutingOnScheduler() diff --git a/RxCocoa/iOS/UIBarButtonItem+Rx.swift b/RxCocoa/iOS/UIBarButtonItem+Rx.swift index c31bba0c..34952a29 100644 --- a/RxCocoa/iOS/UIBarButtonItem+Rx.swift +++ b/RxCocoa/iOS/UIBarButtonItem+Rx.swift @@ -38,8 +38,14 @@ extension UIBarButtonItem { Reactive wrapper for target action pattern on `self`. */ public var rx_tap: ControlEvent { - let source: Observable = AnonymousObservable { observer in - let target = BarButtonItemTarget(barButtonItem: self) { + let source: Observable = AnonymousObservable { [weak self] observer in + + guard let control = self else { + observer.on(.Completed) + return NopDisposable.instance + } + + let target = BarButtonItemTarget(barButtonItem: control) { observer.on(.Next()) } return target @@ -52,7 +58,7 @@ extension UIBarButtonItem { @objc -class BarButtonItemTarget: NSObject, Disposable { +class BarButtonItemTarget: RxTarget { typealias Callback = () -> Void weak var barButtonItem: UIBarButtonItem? @@ -66,12 +72,11 @@ class BarButtonItemTarget: NSObject, Disposable { barButtonItem.action = Selector("action:") } - deinit { - dispose() - } - - func dispose() { + override func dispose() { + super.dispose() +#if DEBUG MainScheduler.ensureExecutingOnScheduler() +#endif barButtonItem?.target = nil barButtonItem?.action = nil diff --git a/RxCocoa/iOS/UICollectionView+Rx.swift b/RxCocoa/iOS/UICollectionView+Rx.swift index 2bd20fd1..5a85c43f 100644 --- a/RxCocoa/iOS/UICollectionView+Rx.swift +++ b/RxCocoa/iOS/UICollectionView+Rx.swift @@ -143,10 +143,13 @@ extension UICollectionView { */ public func rx_modelSelected() -> ControlEvent { - let source: Observable = rx_itemSelected .map { indexPath in - let dataSource: RxCollectionViewReactiveArrayDataSource = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_itemsWith*` methods was used.") + let source: Observable = rx_itemSelected.flatMap { [weak self] indexPath -> Observable in + guard let view = self else { + return empty() + } + let dataSource: RxCollectionViewReactiveArrayDataSource = castOrFatalError(view.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_itemsWith*` methods was used.") - return dataSource.modelAtIndex(indexPath.item)! + return just(dataSource.modelAtIndex(indexPath.item)!) } return ControlEvent(source: source) diff --git a/RxCocoa/iOS/UIControl+Rx.swift b/RxCocoa/iOS/UIControl+Rx.swift index 63cb2245..c72465c8 100644 --- a/RxCocoa/iOS/UIControl+Rx.swift +++ b/RxCocoa/iOS/UIControl+Rx.swift @@ -41,10 +41,15 @@ extension UIControl { - parameter controlEvents: Filter for observed event types. */ public func rx_controlEvents(controlEvents: UIControlEvents) -> ControlEvent { - let source: Observable = AnonymousObservable { observer in + let source: Observable = AnonymousObservable { [weak self] observer in MainScheduler.ensureExecutingOnScheduler() - - let controlTarget = ControlTarget(control: self, controlEvents: controlEvents) { + + guard let control = self else { + observer.on(.Completed) + return NopDisposable.instance + } + + let controlTarget = ControlTarget(control: control, controlEvents: controlEvents) { control in observer.on(.Next()) } @@ -58,11 +63,15 @@ extension UIControl { } func rx_value(getter getter: () -> T, setter: T -> Void) -> ControlProperty { - let source: Observable = AnonymousObservable { observer in - + let source: Observable = AnonymousObservable { [weak self] observer in + guard let control = self else { + observer.on(.Completed) + return NopDisposable.instance + } + observer.on(.Next(getter())) - - let controlTarget = ControlTarget(control: self, controlEvents: [.EditingChanged, .ValueChanged]) { control in + + let controlTarget = ControlTarget(control: control, controlEvents: [.EditingChanged, .ValueChanged]) { control in observer.on(.Next(getter())) } @@ -73,7 +82,7 @@ extension UIControl { return ControlProperty(source: source, observer: AnyObserver { event in MainScheduler.ensureExecutingOnScheduler() - + switch event { case .Next(let value): setter(value) diff --git a/RxCocoa/iOS/UIGestureRecognizer+Rx.swift b/RxCocoa/iOS/UIGestureRecognizer+Rx.swift index 16685d1d..ec02538d 100644 --- a/RxCocoa/iOS/UIGestureRecognizer+Rx.swift +++ b/RxCocoa/iOS/UIGestureRecognizer+Rx.swift @@ -20,7 +20,7 @@ class GestureTarget: RxTarget { let selector = Selector("eventHandler:") - unowned let gestureRecognizer: UIGestureRecognizer + weak var gestureRecognizer: UIGestureRecognizer? var callback: Callback? init(_ gestureRecognizer: UIGestureRecognizer, callback: Callback) { @@ -38,15 +38,15 @@ class GestureTarget: RxTarget { } func eventHandler(sender: UIGestureRecognizer!) { - if let callback = self.callback { - callback(self.gestureRecognizer) + if let callback = self.callback, gestureRecognizer = self.gestureRecognizer { + callback(gestureRecognizer) } } override func dispose() { super.dispose() - self.gestureRecognizer.removeTarget(self, action: self.selector) + self.gestureRecognizer?.removeTarget(self, action: self.selector) self.callback = nil } } @@ -59,10 +59,15 @@ extension UIGestureRecognizer { public var rx_event: ControlEvent { let source: Observable = AnonymousObservable { [weak self] observer in MainScheduler.ensureExecutingOnScheduler() + + guard let control = self else { + observer.on(.Completed) + return NopDisposable.instance + } - let observer = GestureTarget(self!) { + let observer = GestureTarget(control) { control in - observer.on(.Next(self!)) + observer.on(.Next(control)) } return observer diff --git a/RxCocoa/iOS/UITableView+Rx.swift b/RxCocoa/iOS/UITableView+Rx.swift index 28d59935..911ea040 100644 --- a/RxCocoa/iOS/UITableView+Rx.swift +++ b/RxCocoa/iOS/UITableView+Rx.swift @@ -187,10 +187,14 @@ extension UITableView { */ public func rx_modelSelected() -> ControlEvent { - let source: Observable = rx_itemSelected.map { ip in - let dataSource: RxTableViewReactiveArrayDataSource = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_subscribeItemsTo` methods was used.") + let source: Observable = rx_itemSelected.flatMap { [weak self] ip -> Observable in + guard let view = self else { + return empty() + } + + let dataSource: RxTableViewReactiveArrayDataSource = castOrFatalError(view.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_subscribeItemsTo` methods was used.") - return dataSource.modelAtIndex(ip.item)! + return just(dataSource.modelAtIndex(ip.item)!) } return ControlEvent(source: source) diff --git a/RxTests/RxCocoaTests/Control+RxTests+Cocoa.swift b/RxTests/RxCocoaTests/Control+RxTests+Cocoa.swift new file mode 100644 index 00000000..9e69a780 --- /dev/null +++ b/RxTests/RxCocoaTests/Control+RxTests+Cocoa.swift @@ -0,0 +1,44 @@ +// +// Control+RxTests+Cocoa.swift +// RxTests +// +// Created by Krunoslav Zaher on 10/19/15. +// +// + +import Cocoa +import RxSwift +import RxCocoa +import XCTest + +// NSTextField +extension ControlTests { + func testTextField_TextCompletesOnDealloc() { + let createView: () -> NSTextField = { NSTextField(frame: CGRectMake(0, 0, 1, 1)) } + ensurePropertyDeallocated(createView, "a") { (view: NSTextField) in view.rx_text } + } +} + +// NSControl +extension ControlTests { + func testControl_DelegateEventCompletesOnDealloc() { + let createView: () -> NSControl = { NSControl(frame: CGRectMake(0, 0, 1, 1)) } + ensureEventDeallocated(createView) { (view: NSControl) in view.rx_controlEvents } + } +} + +// NSSlider +extension ControlTests { + func testCollectionView_DelegateEventCompletesOnDealloc() { + let createView: () -> NSSlider = { NSSlider(frame: CGRectMake(0, 0, 1, 1)) } + ensurePropertyDeallocated(createView, 0.3) { (view: NSSlider) in view.rx_value } + } +} + +// NSButton +extension ControlTests { + func testButton_DelegateEventCompletesOnDealloc() { + let createView: () -> NSButton = { NSButton(frame: CGRectMake(0, 0, 1, 1)) } + ensureEventDeallocated(createView) { (view: NSButton) in view.rx_tap } + } +} \ No newline at end of file diff --git a/RxTests/RxCocoaTests/Control+RxTests+UIKit.swift b/RxTests/RxCocoaTests/Control+RxTests+UIKit.swift new file mode 100644 index 00000000..63bba54e --- /dev/null +++ b/RxTests/RxCocoaTests/Control+RxTests+UIKit.swift @@ -0,0 +1,174 @@ +// +// Control+RxTests+UIKit.swift +// RxTests +// +// Created by Ash Furrow on 2015-07-04. +// +// + +import UIKit +import RxSwift +import RxCocoa +import XCTest + +extension ControlTests { + func testSubscribeEnabledToTrue() { + let subject = UIControl() + let enabledSequence = Variable(false) + enabledSequence.subscribe(subject.rx_enabled) + + enabledSequence.value = true + XCTAssert(subject.enabled == true, "Expected enabled set to true") + } + + func testSubscribeEnabledToFalse() { + let subject = UIControl() + let enabledSequence = Variable(true) + enabledSequence.subscribe(subject.rx_enabled) + + enabledSequence.value = false + XCTAssert(subject.enabled == false, "Expected enabled set to false") + } +} + +// UITextField +extension ControlTests { + func testTextField_TextCompletesOnDealloc() { + ensurePropertyDeallocated({ UITextField() }, "a") { (view: UITextField) in view.rx_text } + } +} + +// Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior () +// Don't know why can't use ActionSheet and AlertView inside unit tests + + +// UIActionSheet +extension ControlTests { + func testActionSheet_DelegateEventCompletesOnDealloc() { + let createActionSheet: () -> UIActionSheet = { UIActionSheet(title: "", delegate: nil, cancelButtonTitle: "", destructiveButtonTitle: "") } + ensureEventDeallocated(createActionSheet) { (view: UIActionSheet) in view.rx_clickedButtonAtIndex } + ensureEventDeallocated(createActionSheet) { (view: UIActionSheet) in view.rx_didDismissWithButtonIndex } + ensureEventDeallocated(createActionSheet) { (view: UIActionSheet) in view.rx_willDismissWithButtonIndex } + } +} + +// UIAlertView +extension ControlTests { + func testAlertView_DelegateEventCompletesOnDealloc() { + let createAlertView: () -> UIAlertView = { UIAlertView(title: "", message: "", delegate: nil, cancelButtonTitle: nil) } + ensureEventDeallocated(createAlertView) { (view: UIAlertView) in view.rx_clickedButtonAtIndex } + ensureEventDeallocated(createAlertView) { (view: UIAlertView) in view.rx_didDismissWithButtonIndex } + ensureEventDeallocated(createAlertView) { (view: UIAlertView) in view.rx_willDismissWithButtonIndex } + } +} + + +// UIBarButtonItem +extension ControlTests { + func testBarButtonItem_DelegateEventCompletesOnDealloc() { + ensureEventDeallocated({ UIBarButtonItem() }) { (view: UIBarButtonItem) in view.rx_tap } + } +} + +// UICollectionView +extension ControlTests { + func testCollectionView_DelegateEventCompletesOnDealloc() { + let layout = UICollectionViewFlowLayout() + let createView: () -> UICollectionView = { UICollectionView(frame: CGRectMake(0, 0, 1, 1), collectionViewLayout: layout) } + ensureEventDeallocated(createView) { (view: UICollectionView) in view.rx_itemSelected } + ensureEventDeallocated(createView) { (view: UICollectionView) in view.rx_modelSelected() as ControlEvent } + } +} + + +// UITableView +extension ControlTests { + func testTableView_DelegateEventCompletesOnDealloc() { + let createView: () -> UITableView = { UITableView(frame: CGRectMake(0, 0, 1, 1)) } + ensureEventDeallocated(createView) { (view: UITableView) in view.rx_itemSelected } + ensureEventDeallocated(createView) { (view: UITableView) in view.rx_modelSelected() as ControlEvent } + ensureEventDeallocated(createView) { (view: UITableView) in view.rx_itemDeleted } + ensureEventDeallocated(createView) { (view: UITableView) in view.rx_itemMoved } + ensureEventDeallocated(createView) { (view: UITableView) in view.rx_itemInserted } + } +} + +// UIControl +extension ControlTests { + func testControl_DelegateEventCompletesOnDealloc() { + let createView: () -> UIControl = { UIControl(frame: CGRectMake(0, 0, 1, 1)) } + ensureEventDeallocated(createView) { (view: UIControl) in view.rx_controlEvents(.AllEditingEvents) } + } +} + +// UIDatePicker +extension ControlTests { + func testDatePicker_DelegateEventCompletesOnDealloc() { + let createView: () -> UIDatePicker = { UIDatePicker(frame: CGRectMake(0, 0, 1, 1)) } + ensurePropertyDeallocated(createView, NSDate()) { (view: UIDatePicker) in view.rx_date } + } +} + +// UIGestureRecognizer +extension ControlTests { + func testGestureRecognizer_DelegateEventCompletesOnDealloc() { + let createView: () -> UIGestureRecognizer = { UIGestureRecognizer(target: nil, action: "s") } + ensureEventDeallocated(createView) { (view: UIGestureRecognizer) in view.rx_event } + } +} + +// UIScrollView +extension ControlTests { + func testScrollView_DelegateEventCompletesOnDealloc() { + let createView: () -> UIScrollView = { UIScrollView(frame: CGRectMake(0, 0, 1, 1)) } + ensurePropertyDeallocated(createView, CGPoint(x: 1, y: 1)) { (view: UIScrollView) in view.rx_contentOffset } + } +} + +// UISearchBar +extension ControlTests { + func testSearchBar_DelegateEventCompletesOnDealloc() { + let createView: () -> UISearchBar = { UISearchBar(frame: CGRectMake(0, 0, 1, 1)) } + ensurePropertyDeallocated(createView, "a") { (view: UISearchBar) in view.rx_text } + } +} + +// UISegmentedControl +extension ControlTests { + func testSegmentedControl_DelegateEventCompletesOnDealloc() { + let createView: () -> UISegmentedControl = { UISegmentedControl(items: ["a", "b", "c"]) } + ensurePropertyDeallocated(createView, 1) { (view: UISegmentedControl) in view.rx_value } + } +} + +// UISlider +extension ControlTests { + func testSlider_DelegateEventCompletesOnDealloc() { + let createView: () -> UISlider = { UISlider(frame: CGRectMake(0, 0, 1, 1)) } + ensurePropertyDeallocated(createView, 0.5) { (view: UISlider) in view.rx_value } + } +} + +// UIStepper +extension ControlTests { + func testStepper_DelegateEventCompletesOnDealloc() { + let createView: () -> UIStepper = { UIStepper(frame: CGRectMake(0, 0, 1, 1)) } + ensurePropertyDeallocated(createView, 1) { (view: UIStepper) in view.rx_value } + } +} + +// UISwitch +extension ControlTests { + func testSwitch_DelegateEventCompletesOnDealloc() { + let createView: () -> UISwitch = { UISwitch(frame: CGRectMake(0, 0, 1, 1)) } + ensurePropertyDeallocated(createView, true) { (view: UISwitch) in view.rx_value } + } +} + +// UITextView +extension ControlTests { + func testText_DelegateEventCompletesOnDealloc() { + let createView: () -> UITextView = { UITextView(frame: CGRectMake(0, 0, 1, 1)) } + ensurePropertyDeallocated(createView, "text") { (view: UITextView) in view.rx_text } + } +} \ No newline at end of file diff --git a/RxTests/RxCocoaTests/Control+RxTests.swift b/RxTests/RxCocoaTests/Control+RxTests.swift new file mode 100644 index 00000000..a409c687 --- /dev/null +++ b/RxTests/RxCocoaTests/Control+RxTests.swift @@ -0,0 +1,76 @@ +// +// Control+RxTests.swift +// RxTests +// +// Created by Krunoslav Zaher on 10/18/15. +// +// + +import Foundation +import RxCocoa +import RxSwift +import XCTest + +class ControlTests : RxTest { + + func ensurePropertyDeallocated(createControl: () -> C, _ initialValue: T, _ propertySelector: C -> ControlProperty) { + let variable = Variable(initialValue) + + + var completed = false + var deallocated = false + var lastReturnedPropertyValue: T! + + autoreleasepool { + var control: C! = createControl() + + let property = propertySelector(control) + + let disposable = variable.bindTo(property) + + _ = property.subscribe(onNext: { n in + lastReturnedPropertyValue = n + }, onCompleted: { + completed = true + disposable.dispose() + }) + + + _ = control.rx_deallocated.subscribeNext { _ in + deallocated = true + } + + control = nil + } + + XCTAssertTrue(deallocated) + XCTAssertTrue(completed) + XCTAssertEqual(initialValue, lastReturnedPropertyValue) + } + + func ensureEventDeallocated(createControl: () -> C, _ eventSelector: C -> ControlEvent) { + var completed = false + var deallocated = false + + autoreleasepool { + var control: C! = createControl() + let eventObservable = eventSelector(control) + + _ = eventObservable.subscribe(onNext: { n in + + }, onCompleted: { + completed = true + }) + + _ = control.rx_deallocated.subscribeNext { _ in + deallocated = true + } + + control = nil + } + + + XCTAssertTrue(deallocated) + XCTAssertTrue(completed) + } +} diff --git a/RxTests/RxCocoaTests/UIControl+RxTests.swift b/RxTests/RxCocoaTests/UIControl+RxTests.swift deleted file mode 100644 index 770ae194..00000000 --- a/RxTests/RxCocoaTests/UIControl+RxTests.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// UIControl+RxTests.swift -// RxTests -// -// Created by Ash Furrow on 2015-07-04. -// -// - -import UIKit -import RxSwift -import RxCocoa -import XCTest - -class UIControlRxTests : RxTest { - func testSubscribeEnabledToTrue() { - let subject = UIControl() - let enabledSequence = Variable(false) - enabledSequence.subscribe(subject.rx_enabled) - - enabledSequence.value = true - XCTAssert(subject.enabled == true, "Expected enabled set to true") - } - - func testSubscribeEnabledToFalse() { - let subject = UIControl() - let enabledSequence = Variable(true) - enabledSequence.subscribe(subject.rx_enabled) - - enabledSequence.value = false - XCTAssert(subject.enabled == false, "Expected enabled set to false") - } -} diff --git a/RxTests/RxTests.xcodeproj/project.pbxproj b/RxTests/RxTests.xcodeproj/project.pbxproj index 1ba1677b..8fe89feb 100644 --- a/RxTests/RxTests.xcodeproj/project.pbxproj +++ b/RxTests/RxTests.xcodeproj/project.pbxproj @@ -7,7 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - 5E5D10BB1B48355200432B25 /* UIControl+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E5D10BA1B48355200432B25 /* UIControl+RxTests.swift */; }; + 5E5D10BB1B48355200432B25 /* Control+RxTests+UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E5D10BA1B48355200432B25 /* Control+RxTests+UIKit.swift */; }; C69B65001BA88FAC00A7FA73 /* ObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69B64FF1BA88FAC00A7FA73 /* ObserverTests.swift */; }; C69B65011BA8957C00A7FA73 /* ObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69B64FF1BA88FAC00A7FA73 /* ObserverTests.swift */; }; C801EB5A1B97951100C4D8C4 /* Observable+CreationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C801EB591B97951100C4D8C4 /* Observable+CreationTest.swift */; }; @@ -112,6 +112,10 @@ C8E3812B1B2083C2008CDC33 /* PrimitiveMockObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E3812A1B2083C2008CDC33 /* PrimitiveMockObserver.swift */; }; C8E3812C1B2083C2008CDC33 /* PrimitiveMockObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E3812A1B2083C2008CDC33 /* PrimitiveMockObserver.swift */; }; C8E3813A1B21B77E008CDC33 /* Observable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E381221B2063CC008CDC33 /* Observable+Extensions.swift */; }; + C8E9D2BD1BD422D80079D0DB /* Control+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E9D2BC1BD422D80079D0DB /* Control+RxTests.swift */; settings = {ASSET_TAGS = (); }; }; + C8E9D2BE1BD422D80079D0DB /* Control+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E9D2BC1BD422D80079D0DB /* Control+RxTests.swift */; settings = {ASSET_TAGS = (); }; }; + C8E9D2BF1BD422D80079D0DB /* Control+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E9D2BC1BD422D80079D0DB /* Control+RxTests.swift */; settings = {ASSET_TAGS = (); }; }; + C8E9D2C41BD452650079D0DB /* Control+RxTests+Cocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E9D2C01BD4525B0079D0DB /* Control+RxTests+Cocoa.swift */; settings = {ASSET_TAGS = (); }; }; C8EA2D371BD02E1900FB22AC /* EquatableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EA2D361BD02E1900FB22AC /* EquatableArray.swift */; }; C8EA2D381BD02E1900FB22AC /* EquatableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EA2D361BD02E1900FB22AC /* EquatableArray.swift */; }; C8EA2D391BD02E1900FB22AC /* EquatableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EA2D361BD02E1900FB22AC /* EquatableArray.swift */; }; @@ -122,7 +126,7 @@ D203C4EE1BB9C22800D02D00 /* KVOObservableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8633AE41B0A9FF300375D60 /* KVOObservableTests.swift */; }; D203C4EF1BB9C22800D02D00 /* RxCocoaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B5BEA01B4A6A82000D732C /* RxCocoaTests.swift */; }; D203C4F01BB9C22800D02D00 /* NSObject+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81CC92A1B513FD400915606 /* NSObject+RxTests.swift */; }; - D203C5141BB9C54A00D02D00 /* UIControl+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E5D10BA1B48355200432B25 /* UIControl+RxTests.swift */; }; + D203C5141BB9C54A00D02D00 /* Control+RxTests+UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E5D10BA1B48355200432B25 /* Control+RxTests+UIKit.swift */; }; D251ED291BB9BF90002D0E36 /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468CC1B8A897800BF917B /* RxCocoa.framework */; }; D2AF91971BD2EBB900A008C1 /* MockDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AF91961BD2EBB900A008C1 /* MockDisposable.swift */; }; D2AF91991BD3D9C600A008C1 /* MockDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AF91961BD2EBB900A008C1 /* MockDisposable.swift */; }; @@ -172,7 +176,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 5E5D10BA1B48355200432B25 /* UIControl+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIControl+RxTests.swift"; sourceTree = ""; }; + 5E5D10BA1B48355200432B25 /* Control+RxTests+UIKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Control+RxTests+UIKit.swift"; sourceTree = ""; }; C69B64FF1BA88FAC00A7FA73 /* ObserverTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObserverTests.swift; sourceTree = ""; }; C801EB591B97951100C4D8C4 /* Observable+CreationTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+CreationTest.swift"; sourceTree = ""; }; C80DDEDB1BCE9A03006A1832 /* Driver+Test.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Driver+Test.swift"; sourceTree = ""; }; @@ -231,6 +235,8 @@ C8E381221B2063CC008CDC33 /* Observable+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Extensions.swift"; sourceTree = ""; }; C8E381271B207D03008CDC33 /* PrimitiveHotObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrimitiveHotObservable.swift; sourceTree = ""; }; C8E3812A1B2083C2008CDC33 /* PrimitiveMockObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrimitiveMockObserver.swift; sourceTree = ""; }; + C8E9D2BC1BD422D80079D0DB /* Control+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Control+RxTests.swift"; sourceTree = ""; }; + C8E9D2C01BD4525B0079D0DB /* Control+RxTests+Cocoa.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Control+RxTests+Cocoa.swift"; sourceTree = ""; }; C8EA2D361BD02E1900FB22AC /* EquatableArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EquatableArray.swift; sourceTree = ""; }; C8FDC5F71B2B5B7E0065F8D9 /* ElementIndexPair.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElementIndexPair.swift; sourceTree = ""; }; D2AF91961BD2EBB900A008C1 /* MockDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockDisposable.swift; sourceTree = ""; }; @@ -324,12 +330,14 @@ C811082A1AF50E2A001C13E4 /* RxCocoaTests */ = { isa = PBXGroup; children = ( - C811082B1AF50E2A001C13E4 /* NSNotificationCenterTests.swift */, - C814CEA61AF642D600E98087 /* UI+RxTests.swift */, - 5E5D10BA1B48355200432B25 /* UIControl+RxTests.swift */, + C8E9D2BC1BD422D80079D0DB /* Control+RxTests.swift */, + 5E5D10BA1B48355200432B25 /* Control+RxTests+UIKit.swift */, + C8E9D2C01BD4525B0079D0DB /* Control+RxTests+Cocoa.swift */, C8633AE41B0A9FF300375D60 /* KVOObservableTests.swift */, - C8B5BEA01B4A6A82000D732C /* RxCocoaTests.swift */, + C811082B1AF50E2A001C13E4 /* NSNotificationCenterTests.swift */, C81CC92A1B513FD400915606 /* NSObject+RxTests.swift */, + C8B5BEA01B4A6A82000D732C /* RxCocoaTests.swift */, + C814CEA61AF642D600E98087 /* UI+RxTests.swift */, ); path = RxCocoaTests; sourceTree = SOURCE_ROOT; @@ -608,10 +616,11 @@ C84B8FC21B89D0D500C9CCCF /* BagTest.swift in Sources */, C81108511AF50E2A001C13E4 /* HotObservable.swift in Sources */, C81108541AF50E2A001C13E4 /* Observable.Extensions.swift in Sources */, + C8E9D2BD1BD422D80079D0DB /* Control+RxTests.swift in Sources */, C897EC3B1B10E000009C2CB0 /* BehaviorSubjectTest.swift in Sources */, C81108681AF50E2A001C13E4 /* QueueTests.swift in Sources */, C81108651AF50E2A001C13E4 /* Observable+SingleTest.swift in Sources */, - 5E5D10BB1B48355200432B25 /* UIControl+RxTests.swift in Sources */, + 5E5D10BB1B48355200432B25 /* Control+RxTests+UIKit.swift in Sources */, C897EC4A1B1123DA009C2CB0 /* Observable+MultipleTest+Zip.swift in Sources */, D2AF91971BD2EBB900A008C1 /* MockDisposable.swift in Sources */, C81108571AF50E2A001C13E4 /* Recorded.swift in Sources */, @@ -660,6 +669,7 @@ C80DDEDD1BCE9A03006A1832 /* Driver+Test.swift in Sources */, C88BB89F1B07E64B0064D411 /* DisposableTest.swift in Sources */, C88BB8A01B07E64B0064D411 /* Observable+StandardSequenceOperatorsTest.swift in Sources */, + C8E9D2C41BD452650079D0DB /* Control+RxTests+Cocoa.swift in Sources */, C88BB8A11B07E64B0064D411 /* Observable+MultipleTest.swift in Sources */, C81CC92C1B513FD400915606 /* NSObject+RxTests.swift in Sources */, D2AF91991BD3D9C600A008C1 /* MockDisposable.swift in Sources */, @@ -683,6 +693,7 @@ C88BB8A91B07E64B0064D411 /* Observable+AggregateTest.swift in Sources */, C88BB8AA1B07E64B0064D411 /* MockObserver.swift in Sources */, C80DDEE11BCEE898006A1832 /* MainThreadPrimitiveHotObservable.swift in Sources */, + C8E9D2BE1BD422D80079D0DB /* Control+RxTests.swift in Sources */, C8AF26F01B499E5C00131C03 /* DelegateProxyTest.swift in Sources */, C69B65011BA8957C00A7FA73 /* ObserverTests.swift in Sources */, C8633AE61B0AA0ED00375D60 /* KVOObservableTests.swift in Sources */, @@ -729,8 +740,9 @@ D2EBEB5F1BB9B7DB003A27DC /* Subscription.swift in Sources */, D2EBEB691BB9B7EF003A27DC /* DelegateProxyTest.swift in Sources */, D2EBEB701BB9B7EF003A27DC /* Observable+MultipleTest.swift in Sources */, - D203C5141BB9C54A00D02D00 /* UIControl+RxTests.swift in Sources */, + D203C5141BB9C54A00D02D00 /* Control+RxTests+UIKit.swift in Sources */, D2EBEB581BB9B7CC003A27DC /* TestObservable.swift in Sources */, + C8E9D2BF1BD422D80079D0DB /* Control+RxTests.swift in Sources */, D2EBEB661BB9B7EF003A27DC /* BehaviorSubjectTest.swift in Sources */, D2EBEB671BB9B7EF003A27DC /* CompositeObserverTest.swift in Sources */, D2EBEB7A1BB9B804003A27DC /* PerformanceTools.swift in Sources */, From 0d33a77941d0ce525036d7b1f409e8dd6815906c Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 19 Oct 2015 00:54:15 +0200 Subject: [PATCH 067/210] Adds double binding example. --- .../Views/GitHubSignupViewController.swift | 42 +++++++++++++++---- RxExample/RxExample/OSX/Main.storyboard | 4 +- RxExample/RxExample/iOS/Main.storyboard | 16 +++---- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift b/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift index e274091f..55707b26 100644 --- a/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift +++ b/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift @@ -16,6 +16,27 @@ import RxCocoa typealias ValidationResult = (valid: Bool?, message: String?) typealias ValidationObservable = Observable +// Two way binding operator between control property and variable, that's all it takes { + +infix operator <-> { +} + +func <-> (property: ControlProperty, variable: Variable) -> Disposable { + let bindToUIDisposable = variable + .bindTo(property) + let bindToVariable = property + .subscribe(onNext: { n in + variable.value = n + }, onCompleted: { + bindToUIDisposable.dispose() + }) + + return StableCompositeDisposable.create(bindToUIDisposable, bindToVariable) +} + +// } + + class ValidationService { let API: GitHubAPI @@ -91,6 +112,10 @@ class GitHubSignupViewController : ViewController { @IBOutlet weak var signupOutlet: UIButton! @IBOutlet weak var signingUpOulet: UIActivityIndicatorView! + let username = Variable("") + let password = Variable("") + let repeatedPassword = Variable("") + struct ValidationColors { static let okColor = UIColor(red: 138.0 / 255.0, green: 221.0 / 255.0, blue: 109.0 / 255.0, alpha: 1.0) static let errorColor = UIColor.redColor() @@ -136,10 +161,13 @@ class GitHubSignupViewController : ViewController { let API = self.API let validationService = ValidationService(API: API) - - let username = usernameOutlet.rx_text - let password = passwordOutlet.rx_text - let repeatPassword = repeatedPasswordOutlet.rx_text + + // bind UI values to variables { + usernameOutlet.rx_text <-> username + passwordOutlet.rx_text <-> password + repeatedPasswordOutlet.rx_text <-> repeatedPassword + // } + let signupSampler = signupOutlet.rx_tap let usernameValidation = username @@ -148,14 +176,14 @@ class GitHubSignupViewController : ViewController { } .switchLatest() .shareReplay(1) - + let passwordValidation = password .map { password in return validationService.validatePassword(password) } .shareReplay(1) - - let repeatPasswordValidation = combineLatest(password, repeatPassword) { (password, repeatedPassword) in + + let repeatPasswordValidation = combineLatest(password, repeatedPassword) { (password, repeatedPassword) in validationService.validateRepeatedPassword(password, repeatedPassword: repeatedPassword) } .shareReplay(1) diff --git a/RxExample/RxExample/OSX/Main.storyboard b/RxExample/RxExample/OSX/Main.storyboard index 06e65af4..a54c6b13 100644 --- a/RxExample/RxExample/OSX/Main.storyboard +++ b/RxExample/RxExample/OSX/Main.storyboard @@ -1,7 +1,7 @@ - + - + diff --git a/RxExample/RxExample/iOS/Main.storyboard b/RxExample/RxExample/iOS/Main.storyboard index c1f21804..325d7582 100644 --- a/RxExample/RxExample/iOS/Main.storyboard +++ b/RxExample/RxExample/iOS/Main.storyboard @@ -1,7 +1,7 @@ - + - + @@ -451,8 +451,8 @@ - Unused disposable (unused-disposable) + +The same is valid for `subscribe*`, `bind*` and `drive*` family of functions that return `Disposable`. + +Warning is probably presented in a context similar to this one: + +```Swift +let xs: Observable .... + +xs + .filter { ... } + .map { ... } + .switchLatest() + .subscribe(onNext: { + ... + }, onError: { + ... + }) +``` + +`subscribe` function returns a subscription `Disposable` that can be used to cancel computation and free resources. + +Preferred way of terminating these fluent calls is by using `.addDisposableTo(disposeBag)` or in some equivalent way. + +```Swift +let xs: Observable .... +let disposeBag = DisposeBag() + +xs + .filter { ... } + .map { ... } + .switchLatest() + .subscribe(onNext: { + ... + }, onError: { + ... + }) + .addDisposableTo(disposeBag) // <--- note `addDisposableTo` +``` + +When `disposeBag` gets deallocated, subscription will be automatically disposed. + +In case `xs` terminates in a predictable way with `Completed` or `Error` message, not handling subscription `Disposable` won't leak any resources, but it's still preferred way because in that way element computation is terminated at predictable moment. + +That will also make your code robust and future proof because resources will be properly disposed even if `xs` implementation changes. + +Another way to make sure subscriptions and resources are tied with the lifetime of some object is by using `takeUntil` operator. + +```Swift +let xs: Observable .... +let someObject: NSObject ... + +_ = xs + .filter { ... } + .map { ... } + .switchLatest() + .takeUntil(someObject.rx_dellocated) // <-- note the `takeUntil` operator + .subscribe(onNext: { + ... + }, onError: { + ... + }) +``` + +If ignoring the subscription `Disposable` is desired behavior, this is how to silence the compiler warning. + +```Swift +let xs: Observable .... +let disposeBag = DisposeBag() + +_ = xs // <-- note the underscore + .filter { ... } + .map { ... } + .switchLatest() + .subscribe(onNext: { + ... + }, onError: { + ... + }) +``` + +### Unused observable sequence (unused-observable) + +Warning is probably presented in a context similar to this one: + +```Swift +let xs: Observable .... + +xs + .filter { ... } + .map { ... } +``` + +This code defines observable sequence that is filtered and mapped `xs` sequence but then ignores the result. + +Since this code just defines an observable sequence and then ignores it, it doesn't actually do nothing and it's pretty much useless. + +Your intention was probably to either store the observable sequence definition and use it later ... + +```Swift +let xs: Observable .... + +let ys = xs // <--- names definition as `ys` + .filter { ... } + .map { ... } +``` + +... or start computation based on that definition + +```Swift +let xs: Observable .... +let disposeBag = DisposeBag() + +xs + .filter { ... } + .map { ... } + .subscribeNext { nextElement in // <-- note the `subscribe*` method + ... probably print or something + } + .addDisposableTo(disposeBag) +``` From 24062521b56b647707fce0d350bc7287ec339986 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 19 Oct 2015 21:59:31 +0200 Subject: [PATCH 081/210] Adds unused warnings. --- .../ObservableConvertibleType+Blocking.swift | 1 + RxCocoa/Common/CocoaUnits/ControlEvent.swift | 2 ++ .../Common/CocoaUnits/ControlProperty.swift | 2 ++ .../Driver/ControlEvent+Driver.swift | 1 + .../Driver/ControlProperty+Driver.swift | 1 + .../Driver/Driver+Operators+arity.swift | 14 +++++++++++ .../Driver/Driver+Operators+arity.tt | 2 ++ .../CocoaUnits/Driver/Driver+Operators.swift | 19 +++++++++++++++ .../Driver/Driver+Subscription.swift | 5 ++++ RxCocoa/Common/CocoaUnits/Driver/Driver.swift | 23 ++++++++++++++++--- .../ObservableConvertibleType+Driver.swift | 3 +++ RxCocoa/Common/Observable+Bind.swift | 4 ++++ .../Observables/NSNotificationCenter+Rx.swift | 1 + .../NSObject+Rx+CoreGraphics.swift | 6 +++++ .../Common/Observables/NSURLSession+Rx.swift | 4 ++++ RxSwift/Observable+Extensions.swift | 11 +++++---- RxSwift/ObservableType.swift | 2 ++ .../Implementations/CombineLatest+arity.swift | 7 ++++++ .../Implementations/CombineLatest+arity.tt | 1 + .../Implementations/Zip+arity.swift | 7 ++++++ .../Observables/Implementations/Zip+arity.tt | 1 + .../Observables/Observable+Aggregate.swift | 2 ++ RxSwift/Observables/Observable+Binding.swift | 7 ++++++ .../Observables/Observable+Concurrency.swift | 2 ++ RxSwift/Observables/Observable+Creation.swift | 12 ++++++++++ RxSwift/Observables/Observable+Debug.swift | 1 + RxSwift/Observables/Observable+Multiple.swift | 15 ++++++++++++ RxSwift/Observables/Observable+Single.swift | 10 ++++++++ ...Observable+StandardSequenceOperators.swift | 11 +++++++++ RxSwift/Observables/Observable+Time.swift | 11 +++++++++ .../Tests/Observable+ConcurrencyTest.swift | 2 +- 31 files changed, 181 insertions(+), 9 deletions(-) diff --git a/RxBlocking/ObservableConvertibleType+Blocking.swift b/RxBlocking/ObservableConvertibleType+Blocking.swift index 8fe35391..2d7b255c 100644 --- a/RxBlocking/ObservableConvertibleType+Blocking.swift +++ b/RxBlocking/ObservableConvertibleType+Blocking.swift @@ -17,6 +17,7 @@ extension ObservableConvertibleType { - returns: `BlockingObservable` version of `self` */ + @warn_unused_result(message="http://git.io/rxs.uo") public func toBlocking() -> BlockingObservable { return BlockingObservable(source: self.asObservable()) } diff --git a/RxCocoa/Common/CocoaUnits/ControlEvent.swift b/RxCocoa/Common/CocoaUnits/ControlEvent.swift index 5201216a..16421af8 100644 --- a/RxCocoa/Common/CocoaUnits/ControlEvent.swift +++ b/RxCocoa/Common/CocoaUnits/ControlEvent.swift @@ -55,6 +55,7 @@ public struct ControlEvent : ControlEventType { /** - returns: `Observable` interface. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func asObservable() -> Observable { return self.source } @@ -62,6 +63,7 @@ public struct ControlEvent : ControlEventType { /** - returns: `ControlEvent` interface. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func asControlEvent() -> ControlEvent { return self } diff --git a/RxCocoa/Common/CocoaUnits/ControlProperty.swift b/RxCocoa/Common/CocoaUnits/ControlProperty.swift index 06bc3875..9af9569b 100644 --- a/RxCocoa/Common/CocoaUnits/ControlProperty.swift +++ b/RxCocoa/Common/CocoaUnits/ControlProperty.swift @@ -58,6 +58,7 @@ public struct ControlProperty : ControlPropertyType { /** - returns: `Observable` interface. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func asObservable() -> Observable { return self.source } @@ -65,6 +66,7 @@ public struct ControlProperty : ControlPropertyType { /** - returns: `ControlProperty` interface. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func asControlProperty() -> ControlProperty { return self } diff --git a/RxCocoa/Common/CocoaUnits/Driver/ControlEvent+Driver.swift b/RxCocoa/Common/CocoaUnits/Driver/ControlEvent+Driver.swift index d85eed07..093632b6 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/ControlEvent+Driver.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/ControlEvent+Driver.swift @@ -17,6 +17,7 @@ extension ControlEvent { `ControlEvent` already can't fail, so no special case needs to be handled. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func asDriver() -> Driver { return self.asDriver { (error) -> Driver in #if DEBUG diff --git a/RxCocoa/Common/CocoaUnits/Driver/ControlProperty+Driver.swift b/RxCocoa/Common/CocoaUnits/Driver/ControlProperty+Driver.swift index 0f733fd7..d42535a6 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/ControlProperty+Driver.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/ControlProperty+Driver.swift @@ -17,6 +17,7 @@ extension ControlProperty { `ControlProperty` already can't fail, so no special case needs to be handled. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func asDriver() -> Driver { return self.asDriver { (error) -> Driver in #if DEBUG diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.swift index 8ca3a622..7c3e0b22 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.swift @@ -23,6 +23,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func zip (source1: O1, _ source2: O2, resultSelector: (O1.E, O2.E) throws -> R) -> Driver { @@ -40,6 +41,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func combineLatest (source1: O1, _ source2: O2, resultSelector: (O1.E, O2.E) throws -> R) -> Driver { @@ -61,6 +63,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func zip (source1: O1, _ source2: O2, _ source3: O3, resultSelector: (O1.E, O2.E, O3.E) throws -> R) -> Driver { @@ -78,6 +81,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func combineLatest (source1: O1, _ source2: O2, _ source3: O3, resultSelector: (O1.E, O2.E, O3.E) throws -> R) -> Driver { @@ -99,6 +103,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: (O1.E, O2.E, O3.E, O4.E) throws -> R) -> Driver { @@ -116,6 +121,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func combineLatest (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: (O1.E, O2.E, O3.E, O4.E) throws -> R) -> Driver { @@ -137,6 +143,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> R) -> Driver { @@ -154,6 +161,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func combineLatest (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> R) -> Driver { @@ -175,6 +183,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> R) -> Driver { @@ -192,6 +201,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func combineLatest (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> R) -> Driver { @@ -213,6 +223,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> R) -> Driver { @@ -230,6 +241,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func combineLatest (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> R) -> Driver { @@ -251,6 +263,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> R) -> Driver { @@ -268,6 +281,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func combineLatest (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> R) -> Driver { diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.tt b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.tt index 349b85fa..c4b5cc08 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.tt +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.tt @@ -21,6 +21,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func zip<<%= (Array(1...i).map { "O\($0): DriverConvertibleType" }).joinWithSeparator(", ") %>, R> (<%= (Array(1...i).map { "source\($0): O\($0)" }).joinWithSeparator(", _ ") %>, resultSelector: (<%= (Array(1...i).map { "O\($0).E" }).joinWithSeparator(", ") %>) throws -> R) -> Driver { @@ -38,6 +39,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func combineLatest<<%= (Array(1...i).map { "O\($0): DriverConvertibleType" }).joinWithSeparator(", ") %>, R> (<%= (Array(1...i).map { "source\($0): O\($0)" }).joinWithSeparator(", _ ") %>, resultSelector: (<%= (Array(1...i).map { "O\($0).E" }).joinWithSeparator(", ") %>) throws -> R) -> Driver { diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift index 6099a39f..1d1a629d 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift @@ -19,6 +19,7 @@ extension DriverConvertibleType { - parameter selector: A transform function to apply to each source element. - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func map(selector: E -> R) -> Driver { let source = self .asObservable() @@ -32,6 +33,7 @@ extension DriverConvertibleType { - parameter predicate: A function to test each source element for a condition. - returns: An observable sequence that contains elements from the input sequence that satisfy the condition. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func filter(predicate: (E) -> Bool) -> Driver { let source = self .asObservable() @@ -51,6 +53,7 @@ extension DriverConvertibleType where E : DriverConvertibleType { - returns: The observable sequence that at any point in time produces the elements of the most recent inner observable sequence that has been received. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func switchLatest() -> Driver { let source: Observable = self .asObservable() @@ -68,6 +71,7 @@ extension DriverConvertibleType { - parameter eventHandler: Action to invoke for each event in the observable sequence. - returns: The source sequence with the side-effecting behavior applied. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func doOn(eventHandler: (Event) -> Void) -> Driver { let source = self.asObservable() @@ -84,6 +88,7 @@ extension DriverConvertibleType { - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence. - returns: The source sequence with the side-effecting behavior applied. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func doOn(onNext onNext: (E -> Void)? = nil, onError: (ErrorType -> Void)? = nil, onCompleted: (() -> Void)? = nil) -> Driver { let source = self.asObservable() @@ -101,6 +106,7 @@ extension DriverConvertibleType { - parameter identifier: Identifier that is printed together with event description to standard output. - returns: An observable sequence whose events are printed to standard output. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func debug(identifier: String = "\(__FILE__):\(__LINE__)") -> Driver { let source = self.asObservable() .debug(identifier) @@ -115,6 +121,7 @@ extension DriverConvertibleType where E: Equatable { - returns: An observable sequence only containing the distinct contiguous elements, based on equality operator, from the source sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func distinctUntilChanged() -> Driver { let source = self.asObservable() @@ -132,6 +139,7 @@ extension DriverConvertibleType { - parameter keySelector: A function to compute the comparison key for each element. - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value, from the source sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func distinctUntilChanged(keySelector: (E) -> K) -> Driver { let source = self.asObservable() .distinctUntilChanged(keySelector, comparer: { $0 == $1 }) @@ -144,6 +152,7 @@ extension DriverConvertibleType { - parameter comparer: Equality comparer for computed key values. - returns: An observable sequence only containing the distinct contiguous elements, based on `comparer`, from the source sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func distinctUntilChanged(comparer: (lhs: E, rhs: E) -> Bool) -> Driver { let source = self.asObservable() .distinctUntilChanged({ $0 }, comparer: comparer) @@ -157,6 +166,7 @@ extension DriverConvertibleType { - parameter comparer: Equality comparer for computed key values. - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value and the comparer, from the source sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func distinctUntilChanged(keySelector: (E) -> K, comparer: (lhs: K, rhs: K) -> Bool) -> Driver { let source = self.asObservable() .distinctUntilChanged(keySelector, comparer: comparer) @@ -173,6 +183,7 @@ extension DriverConvertibleType { - parameter selector: A transform function to apply to each element. - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func flatMap(selector: (E) -> Driver) -> Driver { let source = self.asObservable() .flatMap(selector) @@ -190,6 +201,7 @@ extension DriverConvertibleType where E : DriverConvertibleType { - parameter maxConcurrent: Maximum number of inner observable sequences being subscribed to concurrently. - returns: The observable sequence that merges the elements of the observable sequences. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func merge() -> Driver { let source = self.asObservable() .map { $0.asDriver() } @@ -202,6 +214,7 @@ extension DriverConvertibleType where E : DriverConvertibleType { - returns: The observable sequence that merges the elements of the inner sequences. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func merge(maxConcurrent maxConcurrent: Int) -> Driver { let source = self.asObservable() @@ -223,6 +236,7 @@ extension DriverConvertibleType { - parameter scheduler: Scheduler to run the throttle timers and send events on. - returns: The throttled sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func throttle(dueTime: S.TimeInterval, _ scheduler: S) -> Driver { let source = self.asObservable() @@ -240,6 +254,7 @@ extension DriverConvertibleType { - parameter scheduler: Scheduler to run the throttle timers and send events on. - returns: The throttled sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func debounce(dueTime: S.TimeInterval, _ scheduler: S) -> Driver { let source = self.asObservable() @@ -260,6 +275,7 @@ extension DriverConvertibleType { - parameter accumulator: An accumulator function to be invoked on each element. - returns: An observable sequence containing the accumulated values. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func scan(seed: A, accumulator: (A, E) -> A) -> Driver { let source = self.asObservable() @@ -275,6 +291,7 @@ extension SequenceType where Generator.Element : DriverConvertibleType { - returns: An observable sequence that contains the elements of each given sequence, in sequential order. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func concat() -> Driver { let source: Observable = self.lazy.map { $0.asDriver() }.concat() @@ -290,6 +307,7 @@ extension CollectionType where Generator.Element : DriverConvertibleType { - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func zip(resultSelector: [Generator.Element.E] throws -> R) -> Driver { let source: Observable = self.map { $0.asDriver() }.zip(resultSelector) return Driver(source) @@ -304,6 +322,7 @@ extension CollectionType where Generator.Element : DriverConvertibleType { - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func combineLatest(resultSelector: [Generator.Element.E] throws -> R) -> Driver { let source : Observable = self.map { $0.asDriver() }.combineLatest(resultSelector) return Driver(source) diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver+Subscription.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver+Subscription.swift index f3a18ace..c1a2fb4d 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/Driver+Subscription.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Subscription.swift @@ -20,6 +20,7 @@ extension DriverConvertibleType { - parameter observer: Observer that receives events. - returns: Disposable object that can be used to unsubscribe the observer from the subject. */ + @warn_unused_result(message="http://git.io/rxs.ud") public func drive(observer: O) -> Disposable { return self.asObservable().subscribe(observer) } @@ -30,6 +31,7 @@ extension DriverConvertibleType { - parameter with: Function used to bind elements from `self`. - returns: Object representing subscription. */ + @warn_unused_result(message="http://git.io/rxs.ud") public func drive(transformation: Observable -> R) -> R { return transformation(self.asObservable()) } @@ -46,6 +48,7 @@ extension DriverConvertibleType { - parameter curriedArgument: Final argument passed to `binder` to finish binding process. - returns: Object representing subscription. */ + @warn_unused_result(message="http://git.io/rxs.ud") public func drive(with: Observable -> R1 -> R2, curriedArgument: R1) -> R2 { return with(self.asObservable())(curriedArgument) } @@ -62,6 +65,7 @@ extension DriverConvertibleType { gracefully completed, errored, or if the generation is cancelled by disposing subscription) - returns: Subscription object used to unsubscribe from the observable sequence. */ + @warn_unused_result(message="http://git.io/rxs.ud") public func drive(onNext onNext: ((E) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil) -> Disposable { return self.asObservable().subscribe(onNext: onNext, onCompleted: onCompleted, onDisposed: onDisposed) } @@ -72,6 +76,7 @@ extension DriverConvertibleType { - parameter onNext: Action to invoke for each element in the observable sequence. - returns: Subscription object used to unsubscribe from the observable sequence. */ + @warn_unused_result(message="http://git.io/rxs.ud") public func driveNext(onNext: E -> Void) -> Disposable { return self.asObservable().subscribeNext(onNext) } diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver.swift index a101a020..7a95e627 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/Driver.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver.swift @@ -19,10 +19,12 @@ public protocol DriverConvertibleType : ObservableConvertibleType { /** Converts self to `Driver`. */ + @warn_unused_result(message="http://git.io/rxs.uo") func asDriver() -> Driver } extension DriverConvertibleType { + @warn_unused_result(message="http://git.io/rxs.uo") public func asObservable() -> Observable { return asDriver().asObservable() } @@ -62,11 +64,19 @@ public struct Driver : DriverConvertibleType { return Driver(raw: source.asObservable()) } #endif - + + /** + - returns: Built observable sequence. + */ + @warn_unused_result(message="http://git.io/rxs.uo") public func asObservable() -> Observable { return _source } - + + /** + - returns: `self` + */ + @warn_unused_result(message="http://git.io/rxs.uo") public func asDriver() -> Driver { return self } @@ -81,6 +91,7 @@ public struct Drive { - returns: An observable sequence with no elements. */ + @warn_unused_result(message="http://git.io/rxs.uo") public static func empty() -> Driver { return Driver(raw: RxSwift.empty().subscribeOn(ConcurrentMainScheduler.sharedInstance)) } @@ -90,6 +101,7 @@ public struct Drive { - returns: An observable sequence whose observers will never get called. */ + @warn_unused_result(message="http://git.io/rxs.uo") public static func never() -> Driver { return Driver(raw: RxSwift.never().subscribeOn(ConcurrentMainScheduler.sharedInstance)) } @@ -100,6 +112,7 @@ public struct Drive { - parameter element: Single element in the resulting observable sequence. - returns: An observable sequence containing the single specified element. */ + @warn_unused_result(message="http://git.io/rxs.uo") public static func just(element: E) -> Driver { return Driver(raw: RxSwift.just(element).subscribeOn(ConcurrentMainScheduler.sharedInstance)) } @@ -111,6 +124,7 @@ public struct Drive { - returns: An observable sequence with no elements. */ + @warn_unused_result(message="http://git.io/rxs.uo") public static func empty() -> Driver { return Driver(raw: _empty().subscribeOn(ConcurrentMainScheduler.sharedInstance)) } @@ -120,6 +134,7 @@ public struct Drive { - returns: An observable sequence whose observers will never get called. */ + @warn_unused_result(message="http://git.io/rxs.uo") public static func never() -> Driver { return Driver(raw: _never().subscribeOn(ConcurrentMainScheduler.sharedInstance)) } @@ -130,12 +145,14 @@ public struct Drive { - parameter element: Single element in the resulting observable sequence. - returns: An observable sequence containing the single specified element. */ + @warn_unused_result(message="http://git.io/rxs.uo") public static func just(element: E) -> Driver { return Driver(raw: _just(element).subscribeOn(ConcurrentMainScheduler.sharedInstance)) } #endif - + + @warn_unused_result(message="http://git.io/rxs.uo") public static func sequenceOf(elements: E ...) -> Driver { let source = elements.asObservable().subscribeOn(ConcurrentMainScheduler.sharedInstance) return Driver(raw: source) diff --git a/RxCocoa/Common/CocoaUnits/Driver/ObservableConvertibleType+Driver.swift b/RxCocoa/Common/CocoaUnits/Driver/ObservableConvertibleType+Driver.swift index 41abe7de..8473871b 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/ObservableConvertibleType+Driver.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/ObservableConvertibleType+Driver.swift @@ -18,6 +18,7 @@ extension ObservableConvertibleType { - parameter onErrorJustReturn: Element to return in case of error and after that complete the sequence. - returns: Driving observable sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func asDriver(onErrorJustReturn onErrorJustReturn: E) -> Driver { let source = self .asObservable() @@ -32,6 +33,7 @@ extension ObservableConvertibleType { - parameter onErrorDriveWith: Driver that continues to drive the sequence in case of error. - returns: Driving observable sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func asDriver(onErrorDriveWith onErrorDriveWith: Driver) -> Driver { let source = self .asObservable() @@ -48,6 +50,7 @@ extension ObservableConvertibleType { - parameter onErrorRecover: Calculates driver that continues to drive the sequence in case of error. - returns: Driving observable sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func asDriver(onErrorRecover onErrorRecover: (error: ErrorType) -> Driver) -> Driver { let source = self .asObservable() diff --git a/RxCocoa/Common/Observable+Bind.swift b/RxCocoa/Common/Observable+Bind.swift index 18542552..6dea933f 100644 --- a/RxCocoa/Common/Observable+Bind.swift +++ b/RxCocoa/Common/Observable+Bind.swift @@ -21,6 +21,7 @@ extension ObservableType { - parameter observer: Observer that receives events. - returns: Disposable object that can be used to unsubscribe the observer from the subject. */ + @warn_unused_result(message="http://git.io/rxs.ud") public func bindTo(observer: O) -> Disposable { return self.subscribe(observer) } @@ -31,6 +32,7 @@ extension ObservableType { - parameter binder: Function used to bind elements from `self`. - returns: Object representing subscription. */ + @warn_unused_result(message="http://git.io/rxs.ud") public func bindTo(binder: Self -> R) -> R { return binder(self) } @@ -47,6 +49,7 @@ extension ObservableType { - parameter curriedArgument: Final argument passed to `binder` to finish binding process. - returns: Object representing subscription. */ + @warn_unused_result(message="http://git.io/rxs.ud") public func bindTo(binder: Self -> R1 -> R2, curriedArgument: R1) -> R2 { return binder(self)(curriedArgument) } @@ -58,6 +61,7 @@ extension ObservableType { - parameter onNext: Action to invoke for each element in the observable sequence. - returns: Subscription object used to unsubscribe from the observable sequence. */ + @warn_unused_result(message="http://git.io/rxs.ud") public func bindNext(onNext: E -> Void) -> Disposable { return subscribeNext(onNext) } diff --git a/RxCocoa/Common/Observables/NSNotificationCenter+Rx.swift b/RxCocoa/Common/Observables/NSNotificationCenter+Rx.swift index 22b61ccf..ac5c7b35 100644 --- a/RxCocoa/Common/Observables/NSNotificationCenter+Rx.swift +++ b/RxCocoa/Common/Observables/NSNotificationCenter+Rx.swift @@ -19,6 +19,7 @@ extension NSNotificationCenter { - parameter object: Optional object used to filter notifications. - returns: Observable sequence of posted notifications. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func rx_notification(name: String, object: AnyObject? = nil) -> Observable { return create { observer in let nsObserver = self.addObserverForName(name, object: object, queue: nil) { notification in diff --git a/RxCocoa/Common/Observables/NSObject+Rx+CoreGraphics.swift b/RxCocoa/Common/Observables/NSObject+Rx+CoreGraphics.swift index 336ba7b9..765a6e38 100644 --- a/RxCocoa/Common/Observables/NSObject+Rx+CoreGraphics.swift +++ b/RxCocoa/Common/Observables/NSObject+Rx+CoreGraphics.swift @@ -29,6 +29,7 @@ extension NSObject { For more information take a look at `rx_observe` method. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func rx_observe(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { return rx_observe(keyPath, options: options, retainSelf: retainSelf) .map { (value: NSValue?) in @@ -51,6 +52,7 @@ extension NSObject { For more information take a look at `rx_observe` method. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func rx_observe(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { return rx_observe(keyPath, options: options, retainSelf: retainSelf) .map { (value: NSValue?) in @@ -73,6 +75,7 @@ extension NSObject { For more information take a look at `rx_observe` method. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func rx_observe(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { return rx_observe(keyPath, options: options, retainSelf: retainSelf) .map { (value: NSValue?) in @@ -101,6 +104,7 @@ extension NSObject { For more information take a look at `rx_observeWeakly` method. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func rx_observeWeakly(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { return rx_observeWeakly(keyPath, options: options) .map { (value: NSValue?) in @@ -123,6 +127,7 @@ extension NSObject { For more information take a look at `rx_observeWeakly` method. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func rx_observeWeakly(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { return rx_observeWeakly(keyPath, options: options) .map { (value: NSValue?) in @@ -145,6 +150,7 @@ extension NSObject { For more information take a look at `rx_observeWeakly` method. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func rx_observeWeakly(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { return rx_observeWeakly(keyPath, options: options) .map { (value: NSValue?) in diff --git a/RxCocoa/Common/Observables/NSURLSession+Rx.swift b/RxCocoa/Common/Observables/NSURLSession+Rx.swift index 164f88f6..a22476d7 100644 --- a/RxCocoa/Common/Observables/NSURLSession+Rx.swift +++ b/RxCocoa/Common/Observables/NSURLSession+Rx.swift @@ -74,6 +74,7 @@ extension NSURLSession { - parameter request: URL request. - returns: Observable sequence of URL responses. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func rx_response(request: NSURLRequest) -> Observable<(NSData!, NSURLResponse!)> { return create { observer in @@ -126,6 +127,7 @@ extension NSURLSession { - parameter request: URL request. - returns: Observable sequence of response data. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func rx_data(request: NSURLRequest) -> Observable { return rx_response(request).map { (data, response) -> NSData in guard let response = response as? NSHTTPURLResponse else { @@ -161,6 +163,7 @@ extension NSURLSession { - parameter request: URL request. - returns: Observable sequence of response JSON. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func rx_JSON(request: NSURLRequest) -> Observable { return rx_data(request).map { (data) -> AnyObject! in return try NSJSONSerialization.JSONObjectWithData(data ?? NSData(), options: []) @@ -184,6 +187,7 @@ extension NSURLSession { - parameter URL: URL of `NSURLRequest` request. - returns: Observable sequence of response JSON. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func rx_JSON(URL: NSURL) -> Observable { return rx_JSON(NSURLRequest(URL: URL)) } diff --git a/RxSwift/Observable+Extensions.swift b/RxSwift/Observable+Extensions.swift index 0f885d36..03045fe4 100644 --- a/RxSwift/Observable+Extensions.swift +++ b/RxSwift/Observable+Extensions.swift @@ -15,7 +15,7 @@ extension ObservableType { - parameter on: Action to invoke for each event in the observable sequence. - returns: Subscription object used to unsubscribe from the observable sequence. */ - @warn_unused_result + @warn_unused_result(message="http://git.io/rxs.ud") public func subscribe(on: (event: Event) -> Void) -> Disposable { let observer = AnonymousObserver { e in @@ -34,7 +34,7 @@ extension ObservableType { gracefully completed, errored, or if the generation is cancelled by disposing subscription) - returns: Subscription object used to unsubscribe from the observable sequence. */ - @warn_unused_result + @warn_unused_result(message="http://git.io/rxs.ud") public func subscribe(onNext onNext: (E -> Void)? = nil, onError: (ErrorType -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil) -> Disposable { @@ -71,7 +71,7 @@ extension ObservableType { - parameter onNext: Action to invoke for each element in the observable sequence. - returns: Subscription object used to unsubscribe from the observable sequence. */ - @warn_unused_result + @warn_unused_result(message="http://git.io/rxs.ud") public func subscribeNext(onNext: (E) -> Void) -> Disposable { let observer = AnonymousObserver { e in @@ -88,7 +88,7 @@ extension ObservableType { - parameter onRrror: Action to invoke upon errored termination of the observable sequence. - returns: Subscription object used to unsubscribe from the observable sequence. */ - @warn_unused_result + @warn_unused_result(message="http://git.io/rxs.ud") public func subscribeError(onError: (ErrorType) -> Void) -> Disposable { let observer = AnonymousObserver { e in @@ -105,7 +105,7 @@ extension ObservableType { - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence. - returns: Subscription object used to unsubscribe from the observable sequence. */ - @warn_unused_result + @warn_unused_result(message="http://git.io/rxs.ud") public func subscribeCompleted(onCompleted: () -> Void) -> Disposable { let observer = AnonymousObserver { e in @@ -121,6 +121,7 @@ public extension ObservableType { /** All internal subscribe calls go through this method */ + @warn_unused_result(message="http://git.io/rxs.ud") func subscribeSafe(observer: O) -> Disposable { return self.subscribe(observer) } diff --git a/RxSwift/ObservableType.swift b/RxSwift/ObservableType.swift index 9e79ee5c..0b9a13d4 100644 --- a/RxSwift/ObservableType.swift +++ b/RxSwift/ObservableType.swift @@ -40,6 +40,7 @@ public protocol ObservableType : ObservableConvertibleType { - returns: Subscription for `observer` that can be used to cancel production of sequence elements and free resources. */ + @warn_unused_result(message="http://git.io/rxs.ud") func subscribe(observer: O) -> Disposable } @@ -49,6 +50,7 @@ extension ObservableType { /** Default implementation of converting `ObservableType` to `Observable`. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func asObservable() -> Observable { return create(self.subscribe) } diff --git a/RxSwift/Observables/Implementations/CombineLatest+arity.swift b/RxSwift/Observables/Implementations/CombineLatest+arity.swift index 8f1e424b..b2be4979 100644 --- a/RxSwift/Observables/Implementations/CombineLatest+arity.swift +++ b/RxSwift/Observables/Implementations/CombineLatest+arity.swift @@ -20,6 +20,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func combineLatest (source1: O1, _ source2: O2, resultSelector: (O1.E, O2.E) throws -> R) -> Observable { @@ -96,6 +97,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func combineLatest (source1: O1, _ source2: O2, _ source3: O3, resultSelector: (O1.E, O2.E, O3.E) throws -> R) -> Observable { @@ -179,6 +181,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func combineLatest (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: (O1.E, O2.E, O3.E, O4.E) throws -> R) -> Observable { @@ -269,6 +272,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func combineLatest (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> R) -> Observable { @@ -366,6 +370,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func combineLatest (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> R) -> Observable { @@ -470,6 +475,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func combineLatest (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> R) -> Observable { @@ -581,6 +587,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func combineLatest (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> R) -> Observable { diff --git a/RxSwift/Observables/Implementations/CombineLatest+arity.tt b/RxSwift/Observables/Implementations/CombineLatest+arity.tt index 47706697..57ea23dd 100644 --- a/RxSwift/Observables/Implementations/CombineLatest+arity.tt +++ b/RxSwift/Observables/Implementations/CombineLatest+arity.tt @@ -18,6 +18,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func combineLatest<<%= (Array(1...i).map { "O\($0): ObservableType" }).joinWithSeparator(", ") %>, R> (<%= (Array(1...i).map { "source\($0): O\($0)" }).joinWithSeparator(", _ ") %>, resultSelector: (<%= (Array(1...i).map { "O\($0).E" }).joinWithSeparator(", ") %>) throws -> R) -> Observable { diff --git a/RxSwift/Observables/Implementations/Zip+arity.swift b/RxSwift/Observables/Implementations/Zip+arity.swift index 964ce655..7dab0d80 100644 --- a/RxSwift/Observables/Implementations/Zip+arity.swift +++ b/RxSwift/Observables/Implementations/Zip+arity.swift @@ -20,6 +20,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func zip (source1: O1, _ source2: O2, resultSelector: (O1.E, O2.E) throws -> R) -> Observable { @@ -108,6 +109,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func zip (source1: O1, _ source2: O2, _ source3: O3, resultSelector: (O1.E, O2.E, O3.E) throws -> R) -> Observable { @@ -204,6 +206,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: (O1.E, O2.E, O3.E, O4.E) throws -> R) -> Observable { @@ -308,6 +311,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> R) -> Observable { @@ -420,6 +424,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> R) -> Observable { @@ -540,6 +545,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> R) -> Observable { @@ -668,6 +674,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> R) -> Observable { diff --git a/RxSwift/Observables/Implementations/Zip+arity.tt b/RxSwift/Observables/Implementations/Zip+arity.tt index 456b3313..2783b731 100644 --- a/RxSwift/Observables/Implementations/Zip+arity.tt +++ b/RxSwift/Observables/Implementations/Zip+arity.tt @@ -18,6 +18,7 @@ Merges the specified observable sequences into one observable sequence by using - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func zip<<%= (Array(1...i).map { "O\($0): ObservableType" }).joinWithSeparator(", ") %>, R> (<%= (Array(1...i).map { "source\($0): O\($0)" }).joinWithSeparator(", _ ") %>, resultSelector: (<%= (Array(1...i).map { "O\($0).E" }).joinWithSeparator(", ") %>) throws -> R) -> Observable { diff --git a/RxSwift/Observables/Observable+Aggregate.swift b/RxSwift/Observables/Observable+Aggregate.swift index efe3ac90..93643f81 100644 --- a/RxSwift/Observables/Observable+Aggregate.swift +++ b/RxSwift/Observables/Observable+Aggregate.swift @@ -22,6 +22,7 @@ extension ObservableType { - parameter mapResult: A function to transform the final accumulator value into the result value. - returns: An observable sequence containing a single element with the final accumulator value. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func reduce(seed: A, _ accumulator: (A, E) throws -> A, mapResult: (A) throws -> R) -> Observable { return Reduce(source: self.asObservable(), seed: seed, accumulator: accumulator, mapResult: mapResult) @@ -36,6 +37,7 @@ extension ObservableType { - parameter accumulator: A accumulator function to be invoked on each element. - returns: An observable sequence containing a single element with the final accumulator value. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func reduce(seed: A, _ accumulator: (A, E) throws -> A) -> Observable { return Reduce(source: self.asObservable(), seed: seed, accumulator: accumulator, mapResult: { $0 }) diff --git a/RxSwift/Observables/Observable+Binding.swift b/RxSwift/Observables/Observable+Binding.swift index a3548121..3e1439d4 100644 --- a/RxSwift/Observables/Observable+Binding.swift +++ b/RxSwift/Observables/Observable+Binding.swift @@ -22,6 +22,7 @@ extension ObservableType { - parameter subject: Subject to push source elements into. - returns: A connectable observable sequence that upon connection causes the source sequence to push results into the specified subject. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func multicast(subject: S) -> ConnectableObservable { return ConnectableObservable(source: self.asObservable(), subject: subject) @@ -38,6 +39,7 @@ extension ObservableType { - parameter selector: Selector function which can use the multicasted source sequence subject to the policies enforced by the created subject. - returns: An observable sequence that contains the elements of a sequence produced by multicasting the source sequence within a selector function. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func multicast(subjectSelector: () throws -> S, selector: (Observable) throws -> Observable) -> Observable { return Multicast( @@ -59,6 +61,7 @@ extension ObservableType { - returns: A connectable observable sequence that shares a single subscription to the underlying sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func publish() -> ConnectableObservable> { return self.multicast(PublishSubject()) } @@ -76,6 +79,7 @@ extension ObservableType { - parameter bufferSize: Maximum element count of the replay buffer. - returns: A connectable observable sequence that shares a single subscription to the underlying sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func replay(bufferSize: Int) -> ConnectableObservable> { return self.multicast(ReplaySubject.create(bufferSize: bufferSize)) @@ -91,6 +95,7 @@ extension ConnectableObservableType { - returns: An observable sequence that stays connected to the source as long as there is at least one subscription to the observable sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func refCount() -> Observable { return RefCount(source: self) } @@ -107,6 +112,7 @@ extension ObservableType { - returns: An observable sequence that contains the elements of a sequence produced by multicasting the source sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func share() -> Observable { return self.publish().refCount() } @@ -124,6 +130,7 @@ extension ObservableType { - parameter bufferSize: Maximum element count of the replay buffer. - returns: An observable sequence that contains the elements of a sequence produced by multicasting the source sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func shareReplay(bufferSize: Int) -> Observable { if bufferSize == 1 { diff --git a/RxSwift/Observables/Observable+Concurrency.swift b/RxSwift/Observables/Observable+Concurrency.swift index b6959b37..652647c7 100644 --- a/RxSwift/Observables/Observable+Concurrency.swift +++ b/RxSwift/Observables/Observable+Concurrency.swift @@ -21,6 +21,7 @@ extension ObservableType { - parameter scheduler: Scheduler to notify observers on. - returns: The source sequence whose observations happen on the specified scheduler. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func observeOn(scheduler: ImmediateSchedulerType) -> Observable { if let scheduler = scheduler as? SerialDispatchQueueScheduler { @@ -49,6 +50,7 @@ extension ObservableType { - parameter scheduler: Scheduler to perform subscription and unsubscription actions on. - returns: The source sequence whose subscriptions and unsubscriptions happen on the specified scheduler. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func subscribeOn(scheduler: ImmediateSchedulerType) -> Observable { return SubscribeOn(source: self, scheduler: scheduler) diff --git a/RxSwift/Observables/Observable+Creation.swift b/RxSwift/Observables/Observable+Creation.swift index d4f50390..5df1e941 100644 --- a/RxSwift/Observables/Observable+Creation.swift +++ b/RxSwift/Observables/Observable+Creation.swift @@ -16,6 +16,7 @@ Creates an observable sequence from a specified subscribe method implementation. - parameter subscribe: Implementation of the resulting observable sequence's `subscribe` method. - returns: The observable sequence with the specified implementation for the `subscribe` method. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func create(subscribe: (AnyObserver) -> Disposable) -> Observable { return AnonymousObservable(subscribe) } @@ -27,6 +28,7 @@ Returns an empty observable sequence, using the specified scheduler to send out - returns: An observable sequence with no elements. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func empty() -> Observable { return Empty() } @@ -38,6 +40,7 @@ Returns a non-terminating observable sequence, which can be used to denote an in - returns: An observable sequence whose observers will never get called. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func never() -> Observable { return Never() } @@ -50,6 +53,7 @@ Returns an observable sequence that contains a single element. - parameter element: Single element in the resulting observable sequence. - returns: An observable sequence containing the single specified element. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func just(element: E) -> Observable { return Just(element: element) } @@ -61,6 +65,7 @@ This method creates a new Observable instance with a variable number of elements - returns: The observable sequence whose elements are pulled from the given arguments. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func sequenceOf(elements: E ...) -> Observable { return AnonymousObservable { observer in for element in elements { @@ -79,6 +84,7 @@ extension SequenceType { - returns: The observable sequence whose elements are pulled from the given enumerable sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func asObservable() -> Observable { return AnonymousObservable { observer in for element in self { @@ -98,6 +104,7 @@ Returns an observable sequence that terminates with an `error`. - returns: The observable sequence that terminates with specified error. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func failWith(error: ErrorType) -> Observable { return FailWith(error: error) } @@ -110,6 +117,7 @@ Returns an observable sequence that invokes the specified factory function whene - parameter observableFactory: Observable factory function to invoke for each observer that subscribes to the resulting sequence. - returns: An observable sequence whose observers trigger an invocation of the given observable factory function. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func deferred(observableFactory: () throws -> Observable) -> Observable { return Deferred(observableFactory: observableFactory) @@ -125,6 +133,7 @@ to run the loop send out observer messages. - parameter scheduler: Scheduler on which to run the generator loop. - returns: The generated sequence. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func generate(initialState: E, condition: E throws -> Bool, scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance, iterate: E throws -> E) -> Observable { return Generate(initialState: initialState, condition: condition, iterate: iterate, resultSelector: { $0 }, scheduler: scheduler) } @@ -137,6 +146,7 @@ Generates an observable sequence of integral numbers within a specified range, u - parameter scheduler: Scheduler to run the generator loop on. - returns: An observable sequence that contains a range of sequential integral numbers. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func range(start: Int, _ count: Int, _ scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance) -> Observable { if count < 0 { rxFatalError("count can't be negative") @@ -156,6 +166,7 @@ Generates an observable sequence that repeats the given element infinitely, usin - parameter scheduler: Scheduler to run the producer loop on. - returns: An observable sequence that repeats the given element infinitely. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func repeatElement(element: E, _ scheduler: ImmediateSchedulerType) -> Observable { return RepeatElement(element: element, scheduler: scheduler) } @@ -167,6 +178,7 @@ Constructs an observable sequence that depends on a resource object, whose lifet - parameter observableFactory: Factory function to obtain an observable sequence that depends on the obtained resource. - returns: An observable sequence whose lifetime controls the lifetime of the dependent resource object. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func using(resourceFactory: () throws -> R, observableFactory: R throws -> Observable) -> Observable { return Using(resourceFactory: resourceFactory, observableFactory: observableFactory) } diff --git a/RxSwift/Observables/Observable+Debug.swift b/RxSwift/Observables/Observable+Debug.swift index faa40ec5..74c49802 100644 --- a/RxSwift/Observables/Observable+Debug.swift +++ b/RxSwift/Observables/Observable+Debug.swift @@ -18,6 +18,7 @@ extension ObservableType { - parameter identifier: Identifier that is printed together with event description to standard output. - returns: An observable sequence whose events are printed to standard output. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func debug(identifier: String = "\(__FILE__):\(__LINE__)") -> Observable { return Debug(source: self.asObservable(), identifier: identifier) diff --git a/RxSwift/Observables/Observable+Multiple.swift b/RxSwift/Observables/Observable+Multiple.swift index 6cb14adb..6cfac490 100644 --- a/RxSwift/Observables/Observable+Multiple.swift +++ b/RxSwift/Observables/Observable+Multiple.swift @@ -18,6 +18,7 @@ extension CollectionType where Generator.Element : ObservableConvertibleType { - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func combineLatest(resultSelector: [Generator.Element.E] throws -> R) -> Observable { return CombineLatestCollectionType(sources: self, resultSelector: resultSelector) } @@ -33,6 +34,7 @@ extension CollectionType where Generator.Element : ObservableConvertibleType { - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func zip(resultSelector: [Generator.Element.E] throws -> R) -> Observable { return ZipCollectionType(sources: self, resultSelector: resultSelector) } @@ -51,6 +53,7 @@ extension ObservableType where E : ObservableConvertibleType { - returns: The observable sequence that at any point in time produces the elements of the most recent inner observable sequence that has been received. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func switchLatest() -> Observable { return Switch(sources: self.asObservable()) } @@ -66,6 +69,7 @@ extension ObservableType { - parameter second: Second observable sequence. - returns: An observable sequence that contains the elements of `self`, followed by those of the second sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func concat(second: O) -> Observable { return [self.asObservable(), second.asObservable()].concat() } @@ -78,6 +82,7 @@ extension SequenceType where Generator.Element : ObservableConvertibleType { - returns: An observable sequence that contains the elements of each given sequence, in sequential order. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func concat() -> Observable { return Concat(sources: self) @@ -91,6 +96,7 @@ extension ObservableType where E : ObservableConvertibleType { - returns: An observable sequence that contains the elements of each observed inner sequence, in sequential order. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func concat() -> Observable { return self.merge(maxConcurrent: 1) } @@ -106,6 +112,7 @@ extension ObservableType where E : ObservableConvertibleType { - parameter maxConcurrent: Maximum number of inner observable sequences being subscribed to concurrently. - returns: The observable sequence that merges the elements of the observable sequences. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func merge() -> Observable { return Merge(sources: self.asObservable(), maxConcurrent: 0) } @@ -115,6 +122,7 @@ extension ObservableType where E : ObservableConvertibleType { - returns: The observable sequence that merges the elements of the inner sequences. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func merge(maxConcurrent maxConcurrent: Int) -> Observable { return Merge(sources: self.asObservable(), maxConcurrent: maxConcurrent) @@ -131,6 +139,7 @@ extension ObservableType { - parameter handler: Error handler function, producing another observable sequence. - returns: An observable sequence containing the source sequence's elements, followed by the elements produced by the handler's resulting observable sequence in case an error occurred. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func catchError(handler: (ErrorType) throws -> Observable) -> Observable { return Catch(source: self.asObservable(), handler: handler) @@ -142,6 +151,7 @@ extension ObservableType { - parameter element: Last element in an observable sequence in case error occurs. - returns: An observable sequence containing the source sequence's elements, followed by the `element` in case an error occurred. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func catchErrorJustReturn(element: E) -> Observable { return Catch(source: self.asObservable(), handler: { _ in just(element) }) @@ -155,6 +165,7 @@ extension SequenceType where Generator.Element : ObservableConvertibleType { - returns: An observable sequence containing elements from consecutive source sequences until a source sequence terminates successfully. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func catchError() -> Observable { return CatchSequence(sources: self) @@ -171,6 +182,7 @@ extension ObservableType { - parameter other: Observable sequence that terminates propagation of elements of the source sequence. - returns: An observable sequence containing the elements of the source sequence up to the point the other sequence interrupted further propagation. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func takeUntil(other: O) -> Observable { return TakeUntil(source: self.asObservable(), other: other.asObservable()) @@ -187,6 +199,7 @@ extension ObservableType { - parameter other: Observable sequence that terminates propagation of elements of the source sequence. - returns: An observable sequence containing the elements of the source sequence up to the point the other sequence interrupted further propagation. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func skipUntil(other: O) -> Observable { return SkipUntil(source: self.asObservable(), other: other.asObservable()) @@ -203,6 +216,7 @@ extension ObservableType { - parameter right: Second observable sequence. - returns: An observable sequence that surfaces either of the given sequences, whichever reacted first. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func amb (right: O2) -> Observable { @@ -217,6 +231,7 @@ extension SequenceType where Generator.Element : ObservableConvertibleType { - returns: An observable sequence that surfaces any of the given sequences, whichever reacted first. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func amb() -> Observable { return self.reduce(never()) { a, o in diff --git a/RxSwift/Observables/Observable+Single.swift b/RxSwift/Observables/Observable+Single.swift index cfe98678..30b36990 100644 --- a/RxSwift/Observables/Observable+Single.swift +++ b/RxSwift/Observables/Observable+Single.swift @@ -17,6 +17,7 @@ extension ObservableType where E: Equatable { - returns: An observable sequence only containing the distinct contiguous elements, based on equality operator, from the source sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func distinctUntilChanged() -> Observable { return self.distinctUntilChanged({ $0 }, comparer: { ($0 == $1) }) @@ -30,6 +31,7 @@ extension ObservableType { - parameter keySelector: A function to compute the comparison key for each element. - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value, from the source sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func distinctUntilChanged(keySelector: (E) throws -> K) -> Observable { return self.distinctUntilChanged(keySelector, comparer: { $0 == $1 }) @@ -41,6 +43,7 @@ extension ObservableType { - parameter comparer: Equality comparer for computed key values. - returns: An observable sequence only containing the distinct contiguous elements, based on `comparer`, from the source sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func distinctUntilChanged(comparer: (lhs: E, rhs: E) throws -> Bool) -> Observable { return self.distinctUntilChanged({ $0 }, comparer: comparer) @@ -53,6 +56,7 @@ extension ObservableType { - parameter comparer: Equality comparer for computed key values. - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value and the comparer, from the source sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func distinctUntilChanged(keySelector: (E) throws -> K, comparer: (lhs: K, rhs: K) throws -> Bool) -> Observable { return DistinctUntilChanged(source: self.asObservable(), selector: keySelector, comparer: comparer) @@ -69,6 +73,7 @@ extension ObservableType { - parameter eventHandler: Action to invoke for each event in the observable sequence. - returns: The source sequence with the side-effecting behavior applied. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func doOn(eventHandler: (Event) throws -> Void) -> Observable { return Do(source: self.asObservable(), eventHandler: eventHandler) @@ -82,6 +87,7 @@ extension ObservableType { - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence. - returns: The source sequence with the side-effecting behavior applied. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func doOn(onNext onNext: (E throws -> Void)? = nil, onError: (ErrorType throws -> Void)? = nil, onCompleted: (() throws -> Void)? = nil) -> Observable { return Do(source: self.asObservable()) { e in @@ -107,6 +113,7 @@ extension ObservableType { - parameter elements: Elements to prepend to the specified sequence. - returns: The source sequence prepended with the specified values. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func startWith(elements: E ...) -> Observable { return StartWith(source: self.asObservable(), elements: elements) @@ -124,6 +131,7 @@ extension ObservableType { - returns: Observable sequence to repeat until it successfully terminates. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func retry() -> Observable { return CatchSequence(sources: InfiniteSequence(repeatedValue: self.asObservable())) } @@ -136,6 +144,7 @@ extension ObservableType { - parameter maxAttemptCount: Maximum number of times to repeat the sequence. - returns: An observable sequence producing the elements of the given sequence repeatedly until it terminates successfully. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func retry(maxAttemptCount: Int) -> Observable { return CatchSequence(sources: Repeat(count: maxAttemptCount, repeatedValue: self.asObservable())) @@ -155,6 +164,7 @@ extension ObservableType { - parameter accumulator: An accumulator function to be invoked on each element. - returns: An observable sequence containing the accumulated values. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func scan(seed: A, accumulator: (A, E) throws -> A) -> Observable { return Scan(source: self.asObservable(), seed: seed, accumulator: accumulator) diff --git a/RxSwift/Observables/Observable+StandardSequenceOperators.swift b/RxSwift/Observables/Observable+StandardSequenceOperators.swift index 680dd457..faa260df 100644 --- a/RxSwift/Observables/Observable+StandardSequenceOperators.swift +++ b/RxSwift/Observables/Observable+StandardSequenceOperators.swift @@ -18,6 +18,7 @@ extension ObservableType { - parameter predicate: A function to test each source element for a condition. - returns: An observable sequence that contains elements from the input sequence that satisfy the condition. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func filter(predicate: (E) throws -> Bool) -> Observable { return Filter(source: self.asObservable(), predicate: predicate) @@ -34,6 +35,7 @@ extension ObservableType { - parameter predicate: A function to test each element for a condition. - returns: An observable sequence that contains the elements from the input sequence that occur before the element at which the test no longer passes. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func takeWhile(predicate: (E) throws -> Bool) -> Observable { return TakeWhile(source: asObservable(), predicate: predicate) @@ -47,6 +49,7 @@ extension ObservableType { - parameter predicate: A function to test each element for a condition; the second parameter of the function represents the index of the source element. - returns: An observable sequence that contains the elements from the input sequence that occur before the element at which the test no longer passes. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func takeWhileWithIndex(predicate: (E, Int) throws -> Bool) -> Observable { return TakeWhile(source: asObservable(), predicate: predicate) @@ -63,6 +66,7 @@ extension ObservableType { - parameter count: The number of elements to return. - returns: An observable sequence that contains the specified number of elements from the start of the input sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func take(count: Int) -> Observable { if count == 0 { @@ -84,6 +88,7 @@ extension ObservableType { - parameter count: The number of elements to skip before returning the remaining elements. - returns: An observable sequence that contains the elements that occur after the specified index in the input sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func skip(count: Int) -> Observable { return SkipCount(source: self.asObservable(), count: count) @@ -100,6 +105,7 @@ extension ObservableType { - parameter predicate: A function to test each element for a condition. - returns: An observable sequence that contains the elements from the input sequence starting at the first element in the linear series that does not pass the test specified by predicate. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func skipWhile(predicate: (E) throws -> Bool) -> Observable { return SkipWhile(source: self.asObservable(), predicate: predicate) } @@ -111,6 +117,7 @@ extension ObservableType { - parameter predicate: A function to test each element for a condition; the second parameter of the function represents the index of the source element. - returns: An observable sequence that contains the elements from the input sequence starting at the first element in the linear series that does not pass the test specified by predicate. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func skipWhileWithIndex(predicate: (E, Int) throws -> Bool) -> Observable { return SkipWhile(source: self.asObservable(), predicate: predicate) } @@ -126,6 +133,7 @@ extension ObservableType { - parameter selector: A transform function to apply to each source element. - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func map(selector: E throws -> R) -> Observable { return Map(source: self.asObservable(), selector: selector) @@ -137,6 +145,7 @@ extension ObservableType { - parameter selector: A transform function to apply to each source element; the second parameter of the function represents the index of the source element. - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func mapWithIndex(selector: (E, Int) throws -> R) -> Observable { return Map(source: self.asObservable(), selector: selector) @@ -153,6 +162,7 @@ extension ObservableType { - parameter selector: A transform function to apply to each element. - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func flatMap(selector: (E) throws -> O) -> Observable { return FlatMap(source: self.asObservable(), selector: selector) @@ -164,6 +174,7 @@ extension ObservableType { - parameter selector: A transform function to apply to each element; the second parameter of the function represents the index of the source element. - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func flatMapWithIndex(selector: (E, Int) throws -> O) -> Observable { return FlatMap(source: self.asObservable(), selector: selector) diff --git a/RxSwift/Observables/Observable+Time.swift b/RxSwift/Observables/Observable+Time.swift index e0f2fe03..47d6b91c 100644 --- a/RxSwift/Observables/Observable+Time.swift +++ b/RxSwift/Observables/Observable+Time.swift @@ -20,6 +20,7 @@ extension ObservableType { - parameter scheduler: Scheduler to run the throttle timers and send events on. - returns: The throttled sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func throttle(dueTime: S.TimeInterval, _ scheduler: S) -> Observable { return Throttle(source: self.asObservable(), dueTime: dueTime, scheduler: scheduler) @@ -34,6 +35,7 @@ extension ObservableType { - parameter scheduler: Scheduler to run the throttle timers and send events on. - returns: The throttled sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func debounce(dueTime: S.TimeInterval, _ scheduler: S) -> Observable { return Throttle(source: self.asObservable(), dueTime: dueTime, scheduler: scheduler) @@ -54,6 +56,7 @@ extension ObservableType { - parameter sampler: Sampling tick sequence. - returns: Sampled observable sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func sample(sampler: O) -> Observable { return Sample(source: self.asObservable(), sampler: sampler.asObservable(), onlyNew: true) @@ -69,6 +72,7 @@ extension ObservableType { - parameter sampler: Sampling tick sequence. - returns: Sampled observable sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func sampleLatest(sampler: O) -> Observable { return Sample(source: self.asObservable(), sampler: sampler.asObservable(), onlyNew: false) @@ -84,6 +88,7 @@ Returns an observable sequence that produces a value after each period, using th - parameter scheduler: Scheduler to run the timer on. - returns: An observable sequence that produces a value after each period. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func interval(period: S.TimeInterval, _ scheduler: S) -> Observable { return Timer(dueTime: period, @@ -102,6 +107,7 @@ Returns an observable sequence that periodically produces a value after the spec - parameter scheduler: Scheduler to run timers on. - returns: An observable sequence that produces a value after due time has elapsed and then each period. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func timer(dueTime: S.TimeInterval, _ period: S.TimeInterval, _ scheduler: S) -> Observable { return Timer( @@ -118,6 +124,7 @@ Returns an observable sequence that produces a single value at the specified abs - parameter scheduler: Scheduler to run the timer on. - returns: An observable sequence that produces a value at due time. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func timer(dueTime: S.TimeInterval, _ scheduler: S) -> Observable { return Timer( @@ -138,6 +145,7 @@ extension ObservableType { - parameter scheduler: Scheduler to run the timer on. - returns: An observable sequence with the elements taken during the specified duration from the start of the source sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func take(duration: S.TimeInterval, _ scheduler: S) -> Observable { return TakeTime(source: self.asObservable(), duration: duration, scheduler: scheduler) @@ -155,6 +163,7 @@ extension ObservableType { - parameter scheduler: Scheduler to run the timer on. - returns: An observable sequence with the elements skipped during the specified duration from the start of the source sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func skip(duration: S.TimeInterval, _ scheduler: S) -> Observable { return SkipTime(source: self.asObservable(), duration: duration, scheduler: scheduler) @@ -173,6 +182,7 @@ extension ObservableType { - parameter scheduler: Scheduler to run the subscription delay timer on. - returns: Time-shifted sequence. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func delaySubscription(dueTime: S.TimeInterval, _ scheduler: S) -> Observable { return DelaySubscription(source: self.asObservable(), dueTime: dueTime, scheduler: scheduler) @@ -193,6 +203,7 @@ extension ObservableType { - parameter scheduler: Scheduler to run buffering timers on. - returns: An observable sequence of buffers. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func buffer(timeSpan timeSpan: S.TimeInterval, count: Int, scheduler: S) -> Observable<[E]> { return BufferTimeCount(source: self.asObservable(), timeSpan: timeSpan, count: count, scheduler: scheduler) diff --git a/RxTests/RxSwiftTests/Tests/Observable+ConcurrencyTest.swift b/RxTests/RxSwiftTests/Tests/Observable+ConcurrencyTest.swift index 8d7ed977..f2876c7b 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+ConcurrencyTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+ConcurrencyTest.swift @@ -318,7 +318,7 @@ class ObservableConcurrentSchedulerConcurrencyTest: ObservableConcurrencyTestBas let scheduler = self.createScheduler() XCTAssert(numberOfSerialDispatchQueueObservables == 0) - just(0).observeOn(scheduler) + _ = just(0).observeOn(scheduler) self.sleep(0.1) XCTAssert(numberOfSerialDispatchQueueObservables == 0) } From 9fb8e3f4ef93fc952ba3495681da480a9be49a7e Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 19 Oct 2015 22:00:57 +0200 Subject: [PATCH 082/210] Adds changelog for 2.0.0-beta.1 release. --- CHANGELOG.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77320a5e..491a5c21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,43 @@ All notable changes to this project will be documented in this file. --- +## [2.0.0-beta.1](https://github.com/ReactiveX/RxSwift/releases/tag/2.0.0-beta.1) + +#### Updated + +* Adds `Driver` unit. This unit uses Swift compiler to prove certain properties about observable sequences. Specifically + * that fallback error handling is put in place + * results are observed on main thread + * work is performed only when there is a need (at least one subscriber) + * computation results are shared between different observers (replay latest element) +* Renames `ObserverOf` to `AnyObserver`. +* Adds new interface `ObservableConvertibleType`. +* Adds `BlockingObservable` to `RxBlocking` and makes it more consistent with `RxJava`. +* Renames `func subscribe(next:error:completed:disposed:)` to `func subscribe(onNext:onError:onCompleted:onDisposed:)` +* Adds concat convenience method `public func concat(second: O) -> RxSwift.Observable` +* Adds `skipUntil` operator. +* Adds `takeWhile` operator. +* Renames `takeWhile` indexed version to `takeWhileWithIndex` +* Adds `skipWhile` operator. +* Adds `skipWhileWithIndex` operator. +* Adds `using` operator. +* Renames `func doOn(next:error:completed:)` to `func doOn(onNext:onError:onCompleted:)`. +* Makes `RecursiveImmediateSchedulerOf` private. +* Makes `RecursiveSchedulerOf` private. +* Adds `ConcurrentMainScheduler`. +* Adds overflow error so now in case of overflow, operators will return `RxErrorCode.Overflow`. +* Adds `rx_modelAtIndexPath` to `UITableView` and `UICollectionView`. +* Adds `var rx_didUpdateFocusInContextWithAnimationCoordinator: ControlEvent<(context:animationCoordinator:)>` to `UITableView` and `UICollectionView` +* Makes `resultSelector` argument in `combineLatest` explicit `func combineLatest(source1: O1, _ source2: O2, resultSelector: (O1.E, O2.E) throws -> R) -> RxSwift.Observable`. +* Makes `resultSelector` argument in `zip` explicit `func combineLatest(source1: O1, _ source2: O2, resultSelector: (O1.E, O2.E) throws -> R) -> RxSwift.Observable`. +* Adds activity indicator example in `RxExample` app. +* Adds two way binding example in `RxExample` app. +* many other small features + +#### Fixed + +* Problem with xcodebuild 7.0.1 treating tvOS shared schemes as osx schemes. + ## [2.0.0-alpha.4](https://github.com/ReactiveX/RxSwift/releases/tag/2.0.0-alpha.4) #### Updated From e13747fab8f256c9b00ca6f288099f069d3788d9 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Tue, 20 Oct 2015 09:54:55 +0200 Subject: [PATCH 083/210] Updates `Readme.md`. --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4b03fe58..4213e120 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Xcode 7 beta 6 (7A192o) / Swift 2.0 required -**This README.md describes alpha version of RxSwift 2.0.** +**This README.md describes beta version of RxSwift 2.0.** **You can find RxSwift 1.9 for Swift 1.2 [here](https://github.com/ReactiveX/RxSwift/tree/rxswift-1.0).** @@ -462,9 +462,9 @@ Open Rx.xcworkspace, choose `RxExample` and hit run. This method will build ever # Podfile use_frameworks! -pod 'RxSwift', '~> 2.0.0-alpha' -pod 'RxCocoa', '~> 2.0.0-alpha' -pod 'RxBlocking', '~> 2.0.0-alpha' +pod 'RxSwift', '~> 2.0.0-beta' +pod 'RxCocoa', '~> 2.0.0-beta' +pod 'RxBlocking', '~> 2.0.0-beta' ``` type in `Podfile` directory @@ -478,7 +478,7 @@ $ pod install Add this to `Cartfile` ``` -git "git@github.com:ReactiveX/RxSwift.git" "2.0.0-alpha.4" +git "git@github.com:ReactiveX/RxSwift.git" "2.0.0-beta.1" ``` ``` From 642b84e946792cb22ca72613b0410132d6edf5f2 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Tue, 20 Oct 2015 11:16:31 +0200 Subject: [PATCH 084/210] Release 2.0.0-beta.1 --- RxBlocking.podspec | 4 ++-- RxCocoa.podspec | 4 ++-- RxSwift.podspec | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/RxBlocking.podspec b/RxBlocking.podspec index fce3d161..b5ba454a 100644 --- a/RxBlocking.podspec +++ b/RxBlocking.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "RxBlocking" - s.version = "2.0.0-alpha.4" + s.version = "2.0.0-beta.1" s.summary = "RxSwift Blocking operatos" s.description = <<-DESC Set of blocking operators for unit testing @@ -19,5 +19,5 @@ Pod::Spec.new do |s| s.source_files = 'RxBlocking/**/*.swift' - s.dependency 'RxSwift', '~> 2.0.0-alpha' + s.dependency 'RxSwift', '~> 2.0.0-beta' end diff --git a/RxCocoa.podspec b/RxCocoa.podspec index 6d087620..3fc18e9e 100644 --- a/RxCocoa.podspec +++ b/RxCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "RxCocoa" - s.version = "2.0.0-alpha.4" + s.version = "2.0.0-beta.1" s.summary = "RxSwift Cocoa extensions" s.description = <<-DESC * UI extensions @@ -25,5 +25,5 @@ Pod::Spec.new do |s| s.watchos.source_files = 'RxCocoa/iOS/**/*.swift' s.tvos.source_files = 'RxCocoa/iOS/**/*.swift' - s.dependency 'RxSwift', '~> 2.0.0-alpha' + s.dependency 'RxSwift', '~> 2.0.0-beta' end diff --git a/RxSwift.podspec b/RxSwift.podspec index 6d837274..47f4ca0f 100644 --- a/RxSwift.podspec +++ b/RxSwift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "RxSwift" - s.version = "2.0.0-alpha.4" + s.version = "2.0.0-beta.1" s.summary = "Microsoft Reactive Extensions (Rx) for Swift and iOS/OSX platform" s.description = <<-DESC This is a Swift port of Reactive extensions. From e453820c08321f216cac1225988a0c91fbe17fba Mon Sep 17 00:00:00 2001 From: yury Date: Tue, 20 Oct 2015 12:43:35 +0300 Subject: [PATCH 085/210] Add WithLatestFrom operator --- Rx.xcodeproj/project.pbxproj | 10 + RxExample/RxExample.xcodeproj/project.pbxproj | 6 +- .../Implementations/WithLatestFrom.swift | 131 +++++++++ RxSwift/Observables/Observable+Multiple.swift | 16 ++ .../Tests/Observable+MultipleTest.swift | 269 ++++++++++++++++++ 5 files changed, 431 insertions(+), 1 deletion(-) create mode 100644 RxSwift/Observables/Implementations/WithLatestFrom.swift diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index 5065dec5..ad928926 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -585,6 +585,10 @@ D2138C981BB9BEEE00339B5C /* RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E9B1B8A732E0088E94D /* RxCocoa.swift */; }; D2138C991BB9BEEE00339B5C /* RxTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E9C1B8A732E0088E94D /* RxTarget.swift */; }; D21C29311BC6A1C300448E70 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D285BAC31BC0231000B3F602 /* SkipUntil.swift */; }; + D2245A1B1BD5657300E7146F /* WithLatestFrom.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2245A1A1BD5657300E7146F /* WithLatestFrom.swift */; }; + D2245A1C1BD63C4600E7146F /* WithLatestFrom.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2245A1A1BD5657300E7146F /* WithLatestFrom.swift */; }; + D2245A1D1BD63C4700E7146F /* WithLatestFrom.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2245A1A1BD5657300E7146F /* WithLatestFrom.swift */; }; + D2245A1E1BD63C4A00E7146F /* WithLatestFrom.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2245A1A1BD5657300E7146F /* WithLatestFrom.swift */; }; D22B6D261BC8504A00BCE0AB /* SkipWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22B6D251BC8504A00BCE0AB /* SkipWhile.swift */; }; D235B23E1BD003DD007E84DA /* Using.swift in Sources */ = {isa = PBXBuildFile; fileRef = D235B23D1BD003DD007E84DA /* Using.swift */; }; D235B23F1BD003DD007E84DA /* Using.swift in Sources */ = {isa = PBXBuildFile; fileRef = D235B23D1BD003DD007E84DA /* Using.swift */; }; @@ -965,6 +969,7 @@ C8F0C04B1BBBFBB9001B112F /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8F0C0581BBBFBCE001B112F /* RxBlocking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxBlocking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D2138C751BB9BE9800339B5C /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D2245A1A1BD5657300E7146F /* WithLatestFrom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WithLatestFrom.swift; sourceTree = ""; }; D22B6D251BC8504A00BCE0AB /* SkipWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipWhile.swift; sourceTree = ""; }; D235B23D1BD003DD007E84DA /* Using.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Using.swift; sourceTree = ""; }; D285BAC31BC0231000B3F602 /* SkipUntil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipUntil.swift; sourceTree = ""; }; @@ -1151,6 +1156,7 @@ C8093C6A1B8A72BE0088E94D /* Implementations */ = { isa = PBXGroup; children = ( + D2245A1A1BD5657300E7146F /* WithLatestFrom.swift */, C8093C6B1B8A72BE0088E94D /* Amb.swift */, C8C3DA111B93A3EA004D233E /* AnonymousObservable.swift */, C8093C6D1B8A72BE0088E94D /* AsObservable.swift */, @@ -2084,6 +2090,7 @@ C8093D3C1B8A72BE0088E94D /* Skip.swift in Sources */, C8B144FC1BD2D44500267DCE /* ConcurrentMainScheduler.swift in Sources */, C8093CF01B8A72BE0088E94D /* StableCompositeDisposable.swift in Sources */, + D2245A1C1BD63C4600E7146F /* WithLatestFrom.swift in Sources */, C8093D4E1B8A72BE0088E94D /* Zip+arity.swift in Sources */, C8093D4C1B8A72BE0088E94D /* Timer.swift in Sources */, C8C3DA071B9393AC004D233E /* Empty.swift in Sources */, @@ -2203,6 +2210,7 @@ C8093D3B1B8A72BE0088E94D /* Skip.swift in Sources */, C8B144FB1BD2D44500267DCE /* ConcurrentMainScheduler.swift in Sources */, C8093CEF1B8A72BE0088E94D /* StableCompositeDisposable.swift in Sources */, + D2245A1B1BD5657300E7146F /* WithLatestFrom.swift in Sources */, C8093D4D1B8A72BE0088E94D /* Zip+arity.swift in Sources */, C8093D4B1B8A72BE0088E94D /* Timer.swift in Sources */, C8C3DA061B9393AC004D233E /* Empty.swift in Sources */, @@ -2322,6 +2330,7 @@ C8F0BF9B1BBBFB8B001B112F /* Skip.swift in Sources */, C8B144FE1BD2D44500267DCE /* ConcurrentMainScheduler.swift in Sources */, C8F0BF9C1BBBFB8B001B112F /* StableCompositeDisposable.swift in Sources */, + D2245A1E1BD63C4A00E7146F /* WithLatestFrom.swift in Sources */, C8F0BF9D1BBBFB8B001B112F /* Zip+arity.swift in Sources */, C8F0BF9E1BBBFB8B001B112F /* Timer.swift in Sources */, C8F0BF9F1BBBFB8B001B112F /* Empty.swift in Sources */, @@ -2589,6 +2598,7 @@ D2EBEAF11BB9B6AE003A27DC /* BinaryDisposable.swift in Sources */, C8B144FD1BD2D44500267DCE /* ConcurrentMainScheduler.swift in Sources */, D2EBEB1B1BB9B6C1003A27DC /* Repeat.swift in Sources */, + D2245A1D1BD63C4700E7146F /* WithLatestFrom.swift in Sources */, D2EBEAF81BB9B6B2003A27DC /* ScopedDisposable.swift in Sources */, D2EBEAEA1BB9B697003A27DC /* SchedulerType.swift in Sources */, D2EBEB031BB9B6C1003A27DC /* CombineLatest+CollectionType.swift in Sources */, diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index ebc65c94..44ac88cc 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -311,7 +311,8 @@ C8DF92EA1B0B38C0009BCF9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; }; C8DF92EB1B0B38C0009BCF9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; }; C8DF92F61B0B43A4009BCF9A /* IntroductionExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DF92F51B0B43A4009BCF9A /* IntroductionExampleViewController.swift */; }; - C8E9D2AF1BD3FD960079D0DB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; settings = {ASSET_TAGS = (); }; }; + C8E9D2AF1BD3FD960079D0DB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; }; + D2245A191BD5654C00E7146F /* WithLatestFrom.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2245A0B1BD564A700E7146F /* WithLatestFrom.swift */; }; D2AF91981BD3D95900A008C1 /* Using.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AF91881BD2C51900A008C1 /* Using.swift */; }; EC91FB951BBA144400973245 /* GitHubSearchRepositoriesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC91FB941BBA144400973245 /* GitHubSearchRepositoriesViewController.swift */; }; /* End PBXBuildFile section */ @@ -688,6 +689,7 @@ C8DF92F01B0B3E67009BCF9A /* Info-OSX.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-OSX.plist"; sourceTree = ""; }; C8DF92F21B0B3E71009BCF9A /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = ""; }; C8DF92F51B0B43A4009BCF9A /* IntroductionExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = IntroductionExampleViewController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D2245A0B1BD564A700E7146F /* WithLatestFrom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WithLatestFrom.swift; sourceTree = ""; }; D2AF91881BD2C51900A008C1 /* Using.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Using.swift; sourceTree = ""; }; EC91FB941BBA144400973245 /* GitHubSearchRepositoriesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubSearchRepositoriesViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -1135,6 +1137,7 @@ C89464761BC6C2B00055219D /* Zip+arity.swift */, C89464771BC6C2B00055219D /* Zip+arity.tt */, C89464781BC6C2B00055219D /* Zip+CollectionType.swift */, + D2245A0B1BD564A700E7146F /* WithLatestFrom.swift */, ); path = Implementations; sourceTree = ""; @@ -1707,6 +1710,7 @@ C89464B11BC6C2B00055219D /* SingleAssignmentDisposable.swift in Sources */, C89464AA1BC6C2B00055219D /* DisposeBase.swift in Sources */, C89465871BC6C2BC0055219D /* RxCollectionViewDelegateProxy.swift in Sources */, + D2245A191BD5654C00E7146F /* WithLatestFrom.swift in Sources */, C8297E3D1B6CF905000589EA /* SearchViewModel.swift in Sources */, C89464E61BC6C2B00055219D /* Timer.swift in Sources */, C8297E3E1B6CF905000589EA /* DetailViewController.swift in Sources */, diff --git a/RxSwift/Observables/Implementations/WithLatestFrom.swift b/RxSwift/Observables/Implementations/WithLatestFrom.swift new file mode 100644 index 00000000..f205e1e7 --- /dev/null +++ b/RxSwift/Observables/Implementations/WithLatestFrom.swift @@ -0,0 +1,131 @@ +// +// WithLatestFrom.swift +// RxExample +// +// Created by Yury Korolev on 10/19/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +class WithLatestFromSink : Sink { + + typealias Parent = WithLatestFrom + typealias SecondType = SecondO.E + + private let _parent: Parent + + private var _lock = NSRecursiveLock() + private var _latest: SecondO.E? + + + init(parent: Parent, observer: O, cancel: Disposable) { + _parent = parent + + super.init(observer: observer, cancel: cancel) + } + + func run() -> Disposable { + let sndSubscription = SingleAssignmentDisposable() + let fstO = WithLatestFromFirst(parent: self) + let sndO = WithLatestFromSecond(parent: self, disposable: sndSubscription) + + let fstSubscription = _parent._first.subscribeSafe(fstO) + sndSubscription.disposable = _parent._second.subscribeSafe(sndO) + + return StableCompositeDisposable.create(fstSubscription, sndSubscription) + } +} + +class WithLatestFromFirst: ObserverType { + + typealias Parent = WithLatestFromSink + typealias E = FirstO.E + private let _parent: Parent + + init(parent: Parent) { + _parent = parent + } + + func on(event: Event) { + switch event { + case let .Next(value): + guard let latest = _parent._latest else { return } + do { + let res = try _parent._parent._resultSelector(value, latest) + + _parent._lock.performLocked { + _parent.observer?.onNext(res) + } + } catch let e { + _parent._lock.performLocked { + _parent.observer?.onError(e) + _parent.dispose() + } + } + case .Completed: + _parent._lock.performLocked { + _parent.observer?.onComplete() + _parent.dispose() + } + case let .Error(error): + _parent._lock.performLocked { + _parent.observer?.onError(error) + _parent.dispose() + } + } + } +} + +class WithLatestFromSecond: ObserverType { + + typealias Parent = WithLatestFromSink + typealias E = SecondO.E + + private let _parent: Parent + private let _disposable: Disposable + + init(parent: Parent, disposable: Disposable) { + _parent = parent + _disposable = disposable + } + + func on(event: Event) { + switch event { + case let .Next(value): + _parent._latest = value + case .Completed: + _disposable.dispose() + case let .Error(error): + _parent._lock.performLocked { + _parent.observer?.onError(error) + _parent.dispose() + } + } + } + +} + +class WithLatestFrom: Producer { + + typealias FirstType = FirstO.E + typealias SecondType = SecondO.E + typealias ResultSelector = (FirstType, SecondType) throws -> ResultType + + private let _first: FirstO + private let _second: SecondO + private let _resultSelector: ResultSelector + + init(first: FirstO, second: SecondO, resultSelector: ResultSelector) { + _first = first + _second = second + _resultSelector = resultSelector + } + + override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + + let sink = WithLatestFromSink(parent: self, observer: observer, cancel: cancel) + setSink(sink) + return sink.run() + } +} \ No newline at end of file diff --git a/RxSwift/Observables/Observable+Multiple.swift b/RxSwift/Observables/Observable+Multiple.swift index 6cb14adb..0e0af8ce 100644 --- a/RxSwift/Observables/Observable+Multiple.swift +++ b/RxSwift/Observables/Observable+Multiple.swift @@ -224,3 +224,19 @@ extension SequenceType where Generator.Element : ObservableConvertibleType { } } } + +// withLatestFrom + +extension ObservableType { + + /** + Merges two observable sequences into one observable sequence by combining each element from the first source with the latest element from the second source, if any. + + - parameter second: Second observable source. + - parameter resultSelector: Function to invoke for each element from the self source combined with the latest element from the second source, if any. + - returns: An observable sequence containing the result of combining each element of the self source with the latest element from the second source, if any, using the specified result selector function. + */ + public func withLatestFrom(second: SecondO, resultSelector: (E, SecondO.E) throws -> ResultType) -> Observable { + return WithLatestFrom(first: self, second: second, resultSelector: resultSelector) + } +} diff --git a/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift b/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift index 7b424157..b016476e 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift @@ -3948,4 +3948,273 @@ extension ObservableMultipleTest { XCTAssert(disposed, "disposed") } +} + + +// MARK: withLatestFrom + +extension ObservableMultipleTest { + + func testWithLatestFrom_Simple1() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, 1), + next(180, 2), + next(250, 3), + next(260, 4), + next(310, 5), + next(340, 6), + next(410, 7), + next(420, 8), + next(470, 9), + next(550, 10), + completed(590) + ]) + + let ys = scheduler.createHotObservable([ + next(255, "bar"), + next(330, "foo"), + next(350, "qux"), + completed(400) + ]) + + let res = scheduler.start { + xs.withLatestFrom(ys, resultSelector: { x, y in "\(x)\(y)"} ) + } + + XCTAssertEqual(res.messages, [ + next(260, "4bar"), + next(310, "5bar"), + next(340, "6foo"), + next(410, "7qux"), + next(420, "8qux"), + next(470, "9qux"), + next(550, "10qux"), + completed(590) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 590) + ]) + + XCTAssertEqual(ys.subscriptions, [ + Subscription(200, 400) + ]) + } + + func testWithLatestFrom_Simple2() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, 1), + next(180, 2), + next(250, 3), + next(260, 4), + next(310, 5), + next(340, 6), + completed(390) + ]) + + let ys = scheduler.createHotObservable([ + next(255, "bar"), + next(330, "foo"), + next(350, "qux"), + next(370, "baz"), + completed(400) + ]) + + let res = scheduler.start { + xs.withLatestFrom(ys, resultSelector: { x, y in "\(x)\(y)"} ) + } + + XCTAssertEqual(res.messages, [ + next(260, "4bar"), + next(310, "5bar"), + next(340, "6foo"), + completed(390) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 390) + ]) + + XCTAssertEqual(ys.subscriptions, [ + Subscription(200, 390) + ]) + } + + func testWithLatestFrom_Simple3() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, 1), + next(180, 2), + next(250, 3), + next(260, 4), + next(310, 5), + next(340, 6), + completed(390) + ]) + + let ys = scheduler.createHotObservable([ + next(245, "bar"), + next(330, "foo"), + next(350, "qux"), + next(370, "baz"), + completed(400) + ]) + + let res = scheduler.start { + xs.withLatestFrom(ys, resultSelector: { x, y in "\(x)\(y)"} ) + } + + XCTAssertEqual(res.messages, [ + next(250, "3bar"), + next(260, "4bar"), + next(310, "5bar"), + next(340, "6foo"), + completed(390) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 390) + ]) + + XCTAssertEqual(ys.subscriptions, [ + Subscription(200, 390) + ]) + } + + func testWithLatestFrom_Error1() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, 1), + next(180, 2), + next(250, 3), + next(260, 4), + next(310, 5), + next(340, 6), + next(410, 7), + next(420, 8), + next(470, 9), + next(550, 10), + error(590, testError) + ]) + + let ys = scheduler.createHotObservable([ + next(255, "bar"), + next(330, "foo"), + next(350, "qux"), + completed(400) + ]) + + let res = scheduler.start { + xs.withLatestFrom(ys, resultSelector: { x, y in "\(x)\(y)"} ) + } + + XCTAssertEqual(res.messages, [ + next(260, "4bar"), + next(310, "5bar"), + next(340, "6foo"), + next(410, "7qux"), + next(420, "8qux"), + next(470, "9qux"), + next(550, "10qux"), + error(590, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 590) + ]) + + XCTAssertEqual(ys.subscriptions, [ + Subscription(200, 400) + ]) + } + + func testWithLatestFrom_Error2() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, 1), + next(180, 2), + next(250, 3), + next(260, 4), + next(310, 5), + next(340, 6), + completed(390) + ]) + + let ys = scheduler.createHotObservable([ + next(255, "bar"), + next(330, "foo"), + next(350, "qux"), + error(370, testError) + ]) + + let res = scheduler.start { + xs.withLatestFrom(ys, resultSelector: { x, y in "\(x)\(y)"} ) + } + + XCTAssertEqual(res.messages, [ + next(260, "4bar"), + next(310, "5bar"), + next(340, "6foo"), + error(370, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 370) + ]) + + XCTAssertEqual(ys.subscriptions, [ + Subscription(200, 370) + ]) + } + + func testWithLatestFrom_Error3() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, 1), + next(180, 2), + next(250, 3), + next(260, 4), + next(310, 5), + next(340, 6), + completed(390) + ]) + + let ys = scheduler.createHotObservable([ + next(255, "bar"), + next(330, "foo"), + next(350, "qux"), + completed(400) + ]) + + let res = scheduler.start { + xs.withLatestFrom(ys) { + (x, y) throws -> String in + if x == 5 { + throw testError + } + return "\(x)\(y)" + } + } + + XCTAssertEqual(res.messages, [ + next(260, "4bar"), + error(310, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 310) + ]) + + XCTAssertEqual(ys.subscriptions, [ + Subscription(200, 310) + ]) + } } \ No newline at end of file From 7acb77a41d4ca2352a713cae14c78f8054223b4f Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Tue, 20 Oct 2015 12:33:41 +0200 Subject: [PATCH 086/210] Updates tvOS support details. --- README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4213e120..80a8a0c3 100644 --- a/README.md +++ b/README.md @@ -456,7 +456,7 @@ Open Rx.xcworkspace, choose `RxExample` and hit run. This method will build ever ### [CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html) -**:warning: IMPORTANT! For tvOS support through CocoaPods use [this hack](https://github.com/orta/cocoapods-expert-difficulty) until `0.39` is released. :warning:** +**:warning: IMPORTANT! For tvOS support CocoaPods `0.39` is required. :warning:** ``` # Podfile @@ -475,6 +475,8 @@ $ pod install ### [Carthage](https://github.com/Carthage/Carthage) +**For Xcode 7.0** + Add this to `Cartfile` ``` @@ -485,6 +487,18 @@ git "git@github.com:ReactiveX/RxSwift.git" "2.0.0-beta.1" $ carthage update ``` +**For Xcode 7.1 and tvOS support** + +Add this to `Cartfile` + +``` +git "git@github.com:ReactiveX/RxSwift.git" "master-7.1" +``` + +``` +$ carthage update +``` + ### Manually using git submodules * Add RxSwift as a submodule From f616a15ee678d61df2f0912a4a883d6a79544e60 Mon Sep 17 00:00:00 2001 From: yury Date: Tue, 20 Oct 2015 22:11:03 +0300 Subject: [PATCH 087/210] Work on subjects --- RxSwift/Disposables/ScheduledDisposable.swift | 2 +- RxSwift/Subjects/BehaviorSubject.swift | 2 +- RxSwift/Subjects/PublishSubject.swift | 56 ++++++------ RxSwift/Subjects/ReplaySubject.swift | 87 ++++++++++--------- RxSwift/Subjects/Variable.swift | 10 +-- 5 files changed, 79 insertions(+), 78 deletions(-) diff --git a/RxSwift/Disposables/ScheduledDisposable.swift b/RxSwift/Disposables/ScheduledDisposable.swift index 300f4cde..8ad765e3 100644 --- a/RxSwift/Disposables/ScheduledDisposable.swift +++ b/RxSwift/Disposables/ScheduledDisposable.swift @@ -36,7 +36,7 @@ public class ScheduledDisposable : Cancelable { */ init(scheduler: ImmediateSchedulerType, disposable: Disposable) { self.scheduler = scheduler - self._disposable = disposable + _disposable = disposable } /** diff --git a/RxSwift/Subjects/BehaviorSubject.swift b/RxSwift/Subjects/BehaviorSubject.swift index d7266460..74ffb6e6 100644 --- a/RxSwift/Subjects/BehaviorSubject.swift +++ b/RxSwift/Subjects/BehaviorSubject.swift @@ -61,7 +61,7 @@ public final class BehaviorSubject : Observable, SubjectType, - parameter value: Initial value sent to observers when no other value has been received by the subject yet. */ public init(value: Element) { - self._value = value + _value = value } /** diff --git a/RxSwift/Subjects/PublishSubject.swift b/RxSwift/Subjects/PublishSubject.swift index bb252048..52909ed5 100644 --- a/RxSwift/Subjects/PublishSubject.swift +++ b/RxSwift/Subjects/PublishSubject.swift @@ -14,23 +14,23 @@ class Subscription : Disposable { private var _lock = SpinLock() // state - private var subject: PublishSubject? - private var key: KeyType? + private var _subject: PublishSubject? + private var _key: KeyType? init(subject: PublishSubject, key: KeyType) { - self.key = key - self.subject = subject + _key = key + _subject = subject } func dispose() { _lock.performLocked { - guard let subject = subject, - let key = key else { + guard let subject = _subject, + let key = _key else { return } - self.subject = nil - self.key = nil + _subject = nil + _key = nil subject.unsubscribe(key) } @@ -47,19 +47,19 @@ public class PublishSubject : Observable, SubjectType, Cancela typealias DisposeKey = Bag>.KeyType - private let lock = NSRecursiveLock() + private let _lock = NSRecursiveLock() // state - var _disposed = false - var observers = Bag>() - var stoppedEvent = nil as Event? + private var _disposed = false + private var _observers = Bag>() + private var _stoppedEvent = nil as Event? /** Indicates whether the subject has been disposed. */ public var disposed: Bool { get { - return self.lock.calculateLocked { + return _lock.calculateLocked { return _disposed } } @@ -78,19 +78,19 @@ public class PublishSubject : Observable, SubjectType, Cancela - parameter event: Event to send to the observers. */ public func on(event: Event) { - lock.performLocked { + _lock.performLocked { switch event { case .Next(_): - if disposed || stoppedEvent != nil { + if disposed || _stoppedEvent != nil { return } - observers.forEach { $0.on(event) } + _observers.forEach { $0.on(event) } case .Completed, .Error: - if stoppedEvent == nil { - self.stoppedEvent = event - observers.forEach { $0.on(event) } - self.observers.removeAll() + if _stoppedEvent == nil { + _stoppedEvent = event + _observers.forEach { $0.on(event) } + _observers.removeAll() } } } @@ -103,8 +103,8 @@ public class PublishSubject : Observable, SubjectType, Cancela - returns: Disposable object that can be used to unsubscribe the observer from the subject. */ public override func subscribe(observer: O) -> Disposable { - return lock.calculateLocked { - if let stoppedEvent = stoppedEvent { + return _lock.calculateLocked { + if let stoppedEvent = _stoppedEvent { observer.on(stoppedEvent) return NopDisposable.instance } @@ -114,14 +114,14 @@ public class PublishSubject : Observable, SubjectType, Cancela return NopDisposable.instance } - let key = observers.insert(observer.asObserver()) + let key = _observers.insert(observer.asObserver()) return Subscription(subject: self, key: key) } } func unsubscribe(key: DisposeKey) { - self.lock.performLocked { - _ = observers.removeKey(key) + _lock.performLocked { + _ = _observers.removeKey(key) } } @@ -136,10 +136,10 @@ public class PublishSubject : Observable, SubjectType, Cancela Unsubscribe all observers and release resources. */ public func dispose() { - self.lock.performLocked { + _lock.performLocked { _disposed = true - self.observers.removeAll() - self.stoppedEvent = nil + _observers.removeAll() + _stoppedEvent = nil } } } \ No newline at end of file diff --git a/RxSwift/Subjects/ReplaySubject.swift b/RxSwift/Subjects/ReplaySubject.swift index e582ac83..d868367b 100644 --- a/RxSwift/Subjects/ReplaySubject.swift +++ b/RxSwift/Subjects/ReplaySubject.swift @@ -61,12 +61,13 @@ public class ReplaySubject : Observable, SubjectType, Observer } class ReplayBufferBase : ReplaySubject { - let lock = NSRecursiveLock() + + private let _lock = NSRecursiveLock() // state - var disposed = false - var stoppedEvent = nil as Event? - var observers = Bag>() + private var _disposed = false + private var _stoppedEvent = nil as Event? + private var _observers = Bag>() override init() { @@ -85,12 +86,12 @@ class ReplayBufferBase : ReplaySubject { } override func on(event: Event) { - lock.performLocked { - if self.disposed { + _lock.performLocked { + if _disposed { return } - if self.stoppedEvent != nil { + if _stoppedEvent != nil { return } @@ -98,20 +99,20 @@ class ReplayBufferBase : ReplaySubject { case .Next(let value): addValueToBuffer(value) trim() - self.observers.forEach { $0.on(event) } + _observers.forEach { $0.on(event) } case .Error, .Completed: - stoppedEvent = event + _stoppedEvent = event trim() - self.observers.forEach { $0.on(event) } - observers.removeAll() + _observers.forEach { $0.on(event) } + _observers.removeAll() } } } override func subscribe(observer: O) -> Disposable { - return lock.calculateLocked { - if self.disposed { + return _lock.calculateLocked { + if _disposed { observer.on(.Error(RxError.DisposedError)) return NopDisposable.instance } @@ -119,44 +120,44 @@ class ReplayBufferBase : ReplaySubject { let AnyObserver = observer.asObserver() replayBuffer(AnyObserver) - if let stoppedEvent = self.stoppedEvent { + if let stoppedEvent = _stoppedEvent { observer.on(stoppedEvent) return NopDisposable.instance } else { - let key = self.observers.insert(AnyObserver) + let key = _observers.insert(AnyObserver) return ReplaySubscription(subject: self, disposeKey: key) } } } override func unsubscribe(key: DisposeKey) { - lock.performLocked { - if self.disposed { + _lock.performLocked { + if _disposed { return } - _ = self.observers.removeKey(key) + _ = _observers.removeKey(key) } } func lockedDispose() { - disposed = true - stoppedEvent = nil - observers.removeAll() + _disposed = true + _stoppedEvent = nil + _observers.removeAll() } override func dispose() { super.dispose() - lock.performLocked { - self.lockedDispose() + _lock.performLocked { + lockedDispose() } } } class ReplayOne : ReplayBufferBase { - var value: Element? + private var _value: Element? override init() { super.init() @@ -167,11 +168,11 @@ class ReplayOne : ReplayBufferBase { } override func addValueToBuffer(value: Element) { - self.value = value + _value = value } override func replayBuffer(observer: AnyObserver) { - if let value = self.value { + if let value = _value { observer.on(.Next(value)) } } @@ -179,45 +180,45 @@ class ReplayOne : ReplayBufferBase { override func lockedDispose() { super.lockedDispose() - value = nil + _value = nil } } class ReplayManyBase : ReplayBufferBase { - var queue: Queue + private var _queue: Queue init(queueSize: Int) { - queue = Queue(capacity: queueSize + 1) + _queue = Queue(capacity: queueSize + 1) } override func addValueToBuffer(value: Element) { - queue.enqueue(value) + _queue.enqueue(value) } override func replayBuffer(observer: AnyObserver) { - for item in queue { + for item in _queue { observer.on(.Next(item)) } } override func lockedDispose() { super.lockedDispose() - self.queue = Queue(capacity: 0) + _queue = Queue(capacity: 0) } } class ReplayMany : ReplayManyBase { - let bufferSize: Int + private let _bufferSize: Int init(bufferSize: Int) { - self.bufferSize = bufferSize + _bufferSize = bufferSize super.init(queueSize: bufferSize) } override func trim() { - while queue.count > bufferSize { - queue.dequeue() + while _queue.count > _bufferSize { + _queue.dequeue() } } } @@ -239,19 +240,19 @@ class ReplaySubscription : Disposable { private var _lock = SpinLock() // state - var subject: Subject? - var disposeKey: DisposeKey? + private var _subject: Subject? + private var _disposeKey: DisposeKey? init(subject: Subject, disposeKey: DisposeKey) { - self.subject = subject - self.disposeKey = disposeKey + _subject = subject + _disposeKey = disposeKey } func dispose() { let oldState = _lock.calculateLocked { () -> (Subject?, DisposeKey?) in - let state = (self.subject, self.disposeKey) - self.subject = nil - self.disposeKey = nil + let state = (self._subject, self._disposeKey) + self._subject = nil + self._disposeKey = nil return state } diff --git a/RxSwift/Subjects/Variable.swift b/RxSwift/Subjects/Variable.swift index 2265cf36..aaeb6bd9 100644 --- a/RxSwift/Subjects/Variable.swift +++ b/RxSwift/Subjects/Variable.swift @@ -16,7 +16,7 @@ Unlike `BehaviorSubject` it can't terminate with error. public class Variable : ObservableType { public typealias E = Element - let subject: BehaviorSubject + private let _subject: BehaviorSubject private var _lock = SpinLock() @@ -40,7 +40,7 @@ public class Variable : ObservableType { _lock.performLocked { _value = newValue } - self.subject.on(.Next(newValue)) + _subject.on(.Next(newValue)) } } @@ -51,7 +51,7 @@ public class Variable : ObservableType { */ public init(_ value: Element) { _value = value - self.subject = BehaviorSubject(value: value) + _subject = BehaviorSubject(value: value) } /** @@ -63,13 +63,13 @@ public class Variable : ObservableType { - returns: Disposable object that can be used to unsubscribe the observer from the variable. */ public func subscribe(observer: O) -> Disposable { - return self.subject.subscribe(observer) + return _subject.subscribe(observer) } /** - returns: Canonical interface for push style sequence */ public func asObservable() -> Observable { - return self.subject + return _subject } } \ No newline at end of file From ce3e9bee90093f66c939ac93eb99c1fcfaba86b8 Mon Sep 17 00:00:00 2001 From: yury Date: Tue, 20 Oct 2015 22:29:12 +0300 Subject: [PATCH 088/210] Add private vars prefixes to ConnectableObservable --- .../ConnectableObservable.swift | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/RxSwift/Observables/Implementations/ConnectableObservable.swift b/RxSwift/Observables/Implementations/ConnectableObservable.swift index 83db99f0..a9715a6c 100644 --- a/RxSwift/Observables/Implementations/ConnectableObservable.swift +++ b/RxSwift/Observables/Implementations/ConnectableObservable.swift @@ -11,27 +11,27 @@ import Foundation class Connection : Disposable { // state - weak var parent: ConnectableObservable? - var subscription : Disposable? + private weak var _parent: ConnectableObservable? + private var _subscription : Disposable? init(parent: ConnectableObservable, subscription: Disposable) { - self.parent = parent - self.subscription = subscription + _parent = parent + _subscription = subscription } func dispose() { - guard let parent = self.parent else { return } + guard let parent = _parent else { return } - parent.lock.performLocked { - guard let oldSubscription = self.subscription else { + parent._lock.performLocked { + guard let oldSubscription = _subscription else { return } - self.subscription = nil - if self.parent?.connection === self { - parent.connection = nil + _subscription = nil + if _parent?._connection === self { + parent._connection = nil } - self.parent = nil + _parent = nil oldSubscription.dispose() } @@ -41,34 +41,34 @@ class Connection : Disposable { public class ConnectableObservable : Observable, ConnectableObservableType { typealias ConnectionType = Connection - let subject: S - let source: Observable + private let _subject: S + private let _source: Observable - let lock = NSRecursiveLock() + private let _lock = NSRecursiveLock() // state - var connection: ConnectionType? + private var _connection: ConnectionType? public init(source: Observable, subject: S) { - self.source = AsObservable(source: source) - self.subject = subject - self.connection = nil + _source = AsObservable(source: source) + _subject = subject + _connection = nil } public func connect() -> Disposable { - return self.lock.calculateLocked { - if let connection = self.connection { + return _lock.calculateLocked { + if let connection = _connection { return connection } - let disposable = self.source.subscribeSafe(self.subject.asObserver()) + let disposable = _source.subscribeSafe(_subject.asObserver()) let connection = Connection(parent: self, subscription: disposable) - self.connection = connection + _connection = connection return connection } } public override func subscribe(observer: O) -> Disposable { - return subject.subscribeSafe(observer) + return _subject.subscribeSafe(observer) } } \ No newline at end of file From f4f94faf4764d6e58344d3c9b99c675be6c2ca69 Mon Sep 17 00:00:00 2001 From: yury Date: Wed, 21 Oct 2015 10:46:39 +0300 Subject: [PATCH 089/210] Better formatting --- .../RxSwiftTests/Tests/Observable+MultipleTest.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift b/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift index b016476e..341ebf7f 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift @@ -3980,7 +3980,7 @@ extension ObservableMultipleTest { ]) let res = scheduler.start { - xs.withLatestFrom(ys, resultSelector: { x, y in "\(x)\(y)"} ) + xs.withLatestFrom(ys) { x, y in "\(x)\(y)" } } XCTAssertEqual(res.messages, [ @@ -4025,7 +4025,7 @@ extension ObservableMultipleTest { ]) let res = scheduler.start { - xs.withLatestFrom(ys, resultSelector: { x, y in "\(x)\(y)"} ) + xs.withLatestFrom(ys) { x, y in "\(x)\(y)" } } XCTAssertEqual(res.messages, [ @@ -4066,7 +4066,7 @@ extension ObservableMultipleTest { ]) let res = scheduler.start { - xs.withLatestFrom(ys, resultSelector: { x, y in "\(x)\(y)"} ) + xs.withLatestFrom(ys) { x, y in "\(x)\(y)" } } XCTAssertEqual(res.messages, [ @@ -4111,7 +4111,7 @@ extension ObservableMultipleTest { ]) let res = scheduler.start { - xs.withLatestFrom(ys, resultSelector: { x, y in "\(x)\(y)"} ) + xs.withLatestFrom(ys) { x, y in "\(x)\(y)" } } XCTAssertEqual(res.messages, [ @@ -4155,7 +4155,7 @@ extension ObservableMultipleTest { ]) let res = scheduler.start { - xs.withLatestFrom(ys, resultSelector: { x, y in "\(x)\(y)"} ) + xs.withLatestFrom(ys) { x, y in "\(x)\(y)" } } XCTAssertEqual(res.messages, [ From b95ebfb3640350568757591e84ba8abe1aa9739d Mon Sep 17 00:00:00 2001 From: yury Date: Wed, 21 Oct 2015 10:57:44 +0300 Subject: [PATCH 090/210] Lock whole on method --- .../Implementations/WithLatestFrom.swift | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/RxSwift/Observables/Implementations/WithLatestFrom.swift b/RxSwift/Observables/Implementations/WithLatestFrom.swift index f205e1e7..f0c68abd 100644 --- a/RxSwift/Observables/Implementations/WithLatestFrom.swift +++ b/RxSwift/Observables/Implementations/WithLatestFrom.swift @@ -48,28 +48,22 @@ class WithLatestFromFirst) { - switch event { - case let .Next(value): - guard let latest = _parent._latest else { return } - do { - let res = try _parent._parent._resultSelector(value, latest) - - _parent._lock.performLocked { + _parent._lock.performLocked { + switch event { + case let .Next(value): + guard let latest = _parent._latest else { return } + do { + let res = try _parent._parent._resultSelector(value, latest) + _parent.observer?.onNext(res) - } - } catch let e { - _parent._lock.performLocked { + } catch let e { _parent.observer?.onError(e) _parent.dispose() } - } - case .Completed: - _parent._lock.performLocked { + case .Completed: _parent.observer?.onComplete() _parent.dispose() - } - case let .Error(error): - _parent._lock.performLocked { + case let .Error(error): _parent.observer?.onError(error) _parent.dispose() } From 3d72cb2a7c28431add6ef1290628d799f3f25427 Mon Sep 17 00:00:00 2001 From: yury Date: Wed, 21 Oct 2015 11:12:16 +0300 Subject: [PATCH 091/210] Merge Sink classes --- .../Implementations/WithLatestFrom.swift | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/RxSwift/Observables/Implementations/WithLatestFrom.swift b/RxSwift/Observables/Implementations/WithLatestFrom.swift index f0c68abd..4b82755d 100644 --- a/RxSwift/Observables/Implementations/WithLatestFrom.swift +++ b/RxSwift/Observables/Implementations/WithLatestFrom.swift @@ -8,10 +8,11 @@ import Foundation -class WithLatestFromSink : Sink { +class WithLatestFromSink : Sink, ObserverType { typealias Parent = WithLatestFrom typealias SecondType = SecondO.E + typealias E = FirstO.E private let _parent: Parent @@ -27,45 +28,33 @@ class WithLatestFromSink Disposable { let sndSubscription = SingleAssignmentDisposable() - let fstO = WithLatestFromFirst(parent: self) let sndO = WithLatestFromSecond(parent: self, disposable: sndSubscription) - let fstSubscription = _parent._first.subscribeSafe(fstO) + let fstSubscription = _parent._first.subscribeSafe(self) sndSubscription.disposable = _parent._second.subscribeSafe(sndO) return StableCompositeDisposable.create(fstSubscription, sndSubscription) } -} - -class WithLatestFromFirst: ObserverType { - typealias Parent = WithLatestFromSink - typealias E = FirstO.E - private let _parent: Parent - - init(parent: Parent) { - _parent = parent - } - func on(event: Event) { - _parent._lock.performLocked { + _lock.performLocked { switch event { case let .Next(value): - guard let latest = _parent._latest else { return } + guard let latest = _latest else { return } do { - let res = try _parent._parent._resultSelector(value, latest) + let res = try _parent._resultSelector(value, latest) - _parent.observer?.onNext(res) + observer?.onNext(res) } catch let e { - _parent.observer?.onError(e) - _parent.dispose() + observer?.onError(e) + dispose() } case .Completed: - _parent.observer?.onComplete() - _parent.dispose() + observer?.onComplete() + dispose() case let .Error(error): - _parent.observer?.onError(error) - _parent.dispose() + observer?.onError(error) + dispose() } } } @@ -87,7 +76,9 @@ class WithLatestFromSecond) { switch event { case let .Next(value): - _parent._latest = value + _parent._lock.performLocked { + _parent._latest = value + } case .Completed: _disposable.dispose() case let .Error(error): From 22b1204c28baf3a444d05a52fb7d816333dd5cef Mon Sep 17 00:00:00 2001 From: yury Date: Wed, 21 Oct 2015 11:22:19 +0300 Subject: [PATCH 092/210] Tune docs --- RxSwift/Observables/Observable+Multiple.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RxSwift/Observables/Observable+Multiple.swift b/RxSwift/Observables/Observable+Multiple.swift index 0e0af8ce..81e63e26 100644 --- a/RxSwift/Observables/Observable+Multiple.swift +++ b/RxSwift/Observables/Observable+Multiple.swift @@ -230,11 +230,11 @@ extension SequenceType where Generator.Element : ObservableConvertibleType { extension ObservableType { /** - Merges two observable sequences into one observable sequence by combining each element from the first source with the latest element from the second source, if any. + Merges two observable sequences into one observable sequence by combining each element from self with the latest element from the second source, if any. - parameter second: Second observable source. - - parameter resultSelector: Function to invoke for each element from the self source combined with the latest element from the second source, if any. - - returns: An observable sequence containing the result of combining each element of the self source with the latest element from the second source, if any, using the specified result selector function. + - parameter resultSelector: Function to invoke for each element from the self combined with the latest element from the second source, if any. + - returns: An observable sequence containing the result of combining each element of the self with the latest element from the second source, if any, using the specified result selector function. */ public func withLatestFrom(second: SecondO, resultSelector: (E, SecondO.E) throws -> ResultType) -> Observable { return WithLatestFrom(first: self, second: second, resultSelector: resultSelector) From 3693c7d9ff82608406975a161c47528256dfec3d Mon Sep 17 00:00:00 2001 From: Junior B Date: Wed, 21 Oct 2015 10:00:22 +0200 Subject: [PATCH 093/210] Adds `toArray` operator --- Rx.xcodeproj/project.pbxproj | 10 ++ RxExample/RxExample.xcodeproj/project.pbxproj | 6 +- .../Observables/Implementations/ToArray.swift | 50 ++++++ .../Observables/Observable+Aggregate.swift | 13 ++ .../Tests/Observable+AggregateTest.swift | 154 ++++++++++++++++++ 5 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 RxSwift/Observables/Implementations/ToArray.swift diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index 58f8bd96..463d89ad 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -531,6 +531,10 @@ C8F0C0441BBBFBB9001B112F /* _RXSwizzling.h in Headers */ = {isa = PBXBuildFile; fileRef = C8093E881B8A732E0088E94D /* _RXSwizzling.h */; settings = {ATTRIBUTES = (Public, ); }; }; C8F0C0451BBBFBB9001B112F /* _RXKVOObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = C8093E861B8A732E0088E94D /* _RXKVOObserver.h */; settings = {ATTRIBUTES = (Public, ); }; }; C8F0C04F1BBBFBCE001B112F /* ObservableConvertibleType+Blocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093F581B8A73A20088E94D /* ObservableConvertibleType+Blocking.swift */; }; + CBEE771F1BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; settings = {ASSET_TAGS = (); }; }; + CBEE77201BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; settings = {ASSET_TAGS = (); }; }; + CBEE77211BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; settings = {ASSET_TAGS = (); }; }; + CBEE77221BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; settings = {ASSET_TAGS = (); }; }; D203C4F31BB9C4CA00D02D00 /* RxCollectionViewReactiveArrayDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88253F11B8A752B00B02D69 /* RxCollectionViewReactiveArrayDataSource.swift */; }; D203C4F41BB9C52400D02D00 /* RxTableViewReactiveArrayDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88253F21B8A752B00B02D69 /* RxTableViewReactiveArrayDataSource.swift */; }; D203C4F51BB9C52900D02D00 /* ItemEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88253F41B8A752B00B02D69 /* ItemEvents.swift */; }; @@ -974,6 +978,7 @@ C8F0C0021BBBFB8B001B112F /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8F0C04B1BBBFBB9001B112F /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8F0C0581BBBFBCE001B112F /* RxBlocking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxBlocking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CBEE771E1BD649A000AD584C /* ToArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToArray.swift; sourceTree = ""; }; D2138C751BB9BE9800339B5C /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D22B6D251BC8504A00BCE0AB /* SkipWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipWhile.swift; sourceTree = ""; }; D235B23D1BD003DD007E84DA /* Using.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Using.swift; sourceTree = ""; }; @@ -1208,6 +1213,7 @@ C8093C8F1B8A72BE0088E94D /* TakeWhile.swift */, C8093C901B8A72BE0088E94D /* Throttle.swift */, C8093C911B8A72BE0088E94D /* Timer.swift */, + CBEE771E1BD649A000AD584C /* ToArray.swift */, C8093C941B8A72BE0088E94D /* Zip.swift */, C8093C921B8A72BE0088E94D /* Zip+arity.swift */, C8093C931B8A72BE0088E94D /* Zip+arity.tt */, @@ -2192,6 +2198,7 @@ C8093CDC1B8A72BE0088E94D /* CompositeDisposable.swift in Sources */, C8093D7E1B8A72BE0088E94D /* ObserverType.swift in Sources */, C8093D401B8A72BE0088E94D /* SubscribeOn.swift in Sources */, + CBEE77201BD649A000AD584C /* ToArray.swift in Sources */, C8093CFE1B8A72BE0088E94D /* Observable.swift in Sources */, C8093CE21B8A72BE0088E94D /* NAryDisposable.swift in Sources */, C8093CEC1B8A72BE0088E94D /* SerialDisposable.swift in Sources */, @@ -2311,6 +2318,7 @@ C8093CDB1B8A72BE0088E94D /* CompositeDisposable.swift in Sources */, C8093D7D1B8A72BE0088E94D /* ObserverType.swift in Sources */, C8093D3F1B8A72BE0088E94D /* SubscribeOn.swift in Sources */, + CBEE771F1BD649A000AD584C /* ToArray.swift in Sources */, C8093CFD1B8A72BE0088E94D /* Observable.swift in Sources */, C8093CE11B8A72BE0088E94D /* NAryDisposable.swift in Sources */, C8093CEB1B8A72BE0088E94D /* SerialDisposable.swift in Sources */, @@ -2430,6 +2438,7 @@ C8F0BFF11BBBFB8B001B112F /* CompositeDisposable.swift in Sources */, C8F0BFF21BBBFB8B001B112F /* ObserverType.swift in Sources */, C8F0BFF31BBBFB8B001B112F /* SubscribeOn.swift in Sources */, + CBEE77221BD649A000AD584C /* ToArray.swift in Sources */, C8F0BFF41BBBFB8B001B112F /* Observable.swift in Sources */, C8F0BFF51BBBFB8B001B112F /* NAryDisposable.swift in Sources */, C8F0BFF61BBBFB8B001B112F /* SerialDisposable.swift in Sources */, @@ -2699,6 +2708,7 @@ D2EBEB431BB9B6DE003A27DC /* SubjectType.swift in Sources */, D2EBEB201BB9B6C1003A27DC /* StartWith.swift in Sources */, D2EBEAF41BB9B6AE003A27DC /* DisposeBase.swift in Sources */, + CBEE77211BD649A000AD584C /* ToArray.swift in Sources */, D2EBEB3F1BB9B6D8003A27DC /* CurrentThreadScheduler.swift in Sources */, D2EBEAF21BB9B6AE003A27DC /* CompositeDisposable.swift in Sources */, D2EBEB0E1BB9B6C1003A27DC /* FlatMap.swift in Sources */, diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index ebc65c94..d53d3e75 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -312,6 +312,7 @@ C8DF92EB1B0B38C0009BCF9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; }; C8DF92F61B0B43A4009BCF9A /* IntroductionExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DF92F51B0B43A4009BCF9A /* IntroductionExampleViewController.swift */; }; C8E9D2AF1BD3FD960079D0DB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; settings = {ASSET_TAGS = (); }; }; + CBEE77541BD8C7B700AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE77531BD8C7B700AD584C /* ToArray.swift */; settings = {ASSET_TAGS = (); }; }; D2AF91981BD3D95900A008C1 /* Using.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AF91881BD2C51900A008C1 /* Using.swift */; }; EC91FB951BBA144400973245 /* GitHubSearchRepositoriesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC91FB941BBA144400973245 /* GitHubSearchRepositoriesViewController.swift */; }; /* End PBXBuildFile section */ @@ -688,6 +689,7 @@ C8DF92F01B0B3E67009BCF9A /* Info-OSX.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-OSX.plist"; sourceTree = ""; }; C8DF92F21B0B3E71009BCF9A /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = ""; }; C8DF92F51B0B43A4009BCF9A /* IntroductionExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = IntroductionExampleViewController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + CBEE77531BD8C7B700AD584C /* ToArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToArray.swift; sourceTree = ""; }; D2AF91881BD2C51900A008C1 /* Using.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Using.swift; sourceTree = ""; }; EC91FB941BBA144400973245 /* GitHubSearchRepositoriesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubSearchRepositoriesViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -1082,7 +1084,6 @@ C89464481BC6C2B00055219D /* Implementations */ = { isa = PBXGroup; children = ( - D2AF91881BD2C51900A008C1 /* Using.swift */, C89464491BC6C2B00055219D /* Amb.swift */, C894644A1BC6C2B00055219D /* AnonymousObservable.swift */, C894644B1BC6C2B00055219D /* AsObservable.swift */, @@ -1131,6 +1132,8 @@ C89464731BC6C2B00055219D /* TakeWhile.swift */, C89464741BC6C2B00055219D /* Throttle.swift */, C89464751BC6C2B00055219D /* Timer.swift */, + CBEE77531BD8C7B700AD584C /* ToArray.swift */, + D2AF91881BD2C51900A008C1 /* Using.swift */, C89464791BC6C2B00055219D /* Zip.swift */, C89464761BC6C2B00055219D /* Zip+arity.swift */, C89464771BC6C2B00055219D /* Zip+arity.tt */, @@ -1713,6 +1716,7 @@ C8297E3F1B6CF905000589EA /* SectionModelType.swift in Sources */, C8297E401B6CF905000589EA /* ImageService.swift in Sources */, C89464AD1BC6C2B00055219D /* NopDisposable.swift in Sources */, + CBEE77541BD8C7B700AD584C /* ToArray.swift in Sources */, C89465771BC6C2BC0055219D /* NSNotificationCenter+Rx.swift in Sources */, C89465091BC6C2B00055219D /* ReplaySubject.swift in Sources */, C8297E411B6CF905000589EA /* RxCollectionViewSectionedReloadDataSource.swift in Sources */, diff --git a/RxSwift/Observables/Implementations/ToArray.swift b/RxSwift/Observables/Implementations/ToArray.swift new file mode 100644 index 00000000..00a450e4 --- /dev/null +++ b/RxSwift/Observables/Implementations/ToArray.swift @@ -0,0 +1,50 @@ +// +// ToArray.swift +// Rx +// +// Created by Junior B. on 20/10/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +class ToArraySink : Sink, ObserverType { + typealias Parent = ToArray + + let parent: Parent + var list = Array() + + init(parent: Parent, observer: O, cancel: Disposable) { + self.parent = parent + + super.init(observer: observer, cancel: cancel) + } + + func on(event: Event) { + switch event { + case .Next(let value): + self.list.append(value) + case .Error(let e): + observer?.on(.Error(e)) + self.dispose() + case .Completed: + observer?.on(.Next(self.list)) + observer?.on(.Completed) + self.dispose() + } + } +} + +class ToArray : Producer<[SourceType]> { + let source: Observable + + init(source: Observable) { + self.source = source + } + + override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + let sink = ToArraySink(parent: self, observer: observer, cancel: cancel) + setSink(sink) + return source.subscribeSafe(sink) + } +} \ No newline at end of file diff --git a/RxSwift/Observables/Observable+Aggregate.swift b/RxSwift/Observables/Observable+Aggregate.swift index 93643f81..b6253ceb 100644 --- a/RxSwift/Observables/Observable+Aggregate.swift +++ b/RxSwift/Observables/Observable+Aggregate.swift @@ -42,4 +42,17 @@ extension ObservableType { -> Observable { return Reduce(source: self.asObservable(), seed: seed, accumulator: accumulator, mapResult: { $0 }) } + + /** + Converts an Observable into another Observable that emits the whole sequence as a single array and then terminates. + + For aggregation behavior see `reduce`. + + - returns: An observable sequence containing all the emitted elements as array. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func toArray() + -> Observable<[E]> { + return ToArray(source: self.asObservable()) + } } diff --git a/RxTests/RxSwiftTests/Tests/Observable+AggregateTest.swift b/RxTests/RxSwiftTests/Tests/Observable+AggregateTest.swift index 069eef91..2dc356fb 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+AggregateTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+AggregateTest.swift @@ -342,6 +342,160 @@ extension ObservableAggregateTest { Subscription(200, 260) ] + XCTAssertEqual(res.messages, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } +} + + +// MARK: toArray +extension ObservableAggregateTest { + + func test_ToArrayWithSingleItem_Return() { + let scheduler = TestScheduler(initialClock: 0) + + let xs: ColdObservable = scheduler.createColdObservable([ + next(10, 1), + completed(20) + ]) + + let res = scheduler.start { + return xs.toArray().map { EquatableArray($0) } + } + + let correctMessages = [ + next(220, EquatableArray([1])), + completed(220) + ] + + let correctSubscriptions = [ + Subscription(200, 220) + ] + + XCTAssertEqual(res.messages, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func test_ToArrayWithMultipleItems_Return() { + let scheduler = TestScheduler(initialClock: 0) + + let xs: ColdObservable = scheduler.createColdObservable([ + next(10, 1), + next(20, 2), + next(30, 3), + next(40, 4), + completed(50) + ]) + + let res = scheduler.start { + return xs.toArray().map { EquatableArray($0) } + } + + let correctMessages = [ + next(250, EquatableArray([1,2,3,4])), + completed(250) + ] + + let correctSubscriptions = [ + Subscription(200, 250) + ] + + XCTAssertEqual(res.messages, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func test_ToArrayWithNoItems_Empty() { + let scheduler = TestScheduler(initialClock: 0) + + let xs: ColdObservable = scheduler.createColdObservable([ + completed(50) + ]) + + let res = scheduler.start { + return xs.toArray().map { EquatableArray($0) } + } + + let correctMessages: [Recorded>] = [ + next(250, EquatableArray([])), + completed(250) + ] + + let correctSubscriptions = [ + Subscription(200, 250) + ] + + XCTAssertEqual(res.messages, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func test_ToArrayWithSingleItem_Never() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1) + ]) + + let res = scheduler.start { + return xs.toArray().map { EquatableArray($0) } + } + + let correctMessages: [Recorded>] = [ + ] + + let correctSubscriptions = [ + Subscription(200, 1000) + ] + + XCTAssertEqual(res.messages, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func test_ToArrayWithImmediateError_Throw() { + let scheduler = TestScheduler(initialClock: 0) + + let xs: ColdObservable = scheduler.createColdObservable([ + error(10, testError) + ]) + + let res = scheduler.start { + return xs.toArray().map { EquatableArray($0) } + } + + let correctMessages: [Recorded>] = [ + error(210, testError) + ] + + let correctSubscriptions = [ + Subscription(200, 210) + ] + + XCTAssertEqual(res.messages, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func test_ToArrayWithMultipleItems_Throw() { + let scheduler = TestScheduler(initialClock: 0) + + let xs: ColdObservable = scheduler.createColdObservable([ + next(10, 1), + next(20, 2), + next(30, 3), + next(40, 4), + error(50, testError) + ]) + + let res = scheduler.start { + return xs.toArray().map { EquatableArray($0) } + } + + let correctMessages: [Recorded>] = [ + error(250, testError) + ] + + let correctSubscriptions = [ + Subscription(200, 250) + ] + XCTAssertEqual(res.messages, correctMessages) XCTAssertEqual(xs.subscriptions, correctSubscriptions) } From 97e807080d0da863e960bd3479c20da47f95893f Mon Sep 17 00:00:00 2001 From: Junior B Date: Wed, 21 Oct 2015 15:27:20 +0200 Subject: [PATCH 094/210] Adds `elementAt` operator --- Rx.xcodeproj/project.pbxproj | 12 +- RxExample/RxExample.xcodeproj/project.pbxproj | 6 +- RxSwift/Error.swift | 9 + .../Implementations/ElementAt.swift | 68 +++++ ...Observable+StandardSequenceOperators.swift | 17 ++ ...rvable+StandardSequenceOperatorsTest.swift | 260 +++++++++++++++++- 6 files changed, 360 insertions(+), 12 deletions(-) create mode 100644 RxSwift/Observables/Implementations/ElementAt.swift diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index 2ece3721..6ff46531 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -292,6 +292,7 @@ C84B38EA1BA43380001B7D88 /* ScheduledItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B38E71BA43380001B7D88 /* ScheduledItem.swift */; }; C84B38EE1BA433CD001B7D88 /* Generate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B38ED1BA433CD001B7D88 /* Generate.swift */; }; C84B38EF1BA433CD001B7D88 /* Generate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B38ED1BA433CD001B7D88 /* Generate.swift */; }; + C84CC52C1BDC342100E06A64 /* ElementAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC52B1BDC342100E06A64 /* ElementAt.swift */; }; C86409FC1BA593F500D3C4E8 /* Range.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409FB1BA593F500D3C4E8 /* Range.swift */; }; C86409FD1BA593F500D3C4E8 /* Range.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409FB1BA593F500D3C4E8 /* Range.swift */; }; C8640A031BA5B12A00D3C4E8 /* Repeat.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8640A021BA5B12A00D3C4E8 /* Repeat.swift */; }; @@ -531,10 +532,10 @@ C8F0C0441BBBFBB9001B112F /* _RXSwizzling.h in Headers */ = {isa = PBXBuildFile; fileRef = C8093E881B8A732E0088E94D /* _RXSwizzling.h */; settings = {ATTRIBUTES = (Public, ); }; }; C8F0C0451BBBFBB9001B112F /* _RXKVOObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = C8093E861B8A732E0088E94D /* _RXKVOObserver.h */; settings = {ATTRIBUTES = (Public, ); }; }; C8F0C04F1BBBFBCE001B112F /* ObservableConvertibleType+Blocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093F581B8A73A20088E94D /* ObservableConvertibleType+Blocking.swift */; }; - CBEE771F1BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; settings = {ASSET_TAGS = (); }; }; - CBEE77201BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; settings = {ASSET_TAGS = (); }; }; - CBEE77211BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; settings = {ASSET_TAGS = (); }; }; - CBEE77221BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; settings = {ASSET_TAGS = (); }; }; + CBEE771F1BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; }; + CBEE77201BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; }; + CBEE77211BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; }; + CBEE77221BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; }; D203C4F31BB9C4CA00D02D00 /* RxCollectionViewReactiveArrayDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88253F11B8A752B00B02D69 /* RxCollectionViewReactiveArrayDataSource.swift */; }; D203C4F41BB9C52400D02D00 /* RxTableViewReactiveArrayDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88253F21B8A752B00B02D69 /* RxTableViewReactiveArrayDataSource.swift */; }; D203C4F51BB9C52900D02D00 /* ItemEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88253F41B8A752B00B02D69 /* ItemEvents.swift */; }; @@ -931,6 +932,7 @@ C849BE2A1BAB5D070019AD27 /* ObservableConvertibleType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableConvertibleType.swift; sourceTree = ""; }; C84B38E71BA43380001B7D88 /* ScheduledItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduledItem.swift; sourceTree = ""; }; C84B38ED1BA433CD001B7D88 /* Generate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Generate.swift; sourceTree = ""; }; + C84CC52B1BDC342100E06A64 /* ElementAt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElementAt.swift; sourceTree = ""; }; C86409FB1BA593F500D3C4E8 /* Range.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Range.swift; sourceTree = ""; }; C8640A021BA5B12A00D3C4E8 /* Repeat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Repeat.swift; sourceTree = ""; }; C88253F11B8A752B00B02D69 /* RxCollectionViewReactiveArrayDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewReactiveArrayDataSource.swift; sourceTree = ""; }; @@ -1171,6 +1173,7 @@ C8093C6A1B8A72BE0088E94D /* Implementations */ = { isa = PBXGroup; children = ( + C84CC52B1BDC342100E06A64 /* ElementAt.swift */, D2245A1A1BD5657300E7146F /* WithLatestFrom.swift */, C8093C6B1B8A72BE0088E94D /* Amb.swift */, C8C3DA111B93A3EA004D233E /* AnonymousObservable.swift */, @@ -2271,6 +2274,7 @@ C8C3DA0F1B939767004D233E /* CurrentThreadScheduler.swift in Sources */, C8093D851B8A72BE0088E94D /* Rx.swift in Sources */, C80D342E1B9245A40014629D /* CombineLatest+CollectionType.swift in Sources */, + C84CC52C1BDC342100E06A64 /* ElementAt.swift in Sources */, C8093DA51B8A72BE0088E94D /* SubjectType.swift in Sources */, C8093D5B1B8A72BE0088E94D /* Observable+Debug.swift in Sources */, C8093D571B8A72BE0088E94D /* Observable+Concurrency.swift in Sources */, diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index 382fa16d..c3436fee 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -86,6 +86,7 @@ C84B913B1B8A282000C9CCCF /* RxCollectionViewSectionedReloadDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C859B9A51B45C80700D012D7 /* RxCollectionViewSectionedReloadDataSource.swift */; }; C84B913C1B8A282000C9CCCF /* RxCollectionViewSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C859B9A71B45C83700D012D7 /* RxCollectionViewSectionedDataSource.swift */; }; C84B913D1B8A282000C9CCCF /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C859B9A91B45CB0900D012D7 /* RxCollectionViewSectionedAnimatedDataSource.swift */; }; + C84CC52E1BDC344100E06A64 /* ElementAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC52D1BDC344100E06A64 /* ElementAt.swift */; }; C859B9A41B45C5D900D012D7 /* PartialUpdatesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C859B9A31B45C5D900D012D7 /* PartialUpdatesViewController.swift */; }; C859B9AC1B45CF9100D012D7 /* NumberCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C859B9AB1B45CF9100D012D7 /* NumberCell.swift */; }; C859B9AE1B45CFAB00D012D7 /* NumberSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C859B9AD1B45CFAB00D012D7 /* NumberSectionView.swift */; }; @@ -312,8 +313,8 @@ C8DF92EB1B0B38C0009BCF9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; }; C8DF92F61B0B43A4009BCF9A /* IntroductionExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DF92F51B0B43A4009BCF9A /* IntroductionExampleViewController.swift */; }; C8E9D2AF1BD3FD960079D0DB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; }; + CBEE77541BD8C7B700AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE77531BD8C7B700AD584C /* ToArray.swift */; }; D2245A191BD5654C00E7146F /* WithLatestFrom.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2245A0B1BD564A700E7146F /* WithLatestFrom.swift */; }; - CBEE77541BD8C7B700AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE77531BD8C7B700AD584C /* ToArray.swift */; settings = {ASSET_TAGS = (); }; }; D2AF91981BD3D95900A008C1 /* Using.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AF91881BD2C51900A008C1 /* Using.swift */; }; EC91FB951BBA144400973245 /* GitHubSearchRepositoriesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC91FB941BBA144400973245 /* GitHubSearchRepositoriesViewController.swift */; }; /* End PBXBuildFile section */ @@ -469,6 +470,7 @@ C83367111AD029AE00C668A7 /* HtmlParsing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HtmlParsing.swift; sourceTree = ""; }; C83367121AD029AE00C668A7 /* ImageService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageService.swift; sourceTree = ""; }; C83367211AD029AE00C668A7 /* Wireframe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Wireframe.swift; sourceTree = ""; }; + C84CC52D1BDC344100E06A64 /* ElementAt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElementAt.swift; sourceTree = ""; }; C859B9A31B45C5D900D012D7 /* PartialUpdatesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PartialUpdatesViewController.swift; sourceTree = ""; }; C859B9A51B45C80700D012D7 /* RxCollectionViewSectionedReloadDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewSectionedReloadDataSource.swift; sourceTree = ""; }; C859B9A71B45C83700D012D7 /* RxCollectionViewSectionedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewSectionedDataSource.swift; sourceTree = ""; }; @@ -1086,6 +1088,7 @@ C89464481BC6C2B00055219D /* Implementations */ = { isa = PBXGroup; children = ( + C84CC52D1BDC344100E06A64 /* ElementAt.swift */, C89464491BC6C2B00055219D /* Amb.swift */, C894644A1BC6C2B00055219D /* AnonymousObservable.swift */, C894644B1BC6C2B00055219D /* AsObservable.swift */, @@ -1737,6 +1740,7 @@ C89464F51BC6C2B00055219D /* AnyObserver.swift in Sources */, C89464D71BC6C2B00055219D /* Range.swift in Sources */, C803974A1BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift in Sources */, + C84CC52E1BDC344100E06A64 /* ElementAt.swift in Sources */, C8297E431B6CF905000589EA /* GitHubAPI.swift in Sources */, C89464EB1BC6C2B00055219D /* Observable+Aggregate.swift in Sources */, C8297E441B6CF905000589EA /* PseudoRandomGenerator.swift in Sources */, diff --git a/RxSwift/Error.swift b/RxSwift/Error.swift index 3c4a7305..74307fe3 100644 --- a/RxSwift/Error.swift +++ b/RxSwift/Error.swift @@ -31,6 +31,10 @@ public enum RxErrorCode : Int { Aritmetic overflow error. */ case Overflow = 4 + /** + Argument out of range error. + */ + case ArgumentOutOfRange = 5 } /** @@ -56,5 +60,10 @@ public struct RxError { Singleton instance of aritmetic overflow error. */ public static let OverflowError = NSError(domain: RxErrorDomain, code: RxErrorCode.Overflow.rawValue, userInfo: nil) + + /** + Singleton instance of argument out of range error. + */ + public static let ArgumentOutOfRange = NSError(domain: RxErrorDomain, code: RxErrorCode.ArgumentOutOfRange.rawValue, userInfo: nil) } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/ElementAt.swift b/RxSwift/Observables/Implementations/ElementAt.swift new file mode 100644 index 00000000..a5772062 --- /dev/null +++ b/RxSwift/Observables/Implementations/ElementAt.swift @@ -0,0 +1,68 @@ +// +// ElementAt.swift +// Rx +// +// Created by Junior B. on 21/10/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + + +class ElementAtSink : Sink, ObserverType { + typealias Parent = ElementAt + + let parent: Parent + var i: Int + + init(parent: Parent, observer: O, cancel: Disposable) { + self.parent = parent + self.i = parent.index + + super.init(observer: observer, cancel: cancel) + } + + func on(event: Event) { + switch event { + case .Next(_): + + if (i == 0) { + observer?.on(event) + observer?.on(.Completed) + self.dispose() + } + + i-- + case .Error(let e): + observer?.on(.Error(e)) + self.dispose() + case .Completed: + if (parent.throwOnEmpty) { + observer?.onError(RxError.ArgumentOutOfRange) + } else { + observer?.on(.Completed) + } + + self.dispose() + } + } +} + +class ElementAt : Producer { + + let source: Observable + let throwOnEmpty: Bool + let index: Int + + init(source: Observable, index: Int, throwOnEmpty: Bool) { + self.source = source + self.index = index + self.throwOnEmpty = throwOnEmpty + } + + override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + let sink = ElementAtSink(parent: self, observer: observer, cancel: cancel) + setSink(sink) + return source.subscribeSafe(sink) + } +} \ No newline at end of file diff --git a/RxSwift/Observables/Observable+StandardSequenceOperators.swift b/RxSwift/Observables/Observable+StandardSequenceOperators.swift index 827ea994..ffc78908 100644 --- a/RxSwift/Observables/Observable+StandardSequenceOperators.swift +++ b/RxSwift/Observables/Observable+StandardSequenceOperators.swift @@ -179,4 +179,21 @@ extension ObservableType { -> Observable { return FlatMap(source: self.asObservable(), selector: selector) } +} + +// elementAt + +extension ObservableType { + + /** + Returns a sequence emitting only item _n_ emitted by an Observable + + - parameter index: The index of the required item (starting from 0). + - returns: An observable sequence that emits the desired item as its own sole emission. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func elementAt(index: Int) + -> Observable { + return ElementAt(source: self.asObservable(), index: index, throwOnEmpty: false) + } } \ No newline at end of file diff --git a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift index fa6ff595..fe9f0b0c 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift @@ -36,8 +36,7 @@ func isPrime(i: Int) -> Bool { return true } -// where - +// MARK: where extension ObservableStandardSequenceOperators { func test_filterComplete() { let scheduler = TestScheduler(initialClock: 0) @@ -210,7 +209,7 @@ extension ObservableStandardSequenceOperators { } } -// takeWhile +// MARK: takeWhile extension ObservableStandardSequenceOperators { func testTakeWhile_Complete_Before() { let scheduler = TestScheduler(initialClock: 0) @@ -701,7 +700,7 @@ extension ObservableStandardSequenceOperators { } -// map +// MARK: map // these test are not port from Rx extension ObservableStandardSequenceOperators { func testMap_Never() { @@ -1043,7 +1042,7 @@ extension ObservableStandardSequenceOperators { } } -// flatMap +// MARK: flatMap extension ObservableStandardSequenceOperators { func testFlatMap_Complete() { @@ -2353,7 +2352,7 @@ extension ObservableStandardSequenceOperators { } -// take +// MARK: take extension ObservableStandardSequenceOperators { func testTake_Complete_After() { @@ -2817,7 +2816,7 @@ extension ObservableStandardSequenceOperators { } } -// skip +// MARK: skip extension ObservableStandardSequenceOperators { func testSkip_Complete_After() { let scheduler = TestScheduler(initialClock: 0) @@ -3644,4 +3643,251 @@ extension ObservableStandardSequenceOperators { Subscription(200, 350) ]) } +} + +// MARK: elementAt +extension ObservableStandardSequenceOperators { + + func testElementAt_Complete_After() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(70, 6), + next(150, 4), + next(210, 9), + next(230, 13), + next(270, 7), + next(280, 1), + next(300, -1), + next(310, 3), + next(340, 8), + next(370, 11), + next(410, 15), + next(415, 16), + next(460, 72), + next(510, 76), + next(560, 32), + next(570, -100), + next(580, -3), + next(590, 5), + next(630, 10), + completed(690) + ]) + + let res = scheduler.start { + xs.elementAt(10) + } + + XCTAssertEqual(res.messages, [ + next(460, 72), + completed(460) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 460) + ]) + } + + + func testElementAt_Complete_Before() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(70, 6), + next(150, 4), + next(210, 9), + next(230, 13), + next(270, 7), + next(280, 1), + next(300, -1), + completed(320) + ]) + + let res = scheduler.start { + xs.elementAt(10) + } + + XCTAssertEqual(res.messages, [ + completed(320) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 320) + ]) + } + + func testElementAt_Error_After() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(70, 6), + next(150, 4), + next(210, 9), + next(230, 13), + next(270, 7), + next(280, 1), + next(300, -1), + next(310, 3), + next(340, 8), + next(370, 11), + next(410, 15), + next(415, 16), + next(460, 72), + next(510, 76), + next(560, 32), + next(570, -100), + next(580, -3), + next(590, 5), + next(630, 10), + error(690, testError) + ]) + + let res = scheduler.start { + xs.elementAt(10) + } + + XCTAssertEqual(res.messages, [ + next(460, 72), + completed(460) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 460) + ]) + } + + func testElementAt_Error_Before() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(70, 6), + next(150, 4), + next(210, 9), + next(230, 13), + next(270, 7), + next(280, 1), + next(300, -1), + error(310, testError) + ]) + + let res = scheduler.start { + xs.elementAt(10) + } + + XCTAssertEqual(res.messages, [ + error(310, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 310) + ]) + } + + func testElementAt_Dispose_Before() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(70, 6), + next(150, 4), + next(210, 9), + next(230, 13), + next(270, 7), + next(280, 1), + next(300, -1), + next(310, 3), + next(340, 8), + next(370, 11), + next(410, 15), + next(415, 16), + next(460, 72), + next(510, 76), + next(560, 32), + next(570, -100), + next(580, -3), + next(590, 5), + next(630, 10), + error(690, testError) + ]) + + let res = scheduler.start(250) { + xs.elementAt(3) + } + + XCTAssertEqual(res.messages, []) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 250) + ]) + } + + func testElementAt_Dispose_After() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(70, 6), + next(150, 4), + next(210, 9), + next(230, 13), + next(270, 7), + next(280, 1), + next(300, -1), + next(310, 3), + next(340, 8), + next(370, 11), + next(410, 15), + next(415, 16), + next(460, 72), + next(510, 76), + next(560, 32), + next(570, -100), + next(580, -3), + next(590, 5), + next(630, 10), + error(690, testError) + ]) + + let res = scheduler.start(400) { + xs.elementAt(3) + } + + XCTAssertEqual(res.messages, [ + next(280, 1), + completed(280) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 280) + ]) + } + + func testElementAt_First() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(70, 6), + next(150, 4), + next(210, 9), + next(230, 13), + next(270, 7), + next(280, 1), + next(300, -1), + next(310, 3), + next(340, 8), + next(370, 11), + completed(400) + ]) + + let res = scheduler.start { + xs.elementAt(0) + } + + XCTAssertEqual(res.messages, [ + next(210, 9), + completed(210) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 210) + ]) + } } \ No newline at end of file From 61e8e33cd7fdc9a1daee9b5607655729c3df8ce2 Mon Sep 17 00:00:00 2001 From: Junior B Date: Fri, 23 Oct 2015 11:03:39 +0200 Subject: [PATCH 095/210] Improves `elementAt` to be platform consistent --- .../Observables/Implementations/ElementAt.swift | 15 +++++++++++++-- .../Observable+StandardSequenceOperators.swift | 2 +- RxSwift/Rx.swift | 9 +++++++++ ...Observable+StandardSequenceOperatorsTest.swift | 2 +- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/RxSwift/Observables/Implementations/ElementAt.swift b/RxSwift/Observables/Implementations/ElementAt.swift index a5772062..bfc1d8d7 100644 --- a/RxSwift/Observables/Implementations/ElementAt.swift +++ b/RxSwift/Observables/Implementations/ElementAt.swift @@ -26,13 +26,24 @@ class ElementAtSink : Sink< switch event { case .Next(_): + if i < 0 { + rxFatalError("index can't be negative") + } + if (i == 0) { observer?.on(event) observer?.on(.Completed) self.dispose() } - - i-- + + do { + try decrementChecked(&i) + } catch(let e) { + observer?.onError(e) + dispose() + return + } + case .Error(let e): observer?.on(.Error(e)) self.dispose() diff --git a/RxSwift/Observables/Observable+StandardSequenceOperators.swift b/RxSwift/Observables/Observable+StandardSequenceOperators.swift index ffc78908..fadccece 100644 --- a/RxSwift/Observables/Observable+StandardSequenceOperators.swift +++ b/RxSwift/Observables/Observable+StandardSequenceOperators.swift @@ -194,6 +194,6 @@ extension ObservableType { @warn_unused_result(message="http://git.io/rxs.uo") public func elementAt(index: Int) -> Observable { - return ElementAt(source: self.asObservable(), index: index, throwOnEmpty: false) + return ElementAt(source: self.asObservable(), index: index, throwOnEmpty: true) } } \ No newline at end of file diff --git a/RxSwift/Rx.swift b/RxSwift/Rx.swift index 90598ec3..4621c980 100644 --- a/RxSwift/Rx.swift +++ b/RxSwift/Rx.swift @@ -37,6 +37,15 @@ func incrementChecked(inout i: Int) throws -> Int { return result } +func decrementChecked(inout i: Int) throws -> Int { + if i == Int.min { + throw RxError.OverflowError + } + let result = i + i -= 1 + return result +} + extension NSObject { func rx_synchronized(@noescape action: () -> T) -> T { objc_sync_enter(self) diff --git a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift index fe9f0b0c..ecdc305c 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift @@ -3708,7 +3708,7 @@ extension ObservableStandardSequenceOperators { } XCTAssertEqual(res.messages, [ - completed(320) + error(320, RxError.ArgumentOutOfRange) ]) XCTAssertEqual(xs.subscriptions, [ From a81b9afecfcac67a39134629e16e02cca0660999 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 25 Oct 2015 00:00:00 +0200 Subject: [PATCH 096/210] Moves index check closer to source of error. --- Rx.xcodeproj/project.pbxproj | 14 ++++++++++---- .../Observables/Implementations/ElementAt.swift | 8 ++++---- RxSwift/Observables/Implementations/Range.swift | 8 ++++++++ RxSwift/Observables/Observable+Creation.swift | 8 -------- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index 6ff46531..b2438460 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -292,7 +292,10 @@ C84B38EA1BA43380001B7D88 /* ScheduledItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B38E71BA43380001B7D88 /* ScheduledItem.swift */; }; C84B38EE1BA433CD001B7D88 /* Generate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B38ED1BA433CD001B7D88 /* Generate.swift */; }; C84B38EF1BA433CD001B7D88 /* Generate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B38ED1BA433CD001B7D88 /* Generate.swift */; }; - C84CC52C1BDC342100E06A64 /* ElementAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC52B1BDC342100E06A64 /* ElementAt.swift */; }; + C84CC5401BDC3B3700E06A64 /* ElementAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC53F1BDC3B3700E06A64 /* ElementAt.swift */; }; + C84CC5411BDC3B3E00E06A64 /* ElementAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC53F1BDC3B3700E06A64 /* ElementAt.swift */; }; + C84CC5421BDC3B3E00E06A64 /* ElementAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC53F1BDC3B3700E06A64 /* ElementAt.swift */; }; + C84CC5431BDC3B3E00E06A64 /* ElementAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC53F1BDC3B3700E06A64 /* ElementAt.swift */; }; C86409FC1BA593F500D3C4E8 /* Range.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409FB1BA593F500D3C4E8 /* Range.swift */; }; C86409FD1BA593F500D3C4E8 /* Range.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409FB1BA593F500D3C4E8 /* Range.swift */; }; C8640A031BA5B12A00D3C4E8 /* Repeat.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8640A021BA5B12A00D3C4E8 /* Repeat.swift */; }; @@ -932,7 +935,7 @@ C849BE2A1BAB5D070019AD27 /* ObservableConvertibleType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableConvertibleType.swift; sourceTree = ""; }; C84B38E71BA43380001B7D88 /* ScheduledItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduledItem.swift; sourceTree = ""; }; C84B38ED1BA433CD001B7D88 /* Generate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Generate.swift; sourceTree = ""; }; - C84CC52B1BDC342100E06A64 /* ElementAt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElementAt.swift; sourceTree = ""; }; + C84CC53F1BDC3B3700E06A64 /* ElementAt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElementAt.swift; sourceTree = ""; }; C86409FB1BA593F500D3C4E8 /* Range.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Range.swift; sourceTree = ""; }; C8640A021BA5B12A00D3C4E8 /* Repeat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Repeat.swift; sourceTree = ""; }; C88253F11B8A752B00B02D69 /* RxCollectionViewReactiveArrayDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewReactiveArrayDataSource.swift; sourceTree = ""; }; @@ -1173,7 +1176,7 @@ C8093C6A1B8A72BE0088E94D /* Implementations */ = { isa = PBXGroup; children = ( - C84CC52B1BDC342100E06A64 /* ElementAt.swift */, + C84CC53F1BDC3B3700E06A64 /* ElementAt.swift */, D2245A1A1BD5657300E7146F /* WithLatestFrom.swift */, C8093C6B1B8A72BE0088E94D /* Amb.swift */, C8C3DA111B93A3EA004D233E /* AnonymousObservable.swift */, @@ -2153,6 +2156,7 @@ C8C3DA101B939767004D233E /* CurrentThreadScheduler.swift in Sources */, C8093D861B8A72BE0088E94D /* Rx.swift in Sources */, C80D342F1B9245A40014629D /* CombineLatest+CollectionType.swift in Sources */, + C84CC5411BDC3B3E00E06A64 /* ElementAt.swift in Sources */, C8093DA61B8A72BE0088E94D /* SubjectType.swift in Sources */, C8093D5C1B8A72BE0088E94D /* Observable+Debug.swift in Sources */, C8093D581B8A72BE0088E94D /* Observable+Concurrency.swift in Sources */, @@ -2274,7 +2278,7 @@ C8C3DA0F1B939767004D233E /* CurrentThreadScheduler.swift in Sources */, C8093D851B8A72BE0088E94D /* Rx.swift in Sources */, C80D342E1B9245A40014629D /* CombineLatest+CollectionType.swift in Sources */, - C84CC52C1BDC342100E06A64 /* ElementAt.swift in Sources */, + C84CC5401BDC3B3700E06A64 /* ElementAt.swift in Sources */, C8093DA51B8A72BE0088E94D /* SubjectType.swift in Sources */, C8093D5B1B8A72BE0088E94D /* Observable+Debug.swift in Sources */, C8093D571B8A72BE0088E94D /* Observable+Concurrency.swift in Sources */, @@ -2396,6 +2400,7 @@ C8F0BFBB1BBBFB8B001B112F /* CurrentThreadScheduler.swift in Sources */, C8F0BFBC1BBBFB8B001B112F /* Rx.swift in Sources */, C8F0BFBD1BBBFB8B001B112F /* CombineLatest+CollectionType.swift in Sources */, + C84CC5431BDC3B3E00E06A64 /* ElementAt.swift in Sources */, C8F0BFBE1BBBFB8B001B112F /* SubjectType.swift in Sources */, C8F0BFBF1BBBFB8B001B112F /* Observable+Debug.swift in Sources */, C8F0BFC01BBBFB8B001B112F /* Observable+Concurrency.swift in Sources */, @@ -2667,6 +2672,7 @@ D2EBEB1F1BB9B6C1003A27DC /* Skip.swift in Sources */, D2EBEB321BB9B6CA003A27DC /* Observable+StandardSequenceOperators.swift in Sources */, D2EBEB391BB9B6D8003A27DC /* DispatchQueueSchedulerPriority.swift in Sources */, + C84CC5421BDC3B3E00E06A64 /* ElementAt.swift in Sources */, D2EBEB081BB9B6C1003A27DC /* DelaySubscription.swift in Sources */, D2EBEADE1BB9B697003A27DC /* Disposable.swift in Sources */, D2EBEB2F1BB9B6CA003A27DC /* Observable+Debug.swift in Sources */, diff --git a/RxSwift/Observables/Implementations/ElementAt.swift b/RxSwift/Observables/Implementations/ElementAt.swift index bfc1d8d7..a9a24eef 100644 --- a/RxSwift/Observables/Implementations/ElementAt.swift +++ b/RxSwift/Observables/Implementations/ElementAt.swift @@ -26,10 +26,6 @@ class ElementAtSink : Sink< switch event { case .Next(_): - if i < 0 { - rxFatalError("index can't be negative") - } - if (i == 0) { observer?.on(event) observer?.on(.Completed) @@ -66,6 +62,10 @@ class ElementAt : Producer { let index: Int init(source: Observable, index: Int, throwOnEmpty: Bool) { + if index < 0 { + rxFatalError("index can't be negative") + } + self.source = source self.index = index self.throwOnEmpty = throwOnEmpty diff --git a/RxSwift/Observables/Implementations/Range.swift b/RxSwift/Observables/Implementations/Range.swift index 2707eaf8..ded21856 100644 --- a/RxSwift/Observables/Implementations/Range.swift +++ b/RxSwift/Observables/Implementations/Range.swift @@ -14,6 +14,14 @@ class RangeProducer<_CompilerWorkaround> : Producer { private let _scheduler: ImmediateSchedulerType init(start: Int, count: Int, scheduler: ImmediateSchedulerType) { + if count < 0 { + rxFatalError("count can't be negative") + } + + if start &+ (count - 1) < start { + rxFatalError("overflow of count") + } + _start = start _count = count _scheduler = scheduler diff --git a/RxSwift/Observables/Observable+Creation.swift b/RxSwift/Observables/Observable+Creation.swift index 12d77432..9b3e9678 100644 --- a/RxSwift/Observables/Observable+Creation.swift +++ b/RxSwift/Observables/Observable+Creation.swift @@ -148,14 +148,6 @@ Generates an observable sequence of integral numbers within a specified range, u */ @warn_unused_result(message="http://git.io/rxs.uo") public func range(start: Int, _ count: Int, _ scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance) -> Observable { - if count < 0 { - rxFatalError("count can't be negative") - } - - if start &+ (count - 1) < start { - rxFatalError("overflow of count") - } - return RangeProducer(start: start, count: count, scheduler: scheduler) } From e4d3c36d9780bff8a1405fae1a3c7aac96cf86ca Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 25 Oct 2015 00:26:53 +0200 Subject: [PATCH 097/210] Adds `_` to private variables for `toArray` and `elementAt`. --- .../Implementations/ElementAt.swift | 28 +++++++++---------- .../Observables/Implementations/ToArray.swift | 18 ++++++------ 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/RxSwift/Observables/Implementations/ElementAt.swift b/RxSwift/Observables/Implementations/ElementAt.swift index a9a24eef..29f635e2 100644 --- a/RxSwift/Observables/Implementations/ElementAt.swift +++ b/RxSwift/Observables/Implementations/ElementAt.swift @@ -12,12 +12,12 @@ import Foundation class ElementAtSink : Sink, ObserverType { typealias Parent = ElementAt - let parent: Parent - var i: Int + let _parent: Parent + var _i: Int init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent - self.i = parent.index + _parent = parent + _i = parent._index super.init(observer: observer, cancel: cancel) } @@ -26,14 +26,14 @@ class ElementAtSink : Sink< switch event { case .Next(_): - if (i == 0) { + if (_i == 0) { observer?.on(event) observer?.on(.Completed) self.dispose() } do { - try decrementChecked(&i) + try decrementChecked(&_i) } catch(let e) { observer?.onError(e) dispose() @@ -44,7 +44,7 @@ class ElementAtSink : Sink< observer?.on(.Error(e)) self.dispose() case .Completed: - if (parent.throwOnEmpty) { + if (_parent._throwOnEmpty) { observer?.onError(RxError.ArgumentOutOfRange) } else { observer?.on(.Completed) @@ -57,23 +57,23 @@ class ElementAtSink : Sink< class ElementAt : Producer { - let source: Observable - let throwOnEmpty: Bool - let index: Int + let _source: Observable + let _throwOnEmpty: Bool + let _index: Int init(source: Observable, index: Int, throwOnEmpty: Bool) { if index < 0 { rxFatalError("index can't be negative") } - self.source = source - self.index = index - self.throwOnEmpty = throwOnEmpty + self._source = source + self._index = index + self._throwOnEmpty = throwOnEmpty } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { let sink = ElementAtSink(parent: self, observer: observer, cancel: cancel) setSink(sink) - return source.subscribeSafe(sink) + return _source.subscribeSafe(sink) } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/ToArray.swift b/RxSwift/Observables/Implementations/ToArray.swift index 00a450e4..35a629ca 100644 --- a/RxSwift/Observables/Implementations/ToArray.swift +++ b/RxSwift/Observables/Implementations/ToArray.swift @@ -11,11 +11,11 @@ import Foundation class ToArraySink : Sink, ObserverType { typealias Parent = ToArray - let parent: Parent - var list = Array() + let _parent: Parent + var _list = Array() init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + self._parent = parent super.init(observer: observer, cancel: cancel) } @@ -23,12 +23,12 @@ class ToArraySink : Sink< func on(event: Event) { switch event { case .Next(let value): - self.list.append(value) + self._list.append(value) case .Error(let e): observer?.on(.Error(e)) self.dispose() case .Completed: - observer?.on(.Next(self.list)) + observer?.on(.Next(_list)) observer?.on(.Completed) self.dispose() } @@ -36,15 +36,15 @@ class ToArraySink : Sink< } class ToArray : Producer<[SourceType]> { - let source: Observable - + let _source: Observable + init(source: Observable) { - self.source = source + _source = source } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { let sink = ToArraySink(parent: self, observer: observer, cancel: cancel) setSink(sink) - return source.subscribeSafe(sink) + return _source.subscribeSafe(sink) } } \ No newline at end of file From a861028d4fdc56fcf0b6acca5004746eb87baa7b Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sat, 24 Oct 2015 09:57:44 +0200 Subject: [PATCH 098/210] Fixes performance tester. --- RxTests/PerformanceTests/main.swift | 29 +++++++++++++++++++++-- RxTests/RxTests.xcodeproj/project.pbxproj | 8 +++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/RxTests/PerformanceTests/main.swift b/RxTests/PerformanceTests/main.swift index 53ce2b89..57d74db5 100644 --- a/RxTests/PerformanceTests/main.swift +++ b/RxTests/PerformanceTests/main.swift @@ -9,6 +9,8 @@ import Foundation import RxSwift import RxCocoa +import AppKit +import CoreLocation let NumberOfIterations = 1000 @@ -45,13 +47,24 @@ func measureMemoryUsage(@noescape work: () -> ()) -> (bytesAllocated: UInt64, al return (approxValuePerIteration(bytesAfter - bytes), approxValuePerIteration(allocationsAfter - allocations)) } +let bechmarkTime = true + func compareTwoImplementations(@noescape first first: () -> (), @noescape second: () -> ()) { // first warm up to keep it fair first() second() - let time1 = measureTime(first) - let time2 = measureTime(second) + let time1: UInt64 + let time2: UInt64 + + if bechmarkTime { + time1 = measureTime(first) + time2 = measureTime(second) + } + else { + time1 = 0 + time2 = 0 + } registerMallocHooks() @@ -72,6 +85,18 @@ func compareTwoImplementations(@noescape first first: () -> (), @noescape second } compareTwoImplementations(first: { + let publishSubject = PublishSubject() + + publishSubject + .shareReplay(1) + .subscribeNext { _ in + + } + + + for i in 0..<100 { + publishSubject.on(.Next(i)) + } }, second: { diff --git a/RxTests/RxTests.xcodeproj/project.pbxproj b/RxTests/RxTests.xcodeproj/project.pbxproj index 0feddd04..97e01155 100644 --- a/RxTests/RxTests.xcodeproj/project.pbxproj +++ b/RxTests/RxTests.xcodeproj/project.pbxproj @@ -51,6 +51,8 @@ C836EA081B8A76CD00AB941D /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C836EA001B8A76A900AB941D /* RxSwift.framework */; }; C84B8FC21B89D0D500C9CCCF /* BagTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B8FC11B89D0D500C9CCCF /* BagTest.swift */; }; C84B8FC31B89D0D500C9CCCF /* BagTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B8FC11B89D0D500C9CCCF /* BagTest.swift */; }; + C84CC4E91BDADA0B00E06A64 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C836EA001B8A76A900AB941D /* RxSwift.framework */; }; + C84CC4EB1BDAEDF700E06A64 /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C84CC4EA1BDAEDF700E06A64 /* RxCocoa.framework */; }; C85F4E431B7F70EA00A866C7 /* CompositeObserverTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C85F4E421B7F70EA00A866C7 /* CompositeObserverTest.swift */; }; C85F4E441B7F70EA00A866C7 /* CompositeObserverTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C85F4E421B7F70EA00A866C7 /* CompositeObserverTest.swift */; }; C8633AE51B0A9FF300375D60 /* KVOObservableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8633AE41B0A9FF300375D60 /* KVOObservableTests.swift */; }; @@ -217,6 +219,8 @@ C81CC92A1B513FD400915606 /* NSObject+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+RxTests.swift"; sourceTree = ""; }; C836EA001B8A76A900AB941D /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C84B8FC11B89D0D500C9CCCF /* BagTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BagTest.swift; sourceTree = ""; }; + C84CC4E71BDAD9FF00E06A64 /* RxBlocking.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxBlocking.framework; path = ../build/Debug/RxBlocking.framework; sourceTree = ""; }; + C84CC4EA1BDAEDF700E06A64 /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxCocoa.framework; path = ../build/Debug/RxCocoa.framework; sourceTree = ""; }; C85F4E421B7F70EA00A866C7 /* CompositeObserverTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompositeObserverTest.swift; sourceTree = ""; }; C8633AE41B0A9FF300375D60 /* KVOObservableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVOObservableTests.swift; sourceTree = ""; }; C868D0F81BB76A29003D1474 /* PerformanceTools.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PerformanceTools.swift; sourceTree = ""; }; @@ -262,6 +266,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + C84CC4EB1BDAEDF700E06A64 /* RxCocoa.framework in Frameworks */, + C84CC4E91BDADA0B00E06A64 /* RxSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -291,6 +297,8 @@ C81108151AF50DDA001C13E4 = { isa = PBXGroup; children = ( + C84CC4EA1BDAEDF700E06A64 /* RxCocoa.framework */, + C84CC4E71BDAD9FF00E06A64 /* RxBlocking.framework */, C8A468CE1B8A897D00BF917B /* RxBlocking.framework */, C8A468CC1B8A897800BF917B /* RxCocoa.framework */, C836EA001B8A76A900AB941D /* RxSwift.framework */, From d1666db4d1c092e1770046d726dc239c46ee2b3e Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sat, 24 Oct 2015 23:45:09 +0200 Subject: [PATCH 099/210] Removes unnecessary `subscribeSafe` in case there is already an `Observable` source. --- Rx.xcodeproj/project.pbxproj | 10 - RxExample/RxExample.xcodeproj/project.pbxproj | 4 - RxSwift/Observable+Extensions.swift | 2 +- RxSwift/Observables/Implementations/Amb.swift | 4 +- .../Observables/Implementations/Buffer.swift | 2 +- .../Observables/Implementations/Catch.swift | 6 +- .../CombineLatest+CollectionType.swift | 2 +- .../Implementations/CombineLatest+arity.swift | 238 ++++++------ .../Implementations/CombineLatest+arity.tt | 20 +- .../ConnectableObservable.swift | 6 +- .../Observables/Implementations/Debug.swift | 2 +- .../Implementations/Deferred.swift | 2 +- .../Implementations/DelaySubscription.swift | 2 +- .../DistinctUntilChanged.swift | 2 +- RxSwift/Observables/Implementations/Do.swift | 2 +- .../Observables/Implementations/Filter.swift | 2 +- .../Observables/Implementations/FlatMap.swift | 4 +- RxSwift/Observables/Implementations/Map.swift | 4 +- .../Observables/Implementations/Merge.swift | 10 +- .../Implementations/Multicast.swift | 2 +- .../Implementations/ObserveOn.swift | 2 +- .../ObserveOnSerialDispatchQueue.swift | 2 +- .../Observables/Implementations/Reduce.swift | 2 +- .../Observables/Implementations/Sample.swift | 4 +- .../Observables/Implementations/Scan.swift | 2 +- .../Observables/Implementations/Skip.swift | 4 +- .../Implementations/SkipUntil.swift | 4 +- .../Implementations/SkipWhile.swift | 4 +- .../Implementations/StartWith.swift | 2 +- .../Observables/Implementations/Switch.swift | 4 +- .../Observables/Implementations/Take.swift | 4 +- .../Implementations/TakeUntil.swift | 4 +- .../Implementations/TakeWhile.swift | 4 +- .../Implementations/Throttle.swift | 2 +- .../Observables/Implementations/ToArray.swift | 2 +- .../Observables/Implementations/Using.swift | 4 +- .../Implementations/WithLatestFrom.swift | 4 +- .../Implementations/Zip+CollectionType.swift | 2 +- .../Implementations/Zip+arity.swift | 364 +++++++++--------- .../Observables/Implementations/Zip+arity.tt | 20 +- RxSwift/Observers/TailRecursiveSink.swift | 2 +- 41 files changed, 377 insertions(+), 391 deletions(-) diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index b2438460..cfc86a26 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -59,8 +59,6 @@ C8093CFE1B8A72BE0088E94D /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C681B8A72BE0088E94D /* Observable.swift */; }; C8093CFF1B8A72BE0088E94D /* Amb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C6B1B8A72BE0088E94D /* Amb.swift */; }; C8093D001B8A72BE0088E94D /* Amb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C6B1B8A72BE0088E94D /* Amb.swift */; }; - C8093D031B8A72BE0088E94D /* AsObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C6D1B8A72BE0088E94D /* AsObservable.swift */; }; - C8093D041B8A72BE0088E94D /* AsObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C6D1B8A72BE0088E94D /* AsObservable.swift */; }; C8093D051B8A72BE0088E94D /* Catch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C6E1B8A72BE0088E94D /* Catch.swift */; }; C8093D061B8A72BE0088E94D /* Catch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C6E1B8A72BE0088E94D /* Catch.swift */; }; C8093D071B8A72BE0088E94D /* CombineLatest+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C6F1B8A72BE0088E94D /* CombineLatest+arity.swift */; }; @@ -393,7 +391,6 @@ C8F0BFA81BBBFB8B001B112F /* Observable+Time.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C9D1B8A72BE0088E94D /* Observable+Time.swift */; }; C8F0BFA91BBBFB8B001B112F /* Observable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C671B8A72BE0088E94D /* Observable+Extensions.swift */; }; C8F0BFAA1BBBFB8B001B112F /* Throttle.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C901B8A72BE0088E94D /* Throttle.swift */; }; - C8F0BFAB1BBBFB8B001B112F /* AsObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C6D1B8A72BE0088E94D /* AsObservable.swift */; }; C8F0BFAC1BBBFB8B001B112F /* Catch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C6E1B8A72BE0088E94D /* Catch.swift */; }; C8F0BFAD1BBBFB8B001B112F /* CombineLatest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C711B8A72BE0088E94D /* CombineLatest.swift */; }; C8F0BFAE1BBBFB8B001B112F /* Observable+Multiple.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C9A1B8A72BE0088E94D /* Observable+Multiple.swift */; }; @@ -647,7 +644,6 @@ D2EBEAFB1BB9B6B2003A27DC /* StableCompositeDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C611B8A72BE0088E94D /* StableCompositeDisposable.swift */; }; D2EBEAFC1BB9B6BA003A27DC /* Amb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C6B1B8A72BE0088E94D /* Amb.swift */; }; D2EBEAFD1BB9B6BA003A27DC /* AnonymousObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C3DA111B93A3EA004D233E /* AnonymousObservable.swift */; }; - D2EBEAFE1BB9B6BA003A27DC /* AsObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C6D1B8A72BE0088E94D /* AsObservable.swift */; }; D2EBEAFF1BB9B6BA003A27DC /* Buffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C821DBA11BA4DCAB008F3809 /* Buffer.swift */; }; D2EBEB001BB9B6BA003A27DC /* Catch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C6E1B8A72BE0088E94D /* Catch.swift */; }; D2EBEB011BB9B6BA003A27DC /* CombineLatest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C711B8A72BE0088E94D /* CombineLatest.swift */; }; @@ -818,7 +814,6 @@ C8093C671B8A72BE0088E94D /* Observable+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Extensions.swift"; sourceTree = ""; }; C8093C681B8A72BE0088E94D /* Observable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = ""; }; C8093C6B1B8A72BE0088E94D /* Amb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Amb.swift; sourceTree = ""; }; - C8093C6D1B8A72BE0088E94D /* AsObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsObservable.swift; sourceTree = ""; }; C8093C6E1B8A72BE0088E94D /* Catch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Catch.swift; sourceTree = ""; }; C8093C6F1B8A72BE0088E94D /* CombineLatest+arity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CombineLatest+arity.swift"; sourceTree = ""; }; C8093C701B8A72BE0088E94D /* CombineLatest+arity.tt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "CombineLatest+arity.tt"; sourceTree = ""; }; @@ -1180,7 +1175,6 @@ D2245A1A1BD5657300E7146F /* WithLatestFrom.swift */, C8093C6B1B8A72BE0088E94D /* Amb.swift */, C8C3DA111B93A3EA004D233E /* AnonymousObservable.swift */, - C8093C6D1B8A72BE0088E94D /* AsObservable.swift */, C821DBA11BA4DCAB008F3809 /* Buffer.swift */, C8093C6E1B8A72BE0088E94D /* Catch.swift */, C8093C711B8A72BE0088E94D /* CombineLatest.swift */, @@ -2133,7 +2127,6 @@ C8093D641B8A72BE0088E94D /* Observable+Time.swift in Sources */, C8093CFC1B8A72BE0088E94D /* Observable+Extensions.swift in Sources */, C8093D4A1B8A72BE0088E94D /* Throttle.swift in Sources */, - C8093D041B8A72BE0088E94D /* AsObservable.swift in Sources */, C8B145011BD2D80100267DCE /* ImmediateScheduler.swift in Sources */, C8093D061B8A72BE0088E94D /* Catch.swift in Sources */, C8093D0C1B8A72BE0088E94D /* CombineLatest.swift in Sources */, @@ -2255,7 +2248,6 @@ C8093D631B8A72BE0088E94D /* Observable+Time.swift in Sources */, C8093CFB1B8A72BE0088E94D /* Observable+Extensions.swift in Sources */, C8093D491B8A72BE0088E94D /* Throttle.swift in Sources */, - C8093D031B8A72BE0088E94D /* AsObservable.swift in Sources */, C8B145001BD2D80100267DCE /* ImmediateScheduler.swift in Sources */, C8093D051B8A72BE0088E94D /* Catch.swift in Sources */, C8093D0B1B8A72BE0088E94D /* CombineLatest.swift in Sources */, @@ -2377,7 +2369,6 @@ C8F0BFA81BBBFB8B001B112F /* Observable+Time.swift in Sources */, C8F0BFA91BBBFB8B001B112F /* Observable+Extensions.swift in Sources */, C8F0BFAA1BBBFB8B001B112F /* Throttle.swift in Sources */, - C8F0BFAB1BBBFB8B001B112F /* AsObservable.swift in Sources */, C8B145031BD2D80100267DCE /* ImmediateScheduler.swift in Sources */, C8F0BFAC1BBBFB8B001B112F /* Catch.swift in Sources */, C8F0BFAD1BBBFB8B001B112F /* CombineLatest.swift in Sources */, @@ -2722,7 +2713,6 @@ D2EBEAED1BB9B6A4003A27DC /* Bag.swift in Sources */, D2EBEB1A1BB9B6C1003A27DC /* RefCount.swift in Sources */, D2EBEB141BB9B6C1003A27DC /* Never.swift in Sources */, - D2EBEAFE1BB9B6BA003A27DC /* AsObservable.swift in Sources */, D2EBEAE01BB9B697003A27DC /* Event.swift in Sources */, D2EBEB411BB9B6DE003A27DC /* PublishSubject.swift in Sources */, D2EBEB431BB9B6DE003A27DC /* SubjectType.swift in Sources */, diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index c3436fee..70f3c369 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -144,7 +144,6 @@ C89464B91BC6C2B00055219D /* ObservableConvertibleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464461BC6C2B00055219D /* ObservableConvertibleType.swift */; }; C89464BA1BC6C2B00055219D /* Amb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464491BC6C2B00055219D /* Amb.swift */; }; C89464BB1BC6C2B00055219D /* AnonymousObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894644A1BC6C2B00055219D /* AnonymousObservable.swift */; }; - C89464BC1BC6C2B00055219D /* AsObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894644B1BC6C2B00055219D /* AsObservable.swift */; }; C89464BD1BC6C2B00055219D /* Buffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894644C1BC6C2B00055219D /* Buffer.swift */; }; C89464BE1BC6C2B00055219D /* Catch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894644D1BC6C2B00055219D /* Catch.swift */; }; C89464BF1BC6C2B00055219D /* CombineLatest+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894644E1BC6C2B00055219D /* CombineLatest+arity.swift */; }; @@ -524,7 +523,6 @@ C89464461BC6C2B00055219D /* ObservableConvertibleType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableConvertibleType.swift; sourceTree = ""; }; C89464491BC6C2B00055219D /* Amb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Amb.swift; sourceTree = ""; }; C894644A1BC6C2B00055219D /* AnonymousObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AnonymousObservable.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - C894644B1BC6C2B00055219D /* AsObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsObservable.swift; sourceTree = ""; }; C894644C1BC6C2B00055219D /* Buffer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Buffer.swift; sourceTree = ""; }; C894644D1BC6C2B00055219D /* Catch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Catch.swift; sourceTree = ""; }; C894644E1BC6C2B00055219D /* CombineLatest+arity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CombineLatest+arity.swift"; sourceTree = ""; }; @@ -1091,7 +1089,6 @@ C84CC52D1BDC344100E06A64 /* ElementAt.swift */, C89464491BC6C2B00055219D /* Amb.swift */, C894644A1BC6C2B00055219D /* AnonymousObservable.swift */, - C894644B1BC6C2B00055219D /* AsObservable.swift */, C894644C1BC6C2B00055219D /* Buffer.swift */, C894644D1BC6C2B00055219D /* Catch.swift */, C89464511BC6C2B00055219D /* CombineLatest.swift */, @@ -1756,7 +1753,6 @@ C89465981BC6C2BC0055219D /* UISearchBar+Rx.swift in Sources */, C89464E21BC6C2B00055219D /* Take.swift in Sources */, C89465901BC6C2BC0055219D /* UIButton+Rx.swift in Sources */, - C89464BC1BC6C2B00055219D /* AsObservable.swift in Sources */, C89464DD1BC6C2B00055219D /* Sink.swift in Sources */, C89464FE1BC6C2B00055219D /* CurrentThreadScheduler.swift in Sources */, C89464BE1BC6C2B00055219D /* Catch.swift in Sources */, diff --git a/RxSwift/Observable+Extensions.swift b/RxSwift/Observable+Extensions.swift index 03045fe4..3d984b89 100644 --- a/RxSwift/Observable+Extensions.swift +++ b/RxSwift/Observable+Extensions.swift @@ -123,6 +123,6 @@ public extension ObservableType { */ @warn_unused_result(message="http://git.io/rxs.ud") func subscribeSafe(observer: O) -> Disposable { - return self.subscribe(observer) + return self.asObservable().subscribe(observer) } } diff --git a/RxSwift/Observables/Implementations/Amb.swift b/RxSwift/Observables/Implementations/Amb.swift index b234a0c7..9c19d568 100644 --- a/RxSwift/Observables/Implementations/Amb.swift +++ b/RxSwift/Observables/Implementations/Amb.swift @@ -98,8 +98,8 @@ class AmbSink : Sink { decide(o, e, .Right, subscription1) } - subscription1.disposable = _parent._left.subscribeSafe(sink1) - subscription2.disposable = _parent._right.subscribeSafe(sink2) + subscription1.disposable = _parent._left.subscribe(sink1) + subscription2.disposable = _parent._right.subscribe(sink2) return disposeAll } diff --git a/RxSwift/Observables/Implementations/Buffer.swift b/RxSwift/Observables/Implementations/Buffer.swift index d106222e..eb380461 100644 --- a/RxSwift/Observables/Implementations/Buffer.swift +++ b/RxSwift/Observables/Implementations/Buffer.swift @@ -49,7 +49,7 @@ class BufferTimeCountSink Disposable { createTimer(_windowID) - return StableCompositeDisposable.create(_timerD, _parent._source.subscribeSafe(self)) + return StableCompositeDisposable.create(_timerD, _parent._source.subscribe(self)) } func startNewWindowAndSendCurrentOne() { diff --git a/RxSwift/Observables/Implementations/Catch.swift b/RxSwift/Observables/Implementations/Catch.swift index 8f6b7876..3924948c 100644 --- a/RxSwift/Observables/Implementations/Catch.swift +++ b/RxSwift/Observables/Implementations/Catch.swift @@ -47,8 +47,8 @@ class CatchSink : Sink, ObserverType { func run() -> Disposable { let d1 = SingleAssignmentDisposable() _subscription.disposable = d1 - d1.disposable = _parent._source.subscribeSafe(self) - + d1.disposable = _parent._source.subscribe(self) + return _subscription } @@ -65,7 +65,7 @@ class CatchSink : Sink, ObserverType { let observer = CatchSinkProxy(parent: self) - _subscription.disposable = catchSequence.subscribeSafe(observer) + _subscription.disposable = catchSequence.subscribe(observer) } catch let e { observer?.on(.Error(e)) diff --git a/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift b/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift index d635b548..e61c4390 100644 --- a/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift +++ b/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift @@ -92,7 +92,7 @@ class CombineLatestCollectionTypeSink : CombineLatestSink { typealias R = O.E typealias Parent = CombineLatest2 - private let _parent: Parent + let _parent: Parent - private var _latestElement1: E1! = nil - private var _latestElement2: E2! = nil + var _latestElement1: E1! = nil + var _latestElement2: E2! = nil init(parent: Parent, observer: O, cancel: Disposable) { _parent = parent @@ -51,8 +51,8 @@ class CombineLatestSink2_ : CombineLatestSink { let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) - subscription1.disposable = _parent._source1.subscribeSafe(observer1) - subscription2.disposable = _parent._source2.subscribeSafe(observer2) + subscription1.disposable = _parent._source1.subscribe(observer1) + subscription2.disposable = _parent._source2.subscribe(observer2) return CompositeDisposable(disposables: [ subscription1, @@ -68,10 +68,10 @@ class CombineLatestSink2_ : CombineLatestSink { class CombineLatest2 : Producer { typealias ResultSelector = (E1, E2) throws -> R - private let _source1: Observable - private let _source2: Observable + let _source1: Observable + let _source2: Observable - private let _resultSelector: ResultSelector + let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, resultSelector: ResultSelector) { _source1 = source1 @@ -111,11 +111,11 @@ class CombineLatestSink3_ : CombineLatestSink { typealias R = O.E typealias Parent = CombineLatest3 - private let _parent: Parent + let _parent: Parent - private var _latestElement1: E1! = nil - private var _latestElement2: E2! = nil - private var _latestElement3: E3! = nil + var _latestElement1: E1! = nil + var _latestElement2: E2! = nil + var _latestElement3: E3! = nil init(parent: Parent, observer: O, cancel: Disposable) { _parent = parent @@ -131,9 +131,9 @@ class CombineLatestSink3_ : CombineLatestSink { let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) let observer3 = CombineLatestObserver(lock: lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) - subscription1.disposable = _parent._source1.subscribeSafe(observer1) - subscription2.disposable = _parent._source2.subscribeSafe(observer2) - subscription3.disposable = _parent._source3.subscribeSafe(observer3) + subscription1.disposable = _parent._source1.subscribe(observer1) + subscription2.disposable = _parent._source2.subscribe(observer2) + subscription3.disposable = _parent._source3.subscribe(observer3) return CompositeDisposable(disposables: [ subscription1, @@ -150,11 +150,11 @@ class CombineLatestSink3_ : CombineLatestSink { class CombineLatest3 : Producer { typealias ResultSelector = (E1, E2, E3) throws -> R - private let _source1: Observable - private let _source2: Observable - private let _source3: Observable + let _source1: Observable + let _source2: Observable + let _source3: Observable - private let _resultSelector: ResultSelector + let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, resultSelector: ResultSelector) { _source1 = source1 @@ -195,12 +195,12 @@ class CombineLatestSink4_ : CombineLatestSink - private let _parent: Parent + let _parent: Parent - private var _latestElement1: E1! = nil - private var _latestElement2: E2! = nil - private var _latestElement3: E3! = nil - private var _latestElement4: E4! = nil + var _latestElement1: E1! = nil + var _latestElement2: E2! = nil + var _latestElement3: E3! = nil + var _latestElement4: E4! = nil init(parent: Parent, observer: O, cancel: Disposable) { _parent = parent @@ -218,10 +218,10 @@ class CombineLatestSink4_ : CombineLatestSink Void in self._latestElement3 = e }, this: subscription3) let observer4 = CombineLatestObserver(lock: lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) - subscription1.disposable = _parent._source1.subscribeSafe(observer1) - subscription2.disposable = _parent._source2.subscribeSafe(observer2) - subscription3.disposable = _parent._source3.subscribeSafe(observer3) - subscription4.disposable = _parent._source4.subscribeSafe(observer4) + subscription1.disposable = _parent._source1.subscribe(observer1) + subscription2.disposable = _parent._source2.subscribe(observer2) + subscription3.disposable = _parent._source3.subscribe(observer3) + subscription4.disposable = _parent._source4.subscribe(observer4) return CompositeDisposable(disposables: [ subscription1, @@ -239,12 +239,12 @@ class CombineLatestSink4_ : CombineLatestSink : Producer { typealias ResultSelector = (E1, E2, E3, E4) throws -> R - private let _source1: Observable - private let _source2: Observable - private let _source3: Observable - private let _source4: Observable + let _source1: Observable + let _source2: Observable + let _source3: Observable + let _source4: Observable - private let _resultSelector: ResultSelector + let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, resultSelector: ResultSelector) { _source1 = source1 @@ -286,13 +286,13 @@ class CombineLatestSink5_ : CombineLatestSi typealias R = O.E typealias Parent = CombineLatest5 - private let _parent: Parent + let _parent: Parent - private var _latestElement1: E1! = nil - private var _latestElement2: E2! = nil - private var _latestElement3: E3! = nil - private var _latestElement4: E4! = nil - private var _latestElement5: E5! = nil + var _latestElement1: E1! = nil + var _latestElement2: E2! = nil + var _latestElement3: E3! = nil + var _latestElement4: E4! = nil + var _latestElement5: E5! = nil init(parent: Parent, observer: O, cancel: Disposable) { _parent = parent @@ -312,11 +312,11 @@ class CombineLatestSink5_ : CombineLatestSi let observer4 = CombineLatestObserver(lock: lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) let observer5 = CombineLatestObserver(lock: lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5) - subscription1.disposable = _parent._source1.subscribeSafe(observer1) - subscription2.disposable = _parent._source2.subscribeSafe(observer2) - subscription3.disposable = _parent._source3.subscribeSafe(observer3) - subscription4.disposable = _parent._source4.subscribeSafe(observer4) - subscription5.disposable = _parent._source5.subscribeSafe(observer5) + subscription1.disposable = _parent._source1.subscribe(observer1) + subscription2.disposable = _parent._source2.subscribe(observer2) + subscription3.disposable = _parent._source3.subscribe(observer3) + subscription4.disposable = _parent._source4.subscribe(observer4) + subscription5.disposable = _parent._source5.subscribe(observer5) return CompositeDisposable(disposables: [ subscription1, @@ -335,13 +335,13 @@ class CombineLatestSink5_ : CombineLatestSi class CombineLatest5 : Producer { typealias ResultSelector = (E1, E2, E3, E4, E5) throws -> R - private let _source1: Observable - private let _source2: Observable - private let _source3: Observable - private let _source4: Observable - private let _source5: Observable + let _source1: Observable + let _source2: Observable + let _source3: Observable + let _source4: Observable + let _source5: Observable - private let _resultSelector: ResultSelector + let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, resultSelector: ResultSelector) { _source1 = source1 @@ -384,14 +384,14 @@ class CombineLatestSink6_ : CombineLate typealias R = O.E typealias Parent = CombineLatest6 - private let _parent: Parent + let _parent: Parent - private var _latestElement1: E1! = nil - private var _latestElement2: E2! = nil - private var _latestElement3: E3! = nil - private var _latestElement4: E4! = nil - private var _latestElement5: E5! = nil - private var _latestElement6: E6! = nil + var _latestElement1: E1! = nil + var _latestElement2: E2! = nil + var _latestElement3: E3! = nil + var _latestElement4: E4! = nil + var _latestElement5: E5! = nil + var _latestElement6: E6! = nil init(parent: Parent, observer: O, cancel: Disposable) { _parent = parent @@ -413,12 +413,12 @@ class CombineLatestSink6_ : CombineLate let observer5 = CombineLatestObserver(lock: lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5) let observer6 = CombineLatestObserver(lock: lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6) - subscription1.disposable = _parent._source1.subscribeSafe(observer1) - subscription2.disposable = _parent._source2.subscribeSafe(observer2) - subscription3.disposable = _parent._source3.subscribeSafe(observer3) - subscription4.disposable = _parent._source4.subscribeSafe(observer4) - subscription5.disposable = _parent._source5.subscribeSafe(observer5) - subscription6.disposable = _parent._source6.subscribeSafe(observer6) + subscription1.disposable = _parent._source1.subscribe(observer1) + subscription2.disposable = _parent._source2.subscribe(observer2) + subscription3.disposable = _parent._source3.subscribe(observer3) + subscription4.disposable = _parent._source4.subscribe(observer4) + subscription5.disposable = _parent._source5.subscribe(observer5) + subscription6.disposable = _parent._source6.subscribe(observer6) return CompositeDisposable(disposables: [ subscription1, @@ -438,14 +438,14 @@ class CombineLatestSink6_ : CombineLate class CombineLatest6 : Producer { typealias ResultSelector = (E1, E2, E3, E4, E5, E6) throws -> R - private let _source1: Observable - private let _source2: Observable - private let _source3: Observable - private let _source4: Observable - private let _source5: Observable - private let _source6: Observable + let _source1: Observable + let _source2: Observable + let _source3: Observable + let _source4: Observable + let _source5: Observable + let _source6: Observable - private let _resultSelector: ResultSelector + let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, source6: Observable, resultSelector: ResultSelector) { _source1 = source1 @@ -489,15 +489,15 @@ class CombineLatestSink7_ : Combine typealias R = O.E typealias Parent = CombineLatest7 - private let _parent: Parent + let _parent: Parent - private var _latestElement1: E1! = nil - private var _latestElement2: E2! = nil - private var _latestElement3: E3! = nil - private var _latestElement4: E4! = nil - private var _latestElement5: E5! = nil - private var _latestElement6: E6! = nil - private var _latestElement7: E7! = nil + var _latestElement1: E1! = nil + var _latestElement2: E2! = nil + var _latestElement3: E3! = nil + var _latestElement4: E4! = nil + var _latestElement5: E5! = nil + var _latestElement6: E6! = nil + var _latestElement7: E7! = nil init(parent: Parent, observer: O, cancel: Disposable) { _parent = parent @@ -521,13 +521,13 @@ class CombineLatestSink7_ : Combine let observer6 = CombineLatestObserver(lock: lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6) let observer7 = CombineLatestObserver(lock: lock, parent: self, index: 6, setLatestValue: { (e: E7) -> Void in self._latestElement7 = e }, this: subscription7) - subscription1.disposable = _parent._source1.subscribeSafe(observer1) - subscription2.disposable = _parent._source2.subscribeSafe(observer2) - subscription3.disposable = _parent._source3.subscribeSafe(observer3) - subscription4.disposable = _parent._source4.subscribeSafe(observer4) - subscription5.disposable = _parent._source5.subscribeSafe(observer5) - subscription6.disposable = _parent._source6.subscribeSafe(observer6) - subscription7.disposable = _parent._source7.subscribeSafe(observer7) + subscription1.disposable = _parent._source1.subscribe(observer1) + subscription2.disposable = _parent._source2.subscribe(observer2) + subscription3.disposable = _parent._source3.subscribe(observer3) + subscription4.disposable = _parent._source4.subscribe(observer4) + subscription5.disposable = _parent._source5.subscribe(observer5) + subscription6.disposable = _parent._source6.subscribe(observer6) + subscription7.disposable = _parent._source7.subscribe(observer7) return CompositeDisposable(disposables: [ subscription1, @@ -548,15 +548,15 @@ class CombineLatestSink7_ : Combine class CombineLatest7 : Producer { typealias ResultSelector = (E1, E2, E3, E4, E5, E6, E7) throws -> R - private let _source1: Observable - private let _source2: Observable - private let _source3: Observable - private let _source4: Observable - private let _source5: Observable - private let _source6: Observable - private let _source7: Observable + let _source1: Observable + let _source2: Observable + let _source3: Observable + let _source4: Observable + let _source5: Observable + let _source6: Observable + let _source7: Observable - private let _resultSelector: ResultSelector + let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, source6: Observable, source7: Observable, resultSelector: ResultSelector) { _source1 = source1 @@ -601,16 +601,16 @@ class CombineLatestSink8_ : Com typealias R = O.E typealias Parent = CombineLatest8 - private let _parent: Parent + let _parent: Parent - private var _latestElement1: E1! = nil - private var _latestElement2: E2! = nil - private var _latestElement3: E3! = nil - private var _latestElement4: E4! = nil - private var _latestElement5: E5! = nil - private var _latestElement6: E6! = nil - private var _latestElement7: E7! = nil - private var _latestElement8: E8! = nil + var _latestElement1: E1! = nil + var _latestElement2: E2! = nil + var _latestElement3: E3! = nil + var _latestElement4: E4! = nil + var _latestElement5: E5! = nil + var _latestElement6: E6! = nil + var _latestElement7: E7! = nil + var _latestElement8: E8! = nil init(parent: Parent, observer: O, cancel: Disposable) { _parent = parent @@ -636,14 +636,14 @@ class CombineLatestSink8_ : Com let observer7 = CombineLatestObserver(lock: lock, parent: self, index: 6, setLatestValue: { (e: E7) -> Void in self._latestElement7 = e }, this: subscription7) let observer8 = CombineLatestObserver(lock: lock, parent: self, index: 7, setLatestValue: { (e: E8) -> Void in self._latestElement8 = e }, this: subscription8) - subscription1.disposable = _parent._source1.subscribeSafe(observer1) - subscription2.disposable = _parent._source2.subscribeSafe(observer2) - subscription3.disposable = _parent._source3.subscribeSafe(observer3) - subscription4.disposable = _parent._source4.subscribeSafe(observer4) - subscription5.disposable = _parent._source5.subscribeSafe(observer5) - subscription6.disposable = _parent._source6.subscribeSafe(observer6) - subscription7.disposable = _parent._source7.subscribeSafe(observer7) - subscription8.disposable = _parent._source8.subscribeSafe(observer8) + subscription1.disposable = _parent._source1.subscribe(observer1) + subscription2.disposable = _parent._source2.subscribe(observer2) + subscription3.disposable = _parent._source3.subscribe(observer3) + subscription4.disposable = _parent._source4.subscribe(observer4) + subscription5.disposable = _parent._source5.subscribe(observer5) + subscription6.disposable = _parent._source6.subscribe(observer6) + subscription7.disposable = _parent._source7.subscribe(observer7) + subscription8.disposable = _parent._source8.subscribe(observer8) return CompositeDisposable(disposables: [ subscription1, @@ -665,16 +665,16 @@ class CombineLatestSink8_ : Com class CombineLatest8 : Producer { typealias ResultSelector = (E1, E2, E3, E4, E5, E6, E7, E8) throws -> R - private let _source1: Observable - private let _source2: Observable - private let _source3: Observable - private let _source4: Observable - private let _source5: Observable - private let _source6: Observable - private let _source7: Observable - private let _source8: Observable + let _source1: Observable + let _source2: Observable + let _source3: Observable + let _source4: Observable + let _source5: Observable + let _source6: Observable + let _source7: Observable + let _source8: Observable - private let _resultSelector: ResultSelector + let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, source6: Observable, source7: Observable, source8: Observable, resultSelector: ResultSelector) { _source1 = source1 diff --git a/RxSwift/Observables/Implementations/CombineLatest+arity.tt b/RxSwift/Observables/Implementations/CombineLatest+arity.tt index 57ea23dd..927f7e0d 100644 --- a/RxSwift/Observables/Implementations/CombineLatest+arity.tt +++ b/RxSwift/Observables/Implementations/CombineLatest+arity.tt @@ -32,14 +32,14 @@ class CombineLatestSink<%= i %>_<<%= (Array(1...i).map { "E\($0)" }).joinWithSep typealias R = O.E typealias Parent = CombineLatest<%= i %><<%= (Array(1...i).map { "E\($0)" }).joinWithSeparator(", ") %>, R> - let parent: Parent + let _parent: Parent <%= (Array(1...i).map { -" var latestElement\($0): E\($0)! = nil" +" var _latestElement\($0): E\($0)! = nil" }).joinWithSeparator("\n") %> init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(arity: <%= i %>, observer: observer, cancel: cancel) } @@ -49,11 +49,11 @@ class CombineLatestSink<%= i %>_<<%= (Array(1...i).map { "E\($0)" }).joinWithSep }).joinWithSeparator("\n") %> <%= (Array(1...i).map { -" let observer\($0) = CombineLatestObserver(lock: lock, parent: self, index: \($0 - 1), setLatestValue: { (e: E\($0)) -> Void in self.latestElement\($0) = e }, this: subscription\($0))" +" let observer\($0) = CombineLatestObserver(lock: lock, parent: self, index: \($0 - 1), setLatestValue: { (e: E\($0)) -> Void in self._latestElement\($0) = e }, this: subscription\($0))" }).joinWithSeparator("\n") %> <%= (Array(1...i).map { -" subscription\($0).disposable = parent.source\($0).subscribeSafe(observer\($0))" +" subscription\($0).disposable = _parent._source\($0).subscribe(observer\($0))" }).joinWithSeparator("\n") %> return CompositeDisposable(disposables: [ @@ -62,7 +62,7 @@ class CombineLatestSink<%= i %>_<<%= (Array(1...i).map { "E\($0)" }).joinWithSep } override func getResult() throws -> R { - return try self.parent.resultSelector(<%= (Array(1...i).map { "latestElement\($0)" }).joinWithSeparator(", ") %>) + return try _parent._resultSelector(<%= (Array(1...i).map { "_latestElement\($0)" }).joinWithSeparator(", ") %>) } } @@ -70,17 +70,17 @@ class CombineLatest<%= i %><<%= (Array(1...i).map { "E\($0)" }).joinWithSeparato typealias ResultSelector = (<%= (Array(1...i).map { "E\($0)" }).joinWithSeparator(", ") %>) throws -> R <%= (Array(1...i).map { -" let source\($0): Observable" +" let _source\($0): Observable" }).joinWithSeparator("\n") %> - let resultSelector: ResultSelector + let _resultSelector: ResultSelector init(<%= (Array(1...i).map { "source\($0): Observable" }).joinWithSeparator(", ") %>, resultSelector: ResultSelector) { <%= (Array(1...i).map { -" self.source\($0) = source\($0)" +" _source\($0) = source\($0)" }).joinWithSeparator("\n") %> - self.resultSelector = resultSelector + _resultSelector = resultSelector } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { diff --git a/RxSwift/Observables/Implementations/ConnectableObservable.swift b/RxSwift/Observables/Implementations/ConnectableObservable.swift index a9715a6c..49d22e1d 100644 --- a/RxSwift/Observables/Implementations/ConnectableObservable.swift +++ b/RxSwift/Observables/Implementations/ConnectableObservable.swift @@ -50,7 +50,7 @@ public class ConnectableObservable : Observable, Connectabl private var _connection: ConnectionType? public init(source: Observable, subject: S) { - _source = AsObservable(source: source) + _source = source _subject = subject _connection = nil } @@ -61,7 +61,7 @@ public class ConnectableObservable : Observable, Connectabl return connection } - let disposable = _source.subscribeSafe(_subject.asObserver()) + let disposable = _source.subscribe(_subject.asObserver()) let connection = Connection(parent: self, subscription: disposable) _connection = connection return connection @@ -69,6 +69,6 @@ public class ConnectableObservable : Observable, Connectabl } public override func subscribe(observer: O) -> Disposable { - return _subject.subscribeSafe(observer) + return _subject.subscribe(observer) } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Debug.swift b/RxSwift/Observables/Implementations/Debug.swift index e0edaf70..f527c2f5 100644 --- a/RxSwift/Observables/Implementations/Debug.swift +++ b/RxSwift/Observables/Implementations/Debug.swift @@ -49,6 +49,6 @@ class Debug : Producer { print("[\(_identifier)] subscribed") let sink = Debug_(parent: self, observer: observer, cancel: cancel) setSink(sink) - return _source.subscribeSafe(sink) + return _source.subscribe(sink) } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Deferred.swift b/RxSwift/Observables/Implementations/Deferred.swift index b52931f6..affbd3d2 100644 --- a/RxSwift/Observables/Implementations/Deferred.swift +++ b/RxSwift/Observables/Implementations/Deferred.swift @@ -22,7 +22,7 @@ class DeferredSink : Sink, ObserverType { func run() -> Disposable { do { let result = try _parent.eval() - return result.subscribeSafe(self) + return result.subscribe(self) } catch let e { observer?.on(.Error(e)) diff --git a/RxSwift/Observables/Implementations/DelaySubscription.swift b/RxSwift/Observables/Implementations/DelaySubscription.swift index 186db7a8..0669bc10 100644 --- a/RxSwift/Observables/Implementations/DelaySubscription.swift +++ b/RxSwift/Observables/Implementations/DelaySubscription.swift @@ -45,7 +45,7 @@ class DelaySubscription: Producer { let sink = DelaySubscriptionSink(parent: self, observer: observer, cancel: cancel) setSink(sink) return _scheduler.scheduleRelative((), dueTime: _dueTime) { _ in - return self._source.subscribeSafe(sink) + return self._source.subscribe(sink) } } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/DistinctUntilChanged.swift b/RxSwift/Observables/Implementations/DistinctUntilChanged.swift index a34bb195..56521765 100644 --- a/RxSwift/Observables/Implementations/DistinctUntilChanged.swift +++ b/RxSwift/Observables/Implementations/DistinctUntilChanged.swift @@ -67,6 +67,6 @@ class DistinctUntilChanged: Producer { override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { let sink = DistinctUntilChangedSink(parent: self, observer: observer, cancel: cancel) setSink(sink) - return _source.subscribeSafe(sink) + return _source.subscribe(sink) } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Do.swift b/RxSwift/Observables/Implementations/Do.swift index 1c02425f..1af070c6 100644 --- a/RxSwift/Observables/Implementations/Do.swift +++ b/RxSwift/Observables/Implementations/Do.swift @@ -50,6 +50,6 @@ class Do : Producer { setSink(sink) - return _source.subscribeSafe(sink) + return _source.subscribe(sink) } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Filter.swift b/RxSwift/Observables/Implementations/Filter.swift index a9ed5713..ad47c6fb 100644 --- a/RxSwift/Observables/Implementations/Filter.swift +++ b/RxSwift/Observables/Implementations/Filter.swift @@ -54,6 +54,6 @@ class Filter : Producer { override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { let sink = FilterSink(parent: self, observer: observer, cancel: cancel) setSink(sink) - return _source.subscribeSafe(sink) + return _source.subscribe(sink) } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/FlatMap.swift b/RxSwift/Observables/Implementations/FlatMap.swift index a7efbeb3..c623ad44 100644 --- a/RxSwift/Observables/Implementations/FlatMap.swift +++ b/RxSwift/Observables/Implementations/FlatMap.swift @@ -118,7 +118,7 @@ class FlatMapSink Disposable { _group.addDisposable(_sourceSubscription) - let subscription = _parent._source.subscribeSafe(self) + let subscription = _parent._source.subscribe(self) _sourceSubscription.disposable = subscription return _group diff --git a/RxSwift/Observables/Implementations/Map.swift b/RxSwift/Observables/Implementations/Map.swift index 5a0d8b1e..5781ed60 100644 --- a/RxSwift/Observables/Implementations/Map.swift +++ b/RxSwift/Observables/Implementations/Map.swift @@ -97,12 +97,12 @@ class Map: Producer { if let _ = _selector1 { let sink = MapSink1(parent: self, observer: observer, cancel: cancel) setSink(sink) - return _source.subscribeSafe(sink) + return _source.subscribe(sink) } else { let sink = MapSink2(parent: self, observer: observer, cancel: cancel) setSink(sink) - return _source.subscribeSafe(sink) + return _source.subscribe(sink) } } diff --git a/RxSwift/Observables/Implementations/Merge.swift b/RxSwift/Observables/Implementations/Merge.swift index b1c030a4..65761898 100644 --- a/RxSwift/Observables/Implementations/Merge.swift +++ b/RxSwift/Observables/Implementations/Merge.swift @@ -66,9 +66,9 @@ class MergeSink func run() -> Disposable { _group.addDisposable(_sourceSubscription) - let disposable = _parent._sources.subscribeSafe(self) + let disposable = _parent._sources.subscribe(self) _sourceSubscription.disposable = disposable - + return _group } @@ -80,7 +80,7 @@ class MergeSink if let key = maybeKey { let observer = MergeSinkIter(parent: self, disposeKey: key) - let disposable = value.asObservable().subscribeSafe(observer) + let disposable = value.asObservable().subscribe(observer) innerSubscription.disposable = disposable } case .Error(let error): @@ -174,7 +174,7 @@ class MergeConcurrentSink Disposable { _group.addDisposable(_sourceSubscription) - let disposable = _parent._sources.subscribeSafe(self) + let disposable = _parent._sources.subscribe(self) _sourceSubscription.disposable = disposable return _group } @@ -187,7 +187,7 @@ class MergeConcurrentSink: Sink, ObserverType { let observable = try _parent._selector(connectable) - let subscription = observable.subscribeSafe(self) + let subscription = observable.subscribe(self) let connection = connectable.connect() return BinaryDisposable(subscription, connection) diff --git a/RxSwift/Observables/Implementations/ObserveOn.swift b/RxSwift/Observables/Implementations/ObserveOn.swift index 563030d4..ec2d12f1 100644 --- a/RxSwift/Observables/Implementations/ObserveOn.swift +++ b/RxSwift/Observables/Implementations/ObserveOn.swift @@ -24,7 +24,7 @@ class ObserveOn : Producer { override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { let sink = ObserveOnSink(scheduler: scheduler, observer: observer, cancel: cancel) setSink(sink) - return source.subscribeSafe(sink) + return source.subscribe(sink) } #if TRACE_RESOURCES diff --git a/RxSwift/Observables/Implementations/ObserveOnSerialDispatchQueue.swift b/RxSwift/Observables/Implementations/ObserveOnSerialDispatchQueue.swift index a4c230e4..3fa66efd 100644 --- a/RxSwift/Observables/Implementations/ObserveOnSerialDispatchQueue.swift +++ b/RxSwift/Observables/Implementations/ObserveOnSerialDispatchQueue.swift @@ -75,7 +75,7 @@ class ObserveOnSerialDispatchQueue : Producer { override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { let sink = ObserveOnSerialDispatchQueueSink(scheduler: scheduler, observer: observer, cancel: cancel) setSink(sink) - return source.subscribeSafe(sink) + return source.subscribe(sink) } #if TRACE_RESOURCES diff --git a/RxSwift/Observables/Implementations/Reduce.swift b/RxSwift/Observables/Implementations/Reduce.swift index 552fb3ae..e8653d85 100644 --- a/RxSwift/Observables/Implementations/Reduce.swift +++ b/RxSwift/Observables/Implementations/Reduce.swift @@ -69,6 +69,6 @@ class Reduce : Producer { override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { let sink = ReduceSink(parent: self, observer: observer, cancel: cancel) setSink(sink) - return _source.subscribeSafe(sink) + return _source.subscribe(sink) } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Sample.swift b/RxSwift/Observables/Implementations/Sample.swift index 47ecaa92..459a3484 100644 --- a/RxSwift/Observables/Implementations/Sample.swift +++ b/RxSwift/Observables/Implementations/Sample.swift @@ -72,8 +72,8 @@ class SampleSequenceSink : Sink, ObserverType { } func run() -> Disposable { - _sourceSubscription.disposable = _parent._source.subscribeSafe(self) - let samplerSubscription = _parent._sampler.subscribeSafe(SamplerSink(parent: self)) + _sourceSubscription.disposable = _parent._source.subscribe(self) + let samplerSubscription = _parent._sampler.subscribe(SamplerSink(parent: self)) return CompositeDisposable(_sourceSubscription, samplerSubscription) } diff --git a/RxSwift/Observables/Implementations/Scan.swift b/RxSwift/Observables/Implementations/Scan.swift index 49e06cd5..58a7d720 100644 --- a/RxSwift/Observables/Implementations/Scan.swift +++ b/RxSwift/Observables/Implementations/Scan.swift @@ -59,6 +59,6 @@ class Scan: Producer { override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { let sink = ScanSink(parent: self, observer: observer, cancel: cancel) setSink(sink) - return _source.subscribeSafe(sink) + return _source.subscribe(sink) } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Skip.swift b/RxSwift/Observables/Implementations/Skip.swift index 9c69b9e1..65df2212 100644 --- a/RxSwift/Observables/Implementations/Skip.swift +++ b/RxSwift/Observables/Implementations/Skip.swift @@ -57,7 +57,7 @@ class SkipCount: Producer { override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { let sink = SkipCountSink(parent: self, observer: observer, cancel: cancel) setSink(sink) - return source.subscribeSafe(sink) + return source.subscribe(sink) } } @@ -102,7 +102,7 @@ class SkipTimeSink Disposable { - let sourceSubscription = _parent._source.subscribeSafe(self) + let sourceSubscription = _parent._source.subscribe(self) let otherObserver = SkipUntilSinkOther(parent: self) - let otherSubscription = _parent._other.subscribeSafe(otherObserver) + let otherSubscription = _parent._other.subscribe(otherObserver) disposable = sourceSubscription otherObserver.disposable = otherSubscription diff --git a/RxSwift/Observables/Implementations/SkipWhile.swift b/RxSwift/Observables/Implementations/SkipWhile.swift index 36942bec..8261f0eb 100644 --- a/RxSwift/Observables/Implementations/SkipWhile.swift +++ b/RxSwift/Observables/Implementations/SkipWhile.swift @@ -104,12 +104,12 @@ class SkipWhile: Producer { if let _ = _predicate { let sink = SkipWhileSink(parent: self, observer: observer, cancel: cancel) setSink(sink) - return _source.subscribeSafe(sink) + return _source.subscribe(sink) } else { let sink = SkipWhileSinkWithIndex(parent: self, observer: observer, cancel: cancel) setSink(sink) - return _source.subscribeSafe(sink) + return _source.subscribe(sink) } } } diff --git a/RxSwift/Observables/Implementations/StartWith.swift b/RxSwift/Observables/Implementations/StartWith.swift index a0ccfd03..450c1189 100644 --- a/RxSwift/Observables/Implementations/StartWith.swift +++ b/RxSwift/Observables/Implementations/StartWith.swift @@ -23,6 +23,6 @@ class StartWith: Producer { observer.on(.Next(e)) } - return source.subscribeSafe(observer) + return source.subscribe(observer) } } diff --git a/RxSwift/Observables/Implementations/Switch.swift b/RxSwift/Observables/Implementations/Switch.swift index 10fa9071..1f48d33c 100644 --- a/RxSwift/Observables/Implementations/Switch.swift +++ b/RxSwift/Observables/Implementations/Switch.swift @@ -30,7 +30,7 @@ class SwitchSink } func run() -> Disposable { - let subscription = _parent._sources.subscribeSafe(self) + let subscription = _parent._sources.subscribe(self) _subscriptions.disposable = subscription return CompositeDisposable(_subscriptions, _innerSubscription) } @@ -48,7 +48,7 @@ class SwitchSink _innerSubscription.disposable = d let observer = SwitchSinkIter(parent: self, id: latest, _self: d) - let disposable = observable.asObservable().subscribeSafe(observer) + let disposable = observable.asObservable().subscribe(observer) d.disposable = disposable case .Error(let error): _lock.performLocked { diff --git a/RxSwift/Observables/Implementations/Take.swift b/RxSwift/Observables/Implementations/Take.swift index ca9054d1..1f4dc72e 100644 --- a/RxSwift/Observables/Implementations/Take.swift +++ b/RxSwift/Observables/Implementations/Take.swift @@ -61,7 +61,7 @@ class TakeCount: Producer { override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { let sink = TakeCountSink(parent: self, observer: observer, cancel: cancel) setSink(sink) - return _source.subscribeSafe(sink) + return _source.subscribe(sink) } } @@ -108,7 +108,7 @@ class TakeTimeSink Disposable { let otherObserver = TakeUntilSinkOther(parent: self) - let otherSubscription = _parent._other.subscribeSafe(otherObserver) + let otherSubscription = _parent._other.subscribe(otherObserver) otherObserver.disposable = otherSubscription - let sourceSubscription = _parent._source.subscribeSafe(self) + let sourceSubscription = _parent._source.subscribe(self) return CompositeDisposable(sourceSubscription, otherSubscription) } diff --git a/RxSwift/Observables/Implementations/TakeWhile.swift b/RxSwift/Observables/Implementations/TakeWhile.swift index 20d4b1e1..22c5f99a 100644 --- a/RxSwift/Observables/Implementations/TakeWhile.swift +++ b/RxSwift/Observables/Implementations/TakeWhile.swift @@ -118,11 +118,11 @@ class TakeWhile: Producer { if let _ = _predicate { let sink = TakeWhileSink(parent: self, observer: observer, cancel: cancel) setSink(sink) - return _source.subscribeSafe(sink) + return _source.subscribe(sink) } else { let sink = TakeWhileSinkWithIndex(parent: self, observer: observer, cancel: cancel) setSink(sink) - return _source.subscribeSafe(sink) + return _source.subscribe(sink) } } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Throttle.swift b/RxSwift/Observables/Implementations/Throttle.swift index 4d881c3e..5ca0642f 100644 --- a/RxSwift/Observables/Implementations/Throttle.swift +++ b/RxSwift/Observables/Implementations/Throttle.swift @@ -29,7 +29,7 @@ class ThrottleSink : Sink, Observe } func run() -> Disposable { - let subscription = _parent._source.subscribeSafe(self) + let subscription = _parent._source.subscribe(self) return CompositeDisposable(subscription, cancellable) } diff --git a/RxSwift/Observables/Implementations/ToArray.swift b/RxSwift/Observables/Implementations/ToArray.swift index 35a629ca..c52cf710 100644 --- a/RxSwift/Observables/Implementations/ToArray.swift +++ b/RxSwift/Observables/Implementations/ToArray.swift @@ -45,6 +45,6 @@ class ToArray : Producer<[SourceType]> { override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { let sink = ToArraySink(parent: self, observer: observer, cancel: cancel) setSink(sink) - return _source.subscribeSafe(sink) + return _source.subscribe(sink) } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Using.swift b/RxSwift/Observables/Implementations/Using.swift index 92f8f87b..6c852634 100644 --- a/RxSwift/Observables/Implementations/Using.swift +++ b/RxSwift/Observables/Implementations/Using.swift @@ -29,12 +29,12 @@ class UsingSink +public func zip (source1: O1, _ source2: O2, resultSelector: (O1.E, O2.E) throws -> R) -> Observable { return Zip2( @@ -34,20 +34,20 @@ class ZipSink2_ : ZipSink { typealias R = O.E typealias Parent = Zip2 - let parent: Parent + let _parent: Parent - var values1: Queue = Queue(capacity: 2) - var values2: Queue = Queue(capacity: 2) + var _values1: Queue = Queue(capacity: 2) + var _values2: Queue = Queue(capacity: 2) init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(arity: 2, observer: observer, cancel: cancel) } override func hasElements(index: Int) -> Bool { switch (index) { - case 0: return values1.count > 0 - case 1: return values2.count > 0 + case 0: return _values1.count > 0 + case 1: return _values2.count > 0 default: rxFatalError("Unhandled case (Function)") @@ -60,11 +60,11 @@ class ZipSink2_ : ZipSink { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() - let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self.values1.enqueue($0) }, this: subscription1) - let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self.values2.enqueue($0) }, this: subscription2) + let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) + let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) - subscription1.disposable = parent.source1.subscribeSafe(observer1) - subscription2.disposable = parent.source2.subscribeSafe(observer2) + subscription1.disposable = _parent.source1.subscribe(observer1) + subscription2.disposable = _parent.source2.subscribe(observer2) return CompositeDisposable(disposables: [ subscription1, @@ -73,7 +73,7 @@ class ZipSink2_ : ZipSink { } override func getResult() throws -> R { - return try self.parent.resultSelector(values1.dequeue(), values2.dequeue()) + return try _parent._resultSelector(_values1.dequeue(), _values2.dequeue()) } } @@ -83,13 +83,13 @@ class Zip2 : Producer { let source1: Observable let source2: Observable - let resultSelector: ResultSelector + let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, resultSelector: ResultSelector) { self.source1 = source1 self.source2 = source2 - self.resultSelector = resultSelector + _resultSelector = resultSelector } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { @@ -110,7 +110,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ @warn_unused_result(message="http://git.io/rxs.uo") -public func zip +public func zip (source1: O1, _ source2: O2, _ source3: O3, resultSelector: (O1.E, O2.E, O3.E) throws -> R) -> Observable { return Zip3( @@ -123,22 +123,22 @@ class ZipSink3_ : ZipSink { typealias R = O.E typealias Parent = Zip3 - let parent: Parent + let _parent: Parent - var values1: Queue = Queue(capacity: 2) - var values2: Queue = Queue(capacity: 2) - var values3: Queue = Queue(capacity: 2) + var _values1: Queue = Queue(capacity: 2) + var _values2: Queue = Queue(capacity: 2) + var _values3: Queue = Queue(capacity: 2) init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(arity: 3, observer: observer, cancel: cancel) } override func hasElements(index: Int) -> Bool { switch (index) { - case 0: return values1.count > 0 - case 1: return values2.count > 0 - case 2: return values3.count > 0 + case 0: return _values1.count > 0 + case 1: return _values2.count > 0 + case 2: return _values3.count > 0 default: rxFatalError("Unhandled case (Function)") @@ -152,13 +152,13 @@ class ZipSink3_ : ZipSink { let subscription2 = SingleAssignmentDisposable() let subscription3 = SingleAssignmentDisposable() - let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self.values1.enqueue($0) }, this: subscription1) - let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self.values2.enqueue($0) }, this: subscription2) - let observer3 = ZipObserver(lock: lock, parent: self, index: 2, setNextValue: { self.values3.enqueue($0) }, this: subscription3) + let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) + let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) + let observer3 = ZipObserver(lock: lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) - subscription1.disposable = parent.source1.subscribeSafe(observer1) - subscription2.disposable = parent.source2.subscribeSafe(observer2) - subscription3.disposable = parent.source3.subscribeSafe(observer3) + subscription1.disposable = _parent.source1.subscribe(observer1) + subscription2.disposable = _parent.source2.subscribe(observer2) + subscription3.disposable = _parent.source3.subscribe(observer3) return CompositeDisposable(disposables: [ subscription1, @@ -168,7 +168,7 @@ class ZipSink3_ : ZipSink { } override func getResult() throws -> R { - return try self.parent.resultSelector(values1.dequeue(), values2.dequeue(), values3.dequeue()) + return try _parent._resultSelector(_values1.dequeue(), _values2.dequeue(), _values3.dequeue()) } } @@ -179,14 +179,14 @@ class Zip3 : Producer { let source2: Observable let source3: Observable - let resultSelector: ResultSelector + let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, resultSelector: ResultSelector) { self.source1 = source1 self.source2 = source2 self.source3 = source3 - self.resultSelector = resultSelector + _resultSelector = resultSelector } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { @@ -207,7 +207,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ @warn_unused_result(message="http://git.io/rxs.uo") -public func zip +public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: (O1.E, O2.E, O3.E, O4.E) throws -> R) -> Observable { return Zip4( @@ -220,24 +220,24 @@ class ZipSink4_ : ZipSink { typealias R = O.E typealias Parent = Zip4 - let parent: Parent + let _parent: Parent - var values1: Queue = Queue(capacity: 2) - var values2: Queue = Queue(capacity: 2) - var values3: Queue = Queue(capacity: 2) - var values4: Queue = Queue(capacity: 2) + var _values1: Queue = Queue(capacity: 2) + var _values2: Queue = Queue(capacity: 2) + var _values3: Queue = Queue(capacity: 2) + var _values4: Queue = Queue(capacity: 2) init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(arity: 4, observer: observer, cancel: cancel) } override func hasElements(index: Int) -> Bool { switch (index) { - case 0: return values1.count > 0 - case 1: return values2.count > 0 - case 2: return values3.count > 0 - case 3: return values4.count > 0 + case 0: return _values1.count > 0 + case 1: return _values2.count > 0 + case 2: return _values3.count > 0 + case 3: return _values4.count > 0 default: rxFatalError("Unhandled case (Function)") @@ -252,15 +252,15 @@ class ZipSink4_ : ZipSink { let subscription3 = SingleAssignmentDisposable() let subscription4 = SingleAssignmentDisposable() - let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self.values1.enqueue($0) }, this: subscription1) - let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self.values2.enqueue($0) }, this: subscription2) - let observer3 = ZipObserver(lock: lock, parent: self, index: 2, setNextValue: { self.values3.enqueue($0) }, this: subscription3) - let observer4 = ZipObserver(lock: lock, parent: self, index: 3, setNextValue: { self.values4.enqueue($0) }, this: subscription4) + let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) + let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) + let observer3 = ZipObserver(lock: lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) + let observer4 = ZipObserver(lock: lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) - subscription1.disposable = parent.source1.subscribeSafe(observer1) - subscription2.disposable = parent.source2.subscribeSafe(observer2) - subscription3.disposable = parent.source3.subscribeSafe(observer3) - subscription4.disposable = parent.source4.subscribeSafe(observer4) + subscription1.disposable = _parent.source1.subscribe(observer1) + subscription2.disposable = _parent.source2.subscribe(observer2) + subscription3.disposable = _parent.source3.subscribe(observer3) + subscription4.disposable = _parent.source4.subscribe(observer4) return CompositeDisposable(disposables: [ subscription1, @@ -271,7 +271,7 @@ class ZipSink4_ : ZipSink { } override func getResult() throws -> R { - return try self.parent.resultSelector(values1.dequeue(), values2.dequeue(), values3.dequeue(), values4.dequeue()) + return try _parent._resultSelector(_values1.dequeue(), _values2.dequeue(), _values3.dequeue(), _values4.dequeue()) } } @@ -283,7 +283,7 @@ class Zip4 : Producer { let source3: Observable let source4: Observable - let resultSelector: ResultSelector + let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, resultSelector: ResultSelector) { self.source1 = source1 @@ -291,7 +291,7 @@ class Zip4 : Producer { self.source3 = source3 self.source4 = source4 - self.resultSelector = resultSelector + _resultSelector = resultSelector } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { @@ -312,7 +312,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ @warn_unused_result(message="http://git.io/rxs.uo") -public func zip +public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> R) -> Observable { return Zip5( @@ -325,26 +325,26 @@ class ZipSink5_ : ZipSink { typealias R = O.E typealias Parent = Zip5 - let parent: Parent + let _parent: Parent - var values1: Queue = Queue(capacity: 2) - var values2: Queue = Queue(capacity: 2) - var values3: Queue = Queue(capacity: 2) - var values4: Queue = Queue(capacity: 2) - var values5: Queue = Queue(capacity: 2) + var _values1: Queue = Queue(capacity: 2) + var _values2: Queue = Queue(capacity: 2) + var _values3: Queue = Queue(capacity: 2) + var _values4: Queue = Queue(capacity: 2) + var _values5: Queue = Queue(capacity: 2) init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(arity: 5, observer: observer, cancel: cancel) } override func hasElements(index: Int) -> Bool { switch (index) { - case 0: return values1.count > 0 - case 1: return values2.count > 0 - case 2: return values3.count > 0 - case 3: return values4.count > 0 - case 4: return values5.count > 0 + case 0: return _values1.count > 0 + case 1: return _values2.count > 0 + case 2: return _values3.count > 0 + case 3: return _values4.count > 0 + case 4: return _values5.count > 0 default: rxFatalError("Unhandled case (Function)") @@ -360,17 +360,17 @@ class ZipSink5_ : ZipSink { let subscription4 = SingleAssignmentDisposable() let subscription5 = SingleAssignmentDisposable() - let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self.values1.enqueue($0) }, this: subscription1) - let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self.values2.enqueue($0) }, this: subscription2) - let observer3 = ZipObserver(lock: lock, parent: self, index: 2, setNextValue: { self.values3.enqueue($0) }, this: subscription3) - let observer4 = ZipObserver(lock: lock, parent: self, index: 3, setNextValue: { self.values4.enqueue($0) }, this: subscription4) - let observer5 = ZipObserver(lock: lock, parent: self, index: 4, setNextValue: { self.values5.enqueue($0) }, this: subscription5) + let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) + let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) + let observer3 = ZipObserver(lock: lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) + let observer4 = ZipObserver(lock: lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) + let observer5 = ZipObserver(lock: lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5) - subscription1.disposable = parent.source1.subscribeSafe(observer1) - subscription2.disposable = parent.source2.subscribeSafe(observer2) - subscription3.disposable = parent.source3.subscribeSafe(observer3) - subscription4.disposable = parent.source4.subscribeSafe(observer4) - subscription5.disposable = parent.source5.subscribeSafe(observer5) + subscription1.disposable = _parent.source1.subscribe(observer1) + subscription2.disposable = _parent.source2.subscribe(observer2) + subscription3.disposable = _parent.source3.subscribe(observer3) + subscription4.disposable = _parent.source4.subscribe(observer4) + subscription5.disposable = _parent.source5.subscribe(observer5) return CompositeDisposable(disposables: [ subscription1, @@ -382,7 +382,7 @@ class ZipSink5_ : ZipSink { } override func getResult() throws -> R { - return try self.parent.resultSelector(values1.dequeue(), values2.dequeue(), values3.dequeue(), values4.dequeue(), values5.dequeue()) + return try _parent._resultSelector(_values1.dequeue(), _values2.dequeue(), _values3.dequeue(), _values4.dequeue(), _values5.dequeue()) } } @@ -395,7 +395,7 @@ class Zip5 : Producer { let source4: Observable let source5: Observable - let resultSelector: ResultSelector + let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, resultSelector: ResultSelector) { self.source1 = source1 @@ -404,7 +404,7 @@ class Zip5 : Producer { self.source4 = source4 self.source5 = source5 - self.resultSelector = resultSelector + _resultSelector = resultSelector } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { @@ -425,7 +425,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ @warn_unused_result(message="http://git.io/rxs.uo") -public func zip +public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> R) -> Observable { return Zip6( @@ -438,28 +438,28 @@ class ZipSink6_ : ZipSink { typealias R = O.E typealias Parent = Zip6 - let parent: Parent + let _parent: Parent - var values1: Queue = Queue(capacity: 2) - var values2: Queue = Queue(capacity: 2) - var values3: Queue = Queue(capacity: 2) - var values4: Queue = Queue(capacity: 2) - var values5: Queue = Queue(capacity: 2) - var values6: Queue = Queue(capacity: 2) + var _values1: Queue = Queue(capacity: 2) + var _values2: Queue = Queue(capacity: 2) + var _values3: Queue = Queue(capacity: 2) + var _values4: Queue = Queue(capacity: 2) + var _values5: Queue = Queue(capacity: 2) + var _values6: Queue = Queue(capacity: 2) init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(arity: 6, observer: observer, cancel: cancel) } override func hasElements(index: Int) -> Bool { switch (index) { - case 0: return values1.count > 0 - case 1: return values2.count > 0 - case 2: return values3.count > 0 - case 3: return values4.count > 0 - case 4: return values5.count > 0 - case 5: return values6.count > 0 + case 0: return _values1.count > 0 + case 1: return _values2.count > 0 + case 2: return _values3.count > 0 + case 3: return _values4.count > 0 + case 4: return _values5.count > 0 + case 5: return _values6.count > 0 default: rxFatalError("Unhandled case (Function)") @@ -476,19 +476,19 @@ class ZipSink6_ : ZipSink { let subscription5 = SingleAssignmentDisposable() let subscription6 = SingleAssignmentDisposable() - let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self.values1.enqueue($0) }, this: subscription1) - let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self.values2.enqueue($0) }, this: subscription2) - let observer3 = ZipObserver(lock: lock, parent: self, index: 2, setNextValue: { self.values3.enqueue($0) }, this: subscription3) - let observer4 = ZipObserver(lock: lock, parent: self, index: 3, setNextValue: { self.values4.enqueue($0) }, this: subscription4) - let observer5 = ZipObserver(lock: lock, parent: self, index: 4, setNextValue: { self.values5.enqueue($0) }, this: subscription5) - let observer6 = ZipObserver(lock: lock, parent: self, index: 5, setNextValue: { self.values6.enqueue($0) }, this: subscription6) + let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) + let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) + let observer3 = ZipObserver(lock: lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) + let observer4 = ZipObserver(lock: lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) + let observer5 = ZipObserver(lock: lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5) + let observer6 = ZipObserver(lock: lock, parent: self, index: 5, setNextValue: { self._values6.enqueue($0) }, this: subscription6) - subscription1.disposable = parent.source1.subscribeSafe(observer1) - subscription2.disposable = parent.source2.subscribeSafe(observer2) - subscription3.disposable = parent.source3.subscribeSafe(observer3) - subscription4.disposable = parent.source4.subscribeSafe(observer4) - subscription5.disposable = parent.source5.subscribeSafe(observer5) - subscription6.disposable = parent.source6.subscribeSafe(observer6) + subscription1.disposable = _parent.source1.subscribe(observer1) + subscription2.disposable = _parent.source2.subscribe(observer2) + subscription3.disposable = _parent.source3.subscribe(observer3) + subscription4.disposable = _parent.source4.subscribe(observer4) + subscription5.disposable = _parent.source5.subscribe(observer5) + subscription6.disposable = _parent.source6.subscribe(observer6) return CompositeDisposable(disposables: [ subscription1, @@ -501,7 +501,7 @@ class ZipSink6_ : ZipSink { } override func getResult() throws -> R { - return try self.parent.resultSelector(values1.dequeue(), values2.dequeue(), values3.dequeue(), values4.dequeue(), values5.dequeue(), values6.dequeue()) + return try _parent._resultSelector(_values1.dequeue(), _values2.dequeue(), _values3.dequeue(), _values4.dequeue(), _values5.dequeue(), _values6.dequeue()) } } @@ -515,7 +515,7 @@ class Zip6 : Producer { let source5: Observable let source6: Observable - let resultSelector: ResultSelector + let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, source6: Observable, resultSelector: ResultSelector) { self.source1 = source1 @@ -525,7 +525,7 @@ class Zip6 : Producer { self.source5 = source5 self.source6 = source6 - self.resultSelector = resultSelector + _resultSelector = resultSelector } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { @@ -546,7 +546,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ @warn_unused_result(message="http://git.io/rxs.uo") -public func zip +public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> R) -> Observable { return Zip7( @@ -559,30 +559,30 @@ class ZipSink7_ : ZipSink { typealias R = O.E typealias Parent = Zip7 - let parent: Parent + let _parent: Parent - var values1: Queue = Queue(capacity: 2) - var values2: Queue = Queue(capacity: 2) - var values3: Queue = Queue(capacity: 2) - var values4: Queue = Queue(capacity: 2) - var values5: Queue = Queue(capacity: 2) - var values6: Queue = Queue(capacity: 2) - var values7: Queue = Queue(capacity: 2) + var _values1: Queue = Queue(capacity: 2) + var _values2: Queue = Queue(capacity: 2) + var _values3: Queue = Queue(capacity: 2) + var _values4: Queue = Queue(capacity: 2) + var _values5: Queue = Queue(capacity: 2) + var _values6: Queue = Queue(capacity: 2) + var _values7: Queue = Queue(capacity: 2) init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(arity: 7, observer: observer, cancel: cancel) } override func hasElements(index: Int) -> Bool { switch (index) { - case 0: return values1.count > 0 - case 1: return values2.count > 0 - case 2: return values3.count > 0 - case 3: return values4.count > 0 - case 4: return values5.count > 0 - case 5: return values6.count > 0 - case 6: return values7.count > 0 + case 0: return _values1.count > 0 + case 1: return _values2.count > 0 + case 2: return _values3.count > 0 + case 3: return _values4.count > 0 + case 4: return _values5.count > 0 + case 5: return _values6.count > 0 + case 6: return _values7.count > 0 default: rxFatalError("Unhandled case (Function)") @@ -600,21 +600,21 @@ class ZipSink7_ : ZipSink { let subscription6 = SingleAssignmentDisposable() let subscription7 = SingleAssignmentDisposable() - let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self.values1.enqueue($0) }, this: subscription1) - let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self.values2.enqueue($0) }, this: subscription2) - let observer3 = ZipObserver(lock: lock, parent: self, index: 2, setNextValue: { self.values3.enqueue($0) }, this: subscription3) - let observer4 = ZipObserver(lock: lock, parent: self, index: 3, setNextValue: { self.values4.enqueue($0) }, this: subscription4) - let observer5 = ZipObserver(lock: lock, parent: self, index: 4, setNextValue: { self.values5.enqueue($0) }, this: subscription5) - let observer6 = ZipObserver(lock: lock, parent: self, index: 5, setNextValue: { self.values6.enqueue($0) }, this: subscription6) - let observer7 = ZipObserver(lock: lock, parent: self, index: 6, setNextValue: { self.values7.enqueue($0) }, this: subscription7) + let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) + let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) + let observer3 = ZipObserver(lock: lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) + let observer4 = ZipObserver(lock: lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) + let observer5 = ZipObserver(lock: lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5) + let observer6 = ZipObserver(lock: lock, parent: self, index: 5, setNextValue: { self._values6.enqueue($0) }, this: subscription6) + let observer7 = ZipObserver(lock: lock, parent: self, index: 6, setNextValue: { self._values7.enqueue($0) }, this: subscription7) - subscription1.disposable = parent.source1.subscribeSafe(observer1) - subscription2.disposable = parent.source2.subscribeSafe(observer2) - subscription3.disposable = parent.source3.subscribeSafe(observer3) - subscription4.disposable = parent.source4.subscribeSafe(observer4) - subscription5.disposable = parent.source5.subscribeSafe(observer5) - subscription6.disposable = parent.source6.subscribeSafe(observer6) - subscription7.disposable = parent.source7.subscribeSafe(observer7) + subscription1.disposable = _parent.source1.subscribe(observer1) + subscription2.disposable = _parent.source2.subscribe(observer2) + subscription3.disposable = _parent.source3.subscribe(observer3) + subscription4.disposable = _parent.source4.subscribe(observer4) + subscription5.disposable = _parent.source5.subscribe(observer5) + subscription6.disposable = _parent.source6.subscribe(observer6) + subscription7.disposable = _parent.source7.subscribe(observer7) return CompositeDisposable(disposables: [ subscription1, @@ -628,7 +628,7 @@ class ZipSink7_ : ZipSink { } override func getResult() throws -> R { - return try self.parent.resultSelector(values1.dequeue(), values2.dequeue(), values3.dequeue(), values4.dequeue(), values5.dequeue(), values6.dequeue(), values7.dequeue()) + return try _parent._resultSelector(_values1.dequeue(), _values2.dequeue(), _values3.dequeue(), _values4.dequeue(), _values5.dequeue(), _values6.dequeue(), _values7.dequeue()) } } @@ -643,7 +643,7 @@ class Zip7 : Producer { let source6: Observable let source7: Observable - let resultSelector: ResultSelector + let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, source6: Observable, source7: Observable, resultSelector: ResultSelector) { self.source1 = source1 @@ -654,7 +654,7 @@ class Zip7 : Producer { self.source6 = source6 self.source7 = source7 - self.resultSelector = resultSelector + _resultSelector = resultSelector } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { @@ -675,7 +675,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ @warn_unused_result(message="http://git.io/rxs.uo") -public func zip +public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> R) -> Observable { return Zip8( @@ -688,32 +688,32 @@ class ZipSink8_ : ZipSink { typealias R = O.E typealias Parent = Zip8 - let parent: Parent + let _parent: Parent - var values1: Queue = Queue(capacity: 2) - var values2: Queue = Queue(capacity: 2) - var values3: Queue = Queue(capacity: 2) - var values4: Queue = Queue(capacity: 2) - var values5: Queue = Queue(capacity: 2) - var values6: Queue = Queue(capacity: 2) - var values7: Queue = Queue(capacity: 2) - var values8: Queue = Queue(capacity: 2) + var _values1: Queue = Queue(capacity: 2) + var _values2: Queue = Queue(capacity: 2) + var _values3: Queue = Queue(capacity: 2) + var _values4: Queue = Queue(capacity: 2) + var _values5: Queue = Queue(capacity: 2) + var _values6: Queue = Queue(capacity: 2) + var _values7: Queue = Queue(capacity: 2) + var _values8: Queue = Queue(capacity: 2) init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(arity: 8, observer: observer, cancel: cancel) } override func hasElements(index: Int) -> Bool { switch (index) { - case 0: return values1.count > 0 - case 1: return values2.count > 0 - case 2: return values3.count > 0 - case 3: return values4.count > 0 - case 4: return values5.count > 0 - case 5: return values6.count > 0 - case 6: return values7.count > 0 - case 7: return values8.count > 0 + case 0: return _values1.count > 0 + case 1: return _values2.count > 0 + case 2: return _values3.count > 0 + case 3: return _values4.count > 0 + case 4: return _values5.count > 0 + case 5: return _values6.count > 0 + case 6: return _values7.count > 0 + case 7: return _values8.count > 0 default: rxFatalError("Unhandled case (Function)") @@ -732,23 +732,23 @@ class ZipSink8_ : ZipSink { let subscription7 = SingleAssignmentDisposable() let subscription8 = SingleAssignmentDisposable() - let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self.values1.enqueue($0) }, this: subscription1) - let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self.values2.enqueue($0) }, this: subscription2) - let observer3 = ZipObserver(lock: lock, parent: self, index: 2, setNextValue: { self.values3.enqueue($0) }, this: subscription3) - let observer4 = ZipObserver(lock: lock, parent: self, index: 3, setNextValue: { self.values4.enqueue($0) }, this: subscription4) - let observer5 = ZipObserver(lock: lock, parent: self, index: 4, setNextValue: { self.values5.enqueue($0) }, this: subscription5) - let observer6 = ZipObserver(lock: lock, parent: self, index: 5, setNextValue: { self.values6.enqueue($0) }, this: subscription6) - let observer7 = ZipObserver(lock: lock, parent: self, index: 6, setNextValue: { self.values7.enqueue($0) }, this: subscription7) - let observer8 = ZipObserver(lock: lock, parent: self, index: 7, setNextValue: { self.values8.enqueue($0) }, this: subscription8) + let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) + let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) + let observer3 = ZipObserver(lock: lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) + let observer4 = ZipObserver(lock: lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) + let observer5 = ZipObserver(lock: lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5) + let observer6 = ZipObserver(lock: lock, parent: self, index: 5, setNextValue: { self._values6.enqueue($0) }, this: subscription6) + let observer7 = ZipObserver(lock: lock, parent: self, index: 6, setNextValue: { self._values7.enqueue($0) }, this: subscription7) + let observer8 = ZipObserver(lock: lock, parent: self, index: 7, setNextValue: { self._values8.enqueue($0) }, this: subscription8) - subscription1.disposable = parent.source1.subscribeSafe(observer1) - subscription2.disposable = parent.source2.subscribeSafe(observer2) - subscription3.disposable = parent.source3.subscribeSafe(observer3) - subscription4.disposable = parent.source4.subscribeSafe(observer4) - subscription5.disposable = parent.source5.subscribeSafe(observer5) - subscription6.disposable = parent.source6.subscribeSafe(observer6) - subscription7.disposable = parent.source7.subscribeSafe(observer7) - subscription8.disposable = parent.source8.subscribeSafe(observer8) + subscription1.disposable = _parent.source1.subscribe(observer1) + subscription2.disposable = _parent.source2.subscribe(observer2) + subscription3.disposable = _parent.source3.subscribe(observer3) + subscription4.disposable = _parent.source4.subscribe(observer4) + subscription5.disposable = _parent.source5.subscribe(observer5) + subscription6.disposable = _parent.source6.subscribe(observer6) + subscription7.disposable = _parent.source7.subscribe(observer7) + subscription8.disposable = _parent.source8.subscribe(observer8) return CompositeDisposable(disposables: [ subscription1, @@ -763,7 +763,7 @@ class ZipSink8_ : ZipSink { } override func getResult() throws -> R { - return try self.parent.resultSelector(values1.dequeue(), values2.dequeue(), values3.dequeue(), values4.dequeue(), values5.dequeue(), values6.dequeue(), values7.dequeue(), values8.dequeue()) + return try _parent._resultSelector(_values1.dequeue(), _values2.dequeue(), _values3.dequeue(), _values4.dequeue(), _values5.dequeue(), _values6.dequeue(), _values7.dequeue(), _values8.dequeue()) } } @@ -779,7 +779,7 @@ class Zip8 : Producer { let source7: Observable let source8: Observable - let resultSelector: ResultSelector + let _resultSelector: ResultSelector init(source1: Observable, source2: Observable, source3: Observable, source4: Observable, source5: Observable, source6: Observable, source7: Observable, source8: Observable, resultSelector: ResultSelector) { self.source1 = source1 @@ -791,7 +791,7 @@ class Zip8 : Producer { self.source7 = source7 self.source8 = source8 - self.resultSelector = resultSelector + _resultSelector = resultSelector } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { diff --git a/RxSwift/Observables/Implementations/Zip+arity.tt b/RxSwift/Observables/Implementations/Zip+arity.tt index 2783b731..6b9a0460 100644 --- a/RxSwift/Observables/Implementations/Zip+arity.tt +++ b/RxSwift/Observables/Implementations/Zip+arity.tt @@ -19,7 +19,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ @warn_unused_result(message="http://git.io/rxs.uo") -public func zip<<%= (Array(1...i).map { "O\($0): ObservableType" }).joinWithSeparator(", ") %>, R> +public func zip<<%= (Array(1...i).map { "O\($0): ObservableConvertibleType" }).joinWithSeparator(", ") %>, R> (<%= (Array(1...i).map { "source\($0): O\($0)" }).joinWithSeparator(", _ ") %>, resultSelector: (<%= (Array(1...i).map { "O\($0).E" }).joinWithSeparator(", ") %>) throws -> R) -> Observable { return Zip<%= i %>( @@ -32,21 +32,21 @@ class ZipSink<%= i %>_<<%= (Array(1...i).map { "E\($0)" }).joinWithSeparator(", typealias R = O.E typealias Parent = Zip<%= i %><<%= (Array(1...i).map { "E\($0)" }).joinWithSeparator(", ") %>, R> - let parent: Parent + let _parent: Parent <%= (Array(1...i).map { -" var values\($0): Queue = Queue(capacity: 2)" +" var _values\($0): Queue = Queue(capacity: 2)" }).joinWithSeparator("\n") %> init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent + _parent = parent super.init(arity: <%= i %>, observer: observer, cancel: cancel) } override func hasElements(index: Int) -> Bool { switch (index) { <%= (Array(0.. 0\n" +" case \($0): return _values\($0 + 1).count > 0\n" }).joinWithSeparator("") %> default: rxFatalError("Unhandled case \(index)") @@ -61,11 +61,11 @@ class ZipSink<%= i %>_<<%= (Array(1...i).map { "E\($0)" }).joinWithSeparator(", }).joinWithSeparator("\n") %> <%= (Array(1...i).map { -" let observer\($0) = ZipObserver(lock: lock, parent: self, index: \($0 - 1), setNextValue: { self.values\($0).enqueue($0) }, this: subscription\($0))" +" let observer\($0) = ZipObserver(lock: lock, parent: self, index: \($0 - 1), setNextValue: { self._values\($0).enqueue($0) }, this: subscription\($0))" }).joinWithSeparator("\n") %> <%= (Array(1...i).map { -" subscription\($0).disposable = parent.source\($0).subscribeSafe(observer\($0))" }).joinWithSeparator("\n") +" subscription\($0).disposable = _parent.source\($0).subscribe(observer\($0))" }).joinWithSeparator("\n") %> return CompositeDisposable(disposables: [ @@ -74,7 +74,7 @@ class ZipSink<%= i %>_<<%= (Array(1...i).map { "E\($0)" }).joinWithSeparator(", } override func getResult() throws -> R { - return try self.parent.resultSelector(<%= (Array(1...i).map { "values\($0).dequeue()" }).joinWithSeparator(", ") %>) + return try _parent._resultSelector(<%= (Array(1...i).map { "_values\($0).dequeue()" }).joinWithSeparator(", ") %>) } } @@ -83,14 +83,14 @@ class Zip<%= i %><<%= (Array(1...i).map { "E\($0)" }).joinWithSeparator(", ") %> <%= (Array(1...i).map { " let source\($0): Observable" }).joinWithSeparator("\n") %> - let resultSelector: ResultSelector + let _resultSelector: ResultSelector init(<%= (Array(1...i).map { "source\($0): Observable" }).joinWithSeparator(", ") %>, resultSelector: ResultSelector) { <%= (Array(1...i).map { " self.source\($0) = source\($0)" }).joinWithSeparator("\n") %> - self.resultSelector = resultSelector + _resultSelector = resultSelector } override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { diff --git a/RxSwift/Observers/TailRecursiveSink.swift b/RxSwift/Observers/TailRecursiveSink.swift index 849f35eb..93bee506 100644 --- a/RxSwift/Observers/TailRecursiveSink.swift +++ b/RxSwift/Observers/TailRecursiveSink.swift @@ -101,7 +101,7 @@ class TailRecursiveSink Date: Sun, 25 Oct 2015 19:23:35 +0700 Subject: [PATCH 100/210] Change diposeBag to disposeBag --- .../Examples/Calculator/CalculatorViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RxExample/RxExample/Examples/Calculator/CalculatorViewController.swift b/RxExample/RxExample/Examples/Calculator/CalculatorViewController.swift index 9620320f..b094033e 100644 --- a/RxExample/RxExample/Examples/Calculator/CalculatorViewController.swift +++ b/RxExample/RxExample/Examples/Calculator/CalculatorViewController.swift @@ -67,7 +67,7 @@ class CalculatorViewController: ViewController { let CLEAR_STATE = CalState(previousNumber: nil, action: .Clear, currentNumber: "0", inScreen: "0", replace: true) - let diposeBag = DisposeBag() + let disposeBag = DisposeBag() override func viewDidLoad() { let commands:[Observable] = [ @@ -122,7 +122,7 @@ class CalculatorViewController: ViewController { self?.lastSignLabel.text = "" } } - .addDisposableTo(diposeBag) + .addDisposableTo(disposeBag) } func tranformState(a: CalState, _ x: Action) -> CalState { From cc8e57a8964d89807e7ee8cd1a39ec47d1b69026 Mon Sep 17 00:00:00 2001 From: Tomi Koskinen Date: Mon, 26 Oct 2015 18:55:50 +0200 Subject: [PATCH 101/210] TakeLast operator --- Rx.xcodeproj/project.pbxproj | 10 + .../Implementations/TakeLast.swift | 61 +++++ ...Observable+StandardSequenceOperators.swift | 23 ++ ...rvable+StandardSequenceOperatorsTest.swift | 248 ++++++++++++++++++ 4 files changed, 342 insertions(+) create mode 100644 RxSwift/Observables/Implementations/TakeLast.swift diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index cfc86a26..1363eb84 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + B1B7C3BD1BDD39DB0076934E /* TakeLast.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B7C3BC1BDD39DB0076934E /* TakeLast.swift */; }; + B1B7C3BE1BDD39DB0076934E /* TakeLast.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B7C3BC1BDD39DB0076934E /* TakeLast.swift */; }; + B1B7C3BF1BDD39DB0076934E /* TakeLast.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B7C3BC1BDD39DB0076934E /* TakeLast.swift */; }; + B1B7C3C01BDD39DB0076934E /* TakeLast.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B7C3BC1BDD39DB0076934E /* TakeLast.swift */; }; C8093CC51B8A72BE0088E94D /* Cancelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C491B8A72BE0088E94D /* Cancelable.swift */; }; C8093CC61B8A72BE0088E94D /* Cancelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C491B8A72BE0088E94D /* Cancelable.swift */; }; C8093CC71B8A72BE0088E94D /* AsyncLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C4B1B8A72BE0088E94D /* AsyncLock.swift */; }; @@ -782,6 +786,7 @@ /* Begin PBXFileReference section */ A111CE961B91C97C00D0DCEE /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B1B7C3BC1BDD39DB0076934E /* TakeLast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TakeLast.swift; sourceTree = ""; }; C809396D1B8A71760088E94D /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C80939E71B8A71840088E94D /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8093BC71B8A71F00088E94D /* RxBlocking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxBlocking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1215,6 +1220,7 @@ C8093C8B1B8A72BE0088E94D /* SubscribeOn.swift */, C8093C8C1B8A72BE0088E94D /* Switch.swift */, C8093C8D1B8A72BE0088E94D /* Take.swift */, + B1B7C3BC1BDD39DB0076934E /* TakeLast.swift */, C8093C8E1B8A72BE0088E94D /* TakeUntil.swift */, C8093C8F1B8A72BE0088E94D /* TakeWhile.swift */, C8093C901B8A72BE0088E94D /* Throttle.swift */, @@ -2138,6 +2144,7 @@ C8093D1A1B8A72BE0088E94D /* DistinctUntilChanged.swift in Sources */, C8093D561B8A72BE0088E94D /* Observable+Binding.swift in Sources */, C8093D7A1B8A72BE0088E94D /* TailRecursiveSink.swift in Sources */, + B1B7C3BE1BDD39DB0076934E /* TakeLast.swift in Sources */, C8093CC81B8A72BE0088E94D /* AsyncLock.swift in Sources */, C8093CD81B8A72BE0088E94D /* BinaryDisposable.swift in Sources */, C89CDB371BCB0DD7002063D9 /* ShareReplay1.swift in Sources */, @@ -2259,6 +2266,7 @@ C8093D191B8A72BE0088E94D /* DistinctUntilChanged.swift in Sources */, C8093D551B8A72BE0088E94D /* Observable+Binding.swift in Sources */, C8093D791B8A72BE0088E94D /* TailRecursiveSink.swift in Sources */, + B1B7C3BD1BDD39DB0076934E /* TakeLast.swift in Sources */, C8093CC71B8A72BE0088E94D /* AsyncLock.swift in Sources */, C8093CD71B8A72BE0088E94D /* BinaryDisposable.swift in Sources */, C89CDB361BCB0DD7002063D9 /* ShareReplay1.swift in Sources */, @@ -2380,6 +2388,7 @@ C8F0BFB21BBBFB8B001B112F /* DistinctUntilChanged.swift in Sources */, C8F0BFB31BBBFB8B001B112F /* Observable+Binding.swift in Sources */, C8F0BFB41BBBFB8B001B112F /* TailRecursiveSink.swift in Sources */, + B1B7C3C01BDD39DB0076934E /* TakeLast.swift in Sources */, C8F0BFB51BBBFB8B001B112F /* AsyncLock.swift in Sources */, C8F0BFB61BBBFB8B001B112F /* BinaryDisposable.swift in Sources */, C89CDB391BCB0DD7002063D9 /* ShareReplay1.swift in Sources */, @@ -2651,6 +2660,7 @@ D2EBEB3A1BB9B6D8003A27DC /* MainScheduler.swift in Sources */, D2EBEB101BB9B6C1003A27DC /* Just.swift in Sources */, D2EBEB181BB9B6C1003A27DC /* Range.swift in Sources */, + B1B7C3BF1BDD39DB0076934E /* TakeLast.swift in Sources */, D2EBEAE21BB9B697003A27DC /* Observable.swift in Sources */, D2EBEB091BB9B6C1003A27DC /* DistinctUntilChanged.swift in Sources */, D2EBEB2A1BB9B6C5003A27DC /* Zip+CollectionType.swift in Sources */, diff --git a/RxSwift/Observables/Implementations/TakeLast.swift b/RxSwift/Observables/Implementations/TakeLast.swift new file mode 100644 index 00000000..b7f9d5f1 --- /dev/null +++ b/RxSwift/Observables/Implementations/TakeLast.swift @@ -0,0 +1,61 @@ +// +// TakeLast.swift +// Rx +// +// Created by Tomi Koskinen on 25/10/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + + +class TakeLastSink : Sink, ObserverType { + typealias Parent = TakeLast + typealias E = ElementType + + private let _parent: Parent + + private var _elements = [ElementType]() + + init(parent: Parent, observer: O, cancel: Disposable) { + _parent = parent + super.init(observer: observer, cancel: cancel) + } + + func on(event: Event) { + switch event { + case .Next(let value): + _elements.append(value) + if _elements.count > self._parent._count { + _elements.removeFirst() + } + case .Error: + observer?.on(event) + dispose() + case .Completed: + if self._elements.count > 0 { + self._elements.forEach { element in + observer?.on(.Next(element)) + } + } + observer?.on(.Completed) + dispose() + } + } +} + +class TakeLast: Producer { + private let _source: Observable + private let _count: Int + + init(source: Observable, count: Int) { + _source = source + _count = count + } + + override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + let sink = TakeLastSink(parent: self, observer: observer, cancel: cancel) + setSink(sink) + return _source.subscribe(sink) + } +} \ No newline at end of file diff --git a/RxSwift/Observables/Observable+StandardSequenceOperators.swift b/RxSwift/Observables/Observable+StandardSequenceOperators.swift index fadccece..f624f2c6 100644 --- a/RxSwift/Observables/Observable+StandardSequenceOperators.swift +++ b/RxSwift/Observables/Observable+StandardSequenceOperators.swift @@ -77,7 +77,30 @@ extension ObservableType { } } } + +// MARK: takeLast + +extension ObservableType { + /** + Returns a specified number of the last elements before the observable sequence completed. + + - parameter count: The number of elements to return. + - returns: An observable sequence that contains the specified number of elements from the end of the input sequence. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func takeLast(count: Int) + -> Observable { + if count == 0 { + return empty() + } + else { + return TakeLast(source: self.asObservable(), count: count) + } + } +} + + // MARK: skip extension ObservableType { diff --git a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift index ecdc305c..df635300 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift @@ -2816,6 +2816,254 @@ extension ObservableStandardSequenceOperators { } } +// MARK: takeLast + +extension ObservableStandardSequenceOperators { + func testTakeLast_Complete_Less() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(70, 6), + next(150, 4), + next(210, 9), + next(230, 13), + next(270, 7), + next(280, 1), + next(300, -1), + completed(300) + ]) + + let res = scheduler.start { + xs.takeLast(7) + } + + XCTAssertEqual(res.messages, [ + next(300, 9), + next(300, 13), + next(300, 7), + next(300, 1), + next(300, -1), + completed(300) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 300) + ]) + } + + func testTakeLast_Complete_Same() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(70, 6), + next(150, 4), + next(210, 9), + next(230, 13), + next(270, 7), + next(280, 1), + next(300, -1), + completed(310) + ]) + + let res = scheduler.start { + xs.takeLast(5) + } + + XCTAssertEqual(res.messages, [ + next(310, 9), + next(310, 13), + next(310, 7), + next(310, 1), + next(310, -1), + completed(310) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 310) + ]) + } + + func testTakeLast_Complete_More() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(70, 6), + next(150, 4), + next(210, 9), + next(230, 13), + next(270, 7), + next(280, 1), + next(300, -1), + next(310, 3), + next(340, 8), + completed(350) + ]) + + let res = scheduler.start { + xs.takeLast(5) + } + + XCTAssertEqual(res.messages, [ + next(350, 7), + next(350, 1), + next(350, -1), + next(350, 3), + next(350, 8), + completed(350) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 350) + ]) + } + + func testTakeLast_Error_Less() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(70, 6), + next(150, 4), + next(210, 9), + next(230, 13), + next(270, 7), + next(280, 1), + next(290, 64), + error(300, testError) + ]) + + let res = scheduler.start { + xs.takeLast(7) + } + + XCTAssertEqual(res.messages, [ + error(300, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 300) + ]) + } + + func testTakeLast_Error_Same() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(70, 6), + next(150, 4), + next(210, 9), + next(230, 13), + next(270, 7), + next(280, 1), + next(300, -1), + error(310, testError) + ]) + + let res = scheduler.start { + xs.takeLast(5) + } + + XCTAssertEqual(res.messages, [ + error(310, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 310) + ]) + } + + func testTakeLast_Error_More() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(70, 6), + next(150, 4), + next(210, 9), + next(230, 13), + next(270, 7), + next(280, 1), + next(300, -1), + next(310, 3), + next(340, 64), + error(360, testError) + ]) + + let res = scheduler.start { + xs.takeLast(5) + } + + XCTAssertEqual(res.messages, [ + error(360, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 360) + ]) + } + + func testTakeLast_0_DefaultScheduler() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(70, 6), + next(150, 4), + next(210, 9), + next(230, 13) + ]) + + let res = scheduler.start { + xs.takeLast(0) + } + + XCTAssertEqual(res.messages, [ + completed(200) + ]) + + XCTAssertEqual(xs.subscriptions, [ + ]) + } + + func testTakeLast_TakeLast1() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(70, 6), + next(150, 4), + next(210, 9), + next(230, 13), + next(270, 7), + next(280, 1), + next(300, -1), + next(310, 3), + next(340, 8), + next(370, 11), + completed(400) + ]) + + let res = scheduler.start { + xs.takeLast(3) + } + + XCTAssertEqual(res.messages, [ + next(400, 3), + next(400, 8), + next(400, 11), + completed(400) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 400) + ]) + } + + func testTakeLast_DecrementCountsFirst() { + let k = BehaviorSubject(value: false) + + _ = k.takeLast(1).subscribeNext { n in + k.on(.Next(!n)) + } + } +} + // MARK: skip extension ObservableStandardSequenceOperators { func testSkip_Complete_After() { From 3e7345df04cfa45bcd5e9ccab7b28abd252ae262 Mon Sep 17 00:00:00 2001 From: Tomi Koskinen Date: Tue, 27 Oct 2015 21:40:26 +0200 Subject: [PATCH 102/210] TakeLast review fixes --- RxExample/RxExample.xcodeproj/project.pbxproj | 4 ++++ .../Observables/Implementations/TakeLast.swift | 16 +++++++++------- .../Observable+StandardSequenceOperators.swift | 15 ++++++--------- ...bservable+StandardSequenceOperatorsTest.swift | 2 +- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index 70f3c369..75817321 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 07E300071B14995F00F00100 /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E300061B14995F00F00100 /* TableViewController.swift */; }; 07E300091B149A2A00F00100 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E300081B149A2A00F00100 /* User.swift */; }; 07E3C2331B03605B0010338D /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E3C2321B03605B0010338D /* Dependencies.swift */; }; + B1B7C3D01BE006870076934E /* TakeLast.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B7C3CF1BE006870076934E /* TakeLast.swift */; }; C803973A1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; }; C803973B1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; }; C80397491BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */; }; @@ -451,6 +452,7 @@ 07E300061B14995F00F00100 /* TableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; }; 07E300081B149A2A00F00100 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 07E3C2321B03605B0010338D /* Dependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Dependencies.swift; path = Examples/Dependencies.swift; sourceTree = ""; }; + B1B7C3CF1BE006870076934E /* TakeLast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TakeLast.swift; sourceTree = ""; }; C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = ""; }; C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubSearchRepositoriesAPI.swift; sourceTree = ""; }; C80DDE7A1BCDA952006A1832 /* SkipWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipWhile.swift; sourceTree = ""; }; @@ -1130,6 +1132,7 @@ C894646F1BC6C2B00055219D /* SubscribeOn.swift */, C89464701BC6C2B00055219D /* Switch.swift */, C89464711BC6C2B00055219D /* Take.swift */, + B1B7C3CF1BE006870076934E /* TakeLast.swift */, C89464721BC6C2B00055219D /* TakeUntil.swift */, C89464731BC6C2B00055219D /* TakeWhile.swift */, C89464741BC6C2B00055219D /* Throttle.swift */, @@ -1661,6 +1664,7 @@ C89464BB1BC6C2B00055219D /* AnonymousObservable.swift in Sources */, C89465991BC6C2BC0055219D /* UISegmentedControl+Rx.swift in Sources */, C8297E371B6CF905000589EA /* RxCollectionViewSectionedDataSource.swift in Sources */, + B1B7C3D01BE006870076934E /* TakeLast.swift in Sources */, C89464CD1BC6C2B00055219D /* FlatMap.swift in Sources */, C8297E381B6CF905000589EA /* Changeset.swift in Sources */, C8297E391B6CF905000589EA /* CollectionViewImageCell.swift in Sources */, diff --git a/RxSwift/Observables/Implementations/TakeLast.swift b/RxSwift/Observables/Implementations/TakeLast.swift index b7f9d5f1..842d9c49 100644 --- a/RxSwift/Observables/Implementations/TakeLast.swift +++ b/RxSwift/Observables/Implementations/TakeLast.swift @@ -15,28 +15,27 @@ class TakeLastSink : Sink private let _parent: Parent - private var _elements = [ElementType]() + private var _elements: Queue init(parent: Parent, observer: O, cancel: Disposable) { _parent = parent + _elements = Queue(capacity: parent._count) super.init(observer: observer, cancel: cancel) } func on(event: Event) { switch event { case .Next(let value): - _elements.append(value) + _elements.enqueue(value) if _elements.count > self._parent._count { - _elements.removeFirst() + _elements.dequeue() } case .Error: observer?.on(event) dispose() case .Completed: - if self._elements.count > 0 { - self._elements.forEach { element in - observer?.on(.Next(element)) - } + for e in _elements { + observer?.on(.Next(e)) } observer?.on(.Completed) dispose() @@ -49,6 +48,9 @@ class TakeLast: Producer { private let _count: Int init(source: Observable, count: Int) { + if count < 0 { + rxFatalError("count can't be negative") + } _source = source _count = count } diff --git a/RxSwift/Observables/Observable+StandardSequenceOperators.swift b/RxSwift/Observables/Observable+StandardSequenceOperators.swift index f624f2c6..5278c3de 100644 --- a/RxSwift/Observables/Observable+StandardSequenceOperators.swift +++ b/RxSwift/Observables/Observable+StandardSequenceOperators.swift @@ -83,20 +83,17 @@ extension ObservableType { extension ObservableType { /** - Returns a specified number of the last elements before the observable sequence completed. + Returns a specified number of contiguous elements from the end of an observable sequence. - - parameter count: The number of elements to return. - - returns: An observable sequence that contains the specified number of elements from the end of the input sequence. + This operator accumulates a buffer with a length enough to store elements count elements. Upon completion of the source sequence, this buffer is drained on the result sequence. This causes the elements to be delayed. + + - parameter count: Number of elements to take from the end of the source sequence. + - returns: An observable sequence containing the specified number of elements from the end of the source sequence. */ @warn_unused_result(message="http://git.io/rxs.uo") public func takeLast(count: Int) -> Observable { - if count == 0 { - return empty() - } - else { - return TakeLast(source: self.asObservable(), count: count) - } + return TakeLast(source: self.asObservable(), count: count) } } diff --git a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift index df635300..5015bac7 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift @@ -3015,10 +3015,10 @@ extension ObservableStandardSequenceOperators { } XCTAssertEqual(res.messages, [ - completed(200) ]) XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 1000) ]) } From a639b462315302b5c6a3db7a8e138f81fc530cc4 Mon Sep 17 00:00:00 2001 From: Tomi Koskinen Date: Tue, 27 Oct 2015 21:30:33 +0200 Subject: [PATCH 103/210] TakeCount count boundary check --- RxSwift/Observables/Implementations/Take.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RxSwift/Observables/Implementations/Take.swift b/RxSwift/Observables/Implementations/Take.swift index 1f4dc72e..9208e618 100644 --- a/RxSwift/Observables/Implementations/Take.swift +++ b/RxSwift/Observables/Implementations/Take.swift @@ -54,6 +54,9 @@ class TakeCount: Producer { private let _count: Int init(source: Observable, count: Int) { + if count < 0 { + rxFatalError("count can't be negative") + } _source = source _count = count } From 5adacc4092e4f4dae9b43d7eb91244bc858bf70b Mon Sep 17 00:00:00 2001 From: Sergey Timoshin Date: Wed, 28 Oct 2015 00:02:13 +0300 Subject: [PATCH 104/210] Typo fix --- Documentation/GettingStarted.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/GettingStarted.md b/Documentation/GettingStarted.md index 71427a0f..74b5d768 100644 --- a/Documentation/GettingStarted.md +++ b/Documentation/GettingStarted.md @@ -304,7 +304,7 @@ Let's create a function which creates a sequence that returns one element upon s func myJust(element: E) -> Observable { return create { observer in observer.on(.Next(element)) - obsever.on(.Completed) + observer.on(.Completed) return NopDisposable.instance } } From 51d138a17728b95f99ac50ce1db548de4a384a41 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Tue, 27 Oct 2015 22:14:37 +0100 Subject: [PATCH 105/210] Increases capacity to correct value on `TakeLast`. --- RxSwift/Observables/Implementations/TakeLast.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RxSwift/Observables/Implementations/TakeLast.swift b/RxSwift/Observables/Implementations/TakeLast.swift index 842d9c49..34edb8a2 100644 --- a/RxSwift/Observables/Implementations/TakeLast.swift +++ b/RxSwift/Observables/Implementations/TakeLast.swift @@ -19,7 +19,7 @@ class TakeLastSink : Sink init(parent: Parent, observer: O, cancel: Disposable) { _parent = parent - _elements = Queue(capacity: parent._count) + _elements = Queue(capacity: parent._count + 1) super.init(observer: observer, cancel: cancel) } From 3be67a317d67bfbcab3acb9f19dabcd242b9ea32 Mon Sep 17 00:00:00 2001 From: Tomi Koskinen Date: Wed, 28 Oct 2015 07:14:46 +0200 Subject: [PATCH 106/210] improve api documentation --- Documentation/API.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/API.md b/Documentation/API.md index e0549f25..04a15b94 100644 --- a/Documentation/API.md +++ b/Documentation/API.md @@ -21,6 +21,8 @@ Operators are stateless by default. * [`never`](http://reactivex.io/documentation/operators/empty-never-throw.html) * [`returnElement` / `just`](http://reactivex.io/documentation/operators/just.html) * [`returnElements`](http://reactivex.io/documentation/operators/from.html) + * [`range`](http://reactivex.io/documentation/operators/range.html) + * [`repeatElement`](http://reactivex.io/documentation/operators/repeat.html) * [`timer`](http://reactivex.io/documentation/operators/timer.html) #### Transforming Observables @@ -32,10 +34,12 @@ Operators are stateless by default. #### Filtering Observables * [`debounce` / `throttle`](http://reactivex.io/documentation/operators/debounce.html) * [`distinctUntilChanged`](http://reactivex.io/documentation/operators/distinct.html) + * [`ElementAt`](http://reactivex.io/documentation/operators/elementat.html) * [`filter` / `where`](http://reactivex.io/documentation/operators/filter.html) * [`sample`](http://reactivex.io/documentation/operators/sample.html) * [`skip`](http://reactivex.io/documentation/operators/skip.html) * [`take`](http://reactivex.io/documentation/operators/take.html) + * [`takeLast`](http://reactivex.io/documentation/operators/takelast.html) #### Combining Observables @@ -57,11 +61,13 @@ Operators are stateless by default. * [`observeOn` / `observeSingleOn`](http://reactivex.io/documentation/operators/observeon.html) * [`subscribe`](http://reactivex.io/documentation/operators/subscribe.html) * [`subscribeOn`](http://reactivex.io/documentation/operators/subscribeon.html) + * [`using`](http://reactivex.io/documentation/operators/using.html) * debug #### Conditional and Boolean Operators * [`amb`](http://reactivex.io/documentation/operators/amb.html) * [`skipWhile`](http://reactivex.io/documentation/operators/skipwhile.html) + * [`skipUntil`](http://reactivex.io/documentation/operators/skipuntil.html) * [`takeUntil`](http://reactivex.io/documentation/operators/takeuntil.html) * [`takeWhile`](http://reactivex.io/documentation/operators/takewhile.html) @@ -69,6 +75,7 @@ Operators are stateless by default. * [`concat`](http://reactivex.io/documentation/operators/concat.html) * [`reduce` / `aggregate`](http://reactivex.io/documentation/operators/reduce.html) + * [`toArray`](http://reactivex.io/documentation/operators/to.html) #### Connectable Observable Operators From af31d2155f373bf1f6cc6f09ad8f2217f11f8dfc Mon Sep 17 00:00:00 2001 From: yury Date: Thu, 29 Oct 2015 01:58:11 +0300 Subject: [PATCH 107/210] Run playgrounds in pre-release-tests.sh --- .../Contents.swift | 3 +-- Rx.playground/Sources/SupportCode.swift | 17 ++++++++++++++++- scripts/playgrounds.sh | 16 ++++++++++++++++ scripts/pre-release-tests.sh | 4 ++++ 4 files changed, 37 insertions(+), 3 deletions(-) create mode 100755 scripts/playgrounds.sh diff --git a/Rx.playground/Pages/Connectable_Observable_Operators.xcplaygroundpage/Contents.swift b/Rx.playground/Pages/Connectable_Observable_Operators.xcplaygroundpage/Contents.swift index 01c41486..6d965bb3 100644 --- a/Rx.playground/Pages/Connectable_Observable_Operators.xcplaygroundpage/Contents.swift +++ b/Rx.playground/Pages/Connectable_Observable_Operators.xcplaygroundpage/Contents.swift @@ -1,7 +1,6 @@ //: [<< Previous](@previous) - [Index](Index) import RxSwift -import XCPlayground /*: ## Connectable Observable Operators @@ -200,6 +199,6 @@ func sampleWithPublish() { // sampleWithPublish() -XCPSetExecutionShouldContinueIndefinitely(true) +playgroundShouldContinueIndefinitely() //: [Index](Index) diff --git a/Rx.playground/Sources/SupportCode.swift b/Rx.playground/Sources/SupportCode.swift index f609a0b9..0228d59e 100644 --- a/Rx.playground/Sources/SupportCode.swift +++ b/Rx.playground/Sources/SupportCode.swift @@ -13,4 +13,19 @@ public func delay(delay:Double, closure:()->()) { Int64(delay * Double(NSEC_PER_SEC)) ), dispatch_get_main_queue(), closure) -} \ No newline at end of file +} + +#if NOT_IN_PLAYGROUND + +public func playgroundShouldContinueIndefinitely() { +} + +#else + +import XCPlayground + +public func playgroundShouldContinueIndefinitely() { + XCPSetExecutionShouldContinueIndefinitely(true) +} + +#endif diff --git a/scripts/playgrounds.sh b/scripts/playgrounds.sh new file mode 100755 index 00000000..cbcb5615 --- /dev/null +++ b/scripts/playgrounds.sh @@ -0,0 +1,16 @@ +. scripts/common.sh + +CONFIGURATIONS=(Release) + +# make sure osx builds +for scheme in "RxSwift-OSX" +do + for configuration in ${CONFIGURATIONS[@]} + do + PAGES_PATH=${BUILD_DIRECTORY}/Build/Products/${configuration}/all-playground-pages.swift + rx ${scheme} ${configuration} "" build + cat Rx.playground/Sources/*.swift Rx.playground/Pages/**/*.swift > ${PAGES_PATH} + swift -v -D NOT_IN_PLAYGROUND -F ${BUILD_DIRECTORY}/Build/Products/${configuration} ${PAGES_PATH} + done +done + diff --git a/scripts/pre-release-tests.sh b/scripts/pre-release-tests.sh index 0227a968..d8ae809f 100755 --- a/scripts/pre-release-tests.sh +++ b/scripts/pre-release-tests.sh @@ -113,6 +113,10 @@ do done done +# compile and run playgrounds + +. scripts/playgrounds.sh + if [ "${RELEASE_TEST}" -eq 1 ]; then mdast -u mdast-slug -u mdast-validate-links ./*.md mdast -u mdast-slug -u mdast-validate-links ./**/*.md From fc9c5645870acb62f1430b7b778c9a91a70db9b3 Mon Sep 17 00:00:00 2001 From: yury Date: Thu, 29 Oct 2015 13:45:19 +0300 Subject: [PATCH 108/210] Build should failed --- .../Pages/Combining_Observables.xcplaygroundpage/Contents.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift b/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift index ea0ef648..0d820021 100644 --- a/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift +++ b/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift @@ -67,7 +67,7 @@ example("combineLatest 1") { example("combineLatest 2") { let intOb1 = just(2) let intOb2 = sequenceOf(0, 1, 2, 3, 4) - + build should failed combineLatest(intOb1, intOb2) { $0 * $1 } From cfa3ff051c1a618701795c0df01f326abdf1e4d1 Mon Sep 17 00:00:00 2001 From: yury Date: Thu, 29 Oct 2015 14:16:59 +0300 Subject: [PATCH 109/210] Revert "Build should failed" This reverts commit fc9c5645870acb62f1430b7b778c9a91a70db9b3. --- .../Pages/Combining_Observables.xcplaygroundpage/Contents.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift b/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift index 0d820021..ea0ef648 100644 --- a/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift +++ b/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift @@ -67,7 +67,7 @@ example("combineLatest 1") { example("combineLatest 2") { let intOb1 = just(2) let intOb2 = sequenceOf(0, 1, 2, 3, 4) - build should failed + combineLatest(intOb1, intOb2) { $0 * $1 } From 0995a7e97945b77a73b0bcf446ef0b763cb1d35a Mon Sep 17 00:00:00 2001 From: yury Date: Thu, 29 Oct 2015 14:27:31 +0300 Subject: [PATCH 110/210] Remove warnings in playground --- .../Contents.swift | 16 +++++----- .../Contents.swift | 4 +-- .../Contents.swift | 30 +++++++++---------- .../Contents.swift | 6 ++-- .../Contents.swift | 4 +-- .../Contents.swift | 2 +- .../Contents.swift | 10 +++---- .../Subjects.xcplaygroundpage/Contents.swift | 2 +- .../Contents.swift | 6 ++-- 9 files changed, 40 insertions(+), 40 deletions(-) diff --git a/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift b/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift index ea0ef648..5ae6ce57 100644 --- a/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift +++ b/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift @@ -45,7 +45,7 @@ example("combineLatest 1") { let intOb1 = PublishSubject() let intOb2 = PublishSubject() - combineLatest(intOb1, intOb2) { + _ = combineLatest(intOb1, intOb2) { "\($0) \($1)" } .subscribe { @@ -68,7 +68,7 @@ example("combineLatest 2") { let intOb1 = just(2) let intOb2 = sequenceOf(0, 1, 2, 3, 4) - combineLatest(intOb1, intOb2) { + _ = combineLatest(intOb1, intOb2) { $0 * $1 } .subscribe { @@ -85,7 +85,7 @@ example("combineLatest 3") { let intOb2 = sequenceOf(0, 1, 2, 3) let intOb3 = sequenceOf(0, 1, 2, 3, 4) - combineLatest(intOb1, intOb2, intOb3) { + _ = combineLatest(intOb1, intOb2, intOb3) { ($0 + $1) * $2 } .subscribe { @@ -108,7 +108,7 @@ example("zip 1") { let intOb1 = PublishSubject() let intOb2 = PublishSubject() - zip(intOb1, intOb2) { + _ = zip(intOb1, intOb2) { "\($0) \($1)" } .subscribe { @@ -132,7 +132,7 @@ example("zip 2") { let intOb2 = sequenceOf(0, 1, 2, 3, 4) - zip(intOb1, intOb2) { + _ = zip(intOb1, intOb2) { $0 * $1 } .subscribe { @@ -146,7 +146,7 @@ example("zip 3") { let intOb2 = sequenceOf(0, 1, 2, 3) let intOb3 = sequenceOf(0, 1, 2, 3, 4) - zip(intOb1, intOb2, intOb3) { + _ = zip(intOb1, intOb2, intOb3) { ($0 + $1) * $2 } .subscribe { @@ -170,7 +170,7 @@ example("merge 1") { let subject1 = PublishSubject() let subject2 = PublishSubject() - sequenceOf(subject1, subject2) + _ = sequenceOf(subject1, subject2) .merge() .subscribeNext { int in print(int) @@ -190,7 +190,7 @@ example("merge 2") { let subject1 = PublishSubject() let subject2 = PublishSubject() - sequenceOf(subject1, subject2) + _ = sequenceOf(subject1, subject2) .merge(maxConcurrent: 2) .subscribe { print($0) diff --git a/Rx.playground/Pages/Conditional_and_Boolean_Operators.xcplaygroundpage/Contents.swift b/Rx.playground/Pages/Conditional_and_Boolean_Operators.xcplaygroundpage/Contents.swift index 03398d0c..9da1d2ec 100644 --- a/Rx.playground/Pages/Conditional_and_Boolean_Operators.xcplaygroundpage/Contents.swift +++ b/Rx.playground/Pages/Conditional_and_Boolean_Operators.xcplaygroundpage/Contents.swift @@ -23,7 +23,7 @@ example("takeUntil") { let originalSequence = PublishSubject() let whenThisSendsNextWorldStops = PublishSubject() - originalSequence + _ = originalSequence .takeUntil(whenThisSendsNextWorldStops) .subscribe { print($0) @@ -53,7 +53,7 @@ example("takeWhile") { let sequence = PublishSubject() - sequence + _ = sequence .takeWhile { int in int < 4 } diff --git a/Rx.playground/Pages/Connectable_Observable_Operators.xcplaygroundpage/Contents.swift b/Rx.playground/Pages/Connectable_Observable_Operators.xcplaygroundpage/Contents.swift index 6d965bb3..c366e4a2 100644 --- a/Rx.playground/Pages/Connectable_Observable_Operators.xcplaygroundpage/Contents.swift +++ b/Rx.playground/Pages/Connectable_Observable_Operators.xcplaygroundpage/Contents.swift @@ -14,13 +14,13 @@ func sampleWithoutConnectableOperators() { let int1 = interval(1, MainScheduler.sharedInstance) - int1 + _ = int1 .subscribe { print("first subscription \($0)") } delay(5) { - int1 + _ = int1 .subscribe { print("second subscription \($0)") } @@ -43,7 +43,7 @@ func sampleWithMulticast() { let subject1 = PublishSubject() - subject1 + _ = subject1 .subscribe { print("Subject \($0)") } @@ -51,7 +51,7 @@ func sampleWithMulticast() { let int1 = interval(1, MainScheduler.sharedInstance) .multicast(subject1) - int1 + _ = int1 .subscribe { print("first subscription \($0)") } @@ -61,14 +61,14 @@ func sampleWithMulticast() { } delay(4) { - int1 + _ = int1 .subscribe { print("second subscription \($0)") } } delay(6) { - int1 + _ = int1 .subscribe { print("third subscription \($0)") } @@ -94,7 +94,7 @@ func sampleWithReplayBuffer0() { let int1 = interval(1, MainScheduler.sharedInstance) .replay(0) - int1 + _ = int1 .subscribe { print("first subscription \($0)") } @@ -104,14 +104,14 @@ func sampleWithReplayBuffer0() { } delay(4) { - int1 + _ = int1 .subscribe { print("second subscription \($0)") } } delay(6) { - int1 + _ = int1 .subscribe { print("third subscription \($0)") } @@ -129,7 +129,7 @@ func sampleWithReplayBuffer2() { let int1 = interval(1, MainScheduler.sharedInstance) .replay(2) - int1 + _ = int1 .subscribe { print("first subscription \($0)") } @@ -139,14 +139,14 @@ func sampleWithReplayBuffer2() { } delay(4) { - int1 + _ = int1 .subscribe { print("second subscription \($0)") } } delay(6) { - int1 + _ = int1 .subscribe { print("third subscription \($0)") } @@ -172,7 +172,7 @@ func sampleWithPublish() { let int1 = interval(1, MainScheduler.sharedInstance) .publish() - int1 + _ = int1 .subscribe { print("first subscription \($0)") } @@ -182,14 +182,14 @@ func sampleWithPublish() { } delay(4) { - int1 + _ = int1 .subscribe { print("second subscription \($0)") } } delay(6) { - int1 + _ = int1 .subscribe { print("third subscription \($0)") } diff --git a/Rx.playground/Pages/Error_Handling_Operators.xcplaygroundpage/Contents.swift b/Rx.playground/Pages/Error_Handling_Operators.xcplaygroundpage/Contents.swift index f6099112..59f10a58 100644 --- a/Rx.playground/Pages/Error_Handling_Operators.xcplaygroundpage/Contents.swift +++ b/Rx.playground/Pages/Error_Handling_Operators.xcplaygroundpage/Contents.swift @@ -21,7 +21,7 @@ example("catchError 1") { let sequenceThatFails = PublishSubject() let recoverySequence = sequenceOf(100, 200, 300, 400) - sequenceThatFails + _ = sequenceThatFails .catchError { error in return recoverySequence } @@ -40,7 +40,7 @@ example("catchError 1") { example("catchError 2") { let sequenceThatFails = PublishSubject() - sequenceThatFails + _ = sequenceThatFails .catchErrorJustReturn(100) .subscribe { print($0) @@ -83,7 +83,7 @@ example("retry") { return NopDisposable.instance } - funnyLookingSequence + _ = funnyLookingSequence .retry() .subscribe { print($0) diff --git a/Rx.playground/Pages/Introduction.xcplaygroundpage/Contents.swift b/Rx.playground/Pages/Introduction.xcplaygroundpage/Contents.swift index 69908bf4..9055e826 100644 --- a/Rx.playground/Pages/Introduction.xcplaygroundpage/Contents.swift +++ b/Rx.playground/Pages/Introduction.xcplaygroundpage/Contents.swift @@ -149,12 +149,12 @@ example("deferred") { } } - deferredSequence + _ = deferredSequence .subscribe { event in print(event) } - deferredSequence + _ = deferredSequence .subscribe { event in print(event) } diff --git a/Rx.playground/Pages/Mathematical_and_Aggregate_Operators.xcplaygroundpage/Contents.swift b/Rx.playground/Pages/Mathematical_and_Aggregate_Operators.xcplaygroundpage/Contents.swift index 64273983..8ee29c4c 100644 --- a/Rx.playground/Pages/Mathematical_and_Aggregate_Operators.xcplaygroundpage/Contents.swift +++ b/Rx.playground/Pages/Mathematical_and_Aggregate_Operators.xcplaygroundpage/Contents.swift @@ -65,7 +65,7 @@ This function will perform a function on each element in the sequence until it i */ example("reduce") { - sequenceOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) + _ = sequenceOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) .reduce(0, +) .subscribe { print($0) diff --git a/Rx.playground/Pages/Observable_Utility_Operators.xcplaygroundpage/Contents.swift b/Rx.playground/Pages/Observable_Utility_Operators.xcplaygroundpage/Contents.swift index 4df8d1ad..3010400c 100644 --- a/Rx.playground/Pages/Observable_Utility_Operators.xcplaygroundpage/Contents.swift +++ b/Rx.playground/Pages/Observable_Utility_Operators.xcplaygroundpage/Contents.swift @@ -18,7 +18,7 @@ A toolbox of useful Operators for working with Observables. example("subscribe") { let sequenceOfInts = PublishSubject() - sequenceOfInts + _ = sequenceOfInts .subscribe { print($0) } @@ -40,7 +40,7 @@ There are several variants of the `subscribe` operator. example("subscribeNext") { let sequenceOfInts = PublishSubject() - sequenceOfInts + _ = sequenceOfInts .subscribeNext { print($0) } @@ -58,7 +58,7 @@ example("subscribeNext") { example("subscribeCompleted") { let sequenceOfInts = PublishSubject() - sequenceOfInts + _ = sequenceOfInts .subscribeCompleted { print("It's completed") } @@ -76,7 +76,7 @@ example("subscribeCompleted") { example("subscribeError") { let sequenceOfInts = PublishSubject() - sequenceOfInts + _ = sequenceOfInts .subscribeError { error in print(error) } @@ -98,7 +98,7 @@ register an action to take upon a variety of Observable lifecycle events example("doOn") { let sequenceOfInts = PublishSubject() - sequenceOfInts + _ = sequenceOfInts .doOn { print("Intercepted event \($0)") } diff --git a/Rx.playground/Pages/Subjects.xcplaygroundpage/Contents.swift b/Rx.playground/Pages/Subjects.xcplaygroundpage/Contents.swift index 0a5b5753..c9f667ee 100644 --- a/Rx.playground/Pages/Subjects.xcplaygroundpage/Contents.swift +++ b/Rx.playground/Pages/Subjects.xcplaygroundpage/Contents.swift @@ -8,7 +8,7 @@ A Subject is a sort of bridge or proxy that is available in some implementations */ func writeSequenceToConsole(name: String, sequence: O) { - sequence + _ = sequence .subscribe { e in print("Subscription: \(name), event: \(e)") } diff --git a/Rx.playground/Pages/Transforming_Observables.xcplaygroundpage/Contents.swift b/Rx.playground/Pages/Transforming_Observables.xcplaygroundpage/Contents.swift index 60ed7d24..92f02385 100644 --- a/Rx.playground/Pages/Transforming_Observables.xcplaygroundpage/Contents.swift +++ b/Rx.playground/Pages/Transforming_Observables.xcplaygroundpage/Contents.swift @@ -21,7 +21,7 @@ Transform the items emitted by an Observable by applying a function to each item example("map") { let originalSequence = sequenceOf(Character("A"), Character("B"), Character("C")) - originalSequence + _ = originalSequence .map { char in char.hashValue } @@ -43,7 +43,7 @@ example("flatMap") { let sequenceString = sequenceOf("A", "B", "C", "D", "E", "F", "--") - sequenceInt + _ = sequenceInt .flatMap { int in sequenceString } @@ -65,7 +65,7 @@ Apply a function to each item emitted by an Observable, sequentially, and emit e example("scan") { let sequenceToSum = sequenceOf(0, 1, 2, 3, 4, 5) - sequenceToSum + _ = sequenceToSum .scan(0) { acum, elem in acum + elem } From 5b380a386d9e961bde98c1a2416fef56ee910b0d Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Thu, 29 Oct 2015 20:35:27 +0100 Subject: [PATCH 111/210] Fixes casing in documentation for `elementAt`. --- Documentation/API.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/API.md b/Documentation/API.md index 04a15b94..a35f98c9 100644 --- a/Documentation/API.md +++ b/Documentation/API.md @@ -34,7 +34,7 @@ Operators are stateless by default. #### Filtering Observables * [`debounce` / `throttle`](http://reactivex.io/documentation/operators/debounce.html) * [`distinctUntilChanged`](http://reactivex.io/documentation/operators/distinct.html) - * [`ElementAt`](http://reactivex.io/documentation/operators/elementat.html) + * [`elementAt`](http://reactivex.io/documentation/operators/elementat.html) * [`filter` / `where`](http://reactivex.io/documentation/operators/filter.html) * [`sample`](http://reactivex.io/documentation/operators/sample.html) * [`skip`](http://reactivex.io/documentation/operators/skip.html) @@ -441,6 +441,6 @@ extension NSTextField { public var rx_delegate: DelegateProxy {} public var rx_text: ControlProperty {} - + } ``` From a701d79a0fdfff794123493021e3d742491317c9 Mon Sep 17 00:00:00 2001 From: David Potter Date: Thu, 29 Oct 2015 15:16:56 -0700 Subject: [PATCH 112/210] Fix warnings due to use in app extensions and to Xcode updates. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Xcode gives two warnings when including the Rx project and building in an app. 1. The first is due to use in a component that will be used in an extension. Xcode displays the message “linking against dylib not safe for use in application extensions”. To configure an app extension target to use an embedded framework, set the target’s “Require Only App-Extension-Safe API” build setting to Yes. https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html 2. The second is due to changes in Xcode that want to update the project file to comply with the latest guidelines. Clicking the warning brings up a prompt to modify the project. --- Rx.xcodeproj/project.pbxproj | 44 ++++++++++++++++++++++++++++++++---- RxBlocking/Info.plist | 2 +- RxCocoa/Info.plist | 2 +- RxSwift/Info.plist | 2 +- 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index 1363eb84..1e5b0745 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -1836,7 +1836,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0700; + LastUpgradeCheck = 0710; ORGANIZATIONNAME = "Krunoslav Zaher"; TargetAttributes = { C8A56AD61AD7424700B4673B = { @@ -2805,6 +2805,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxCocoa/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxCocoa; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -2821,6 +2822,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxCocoa/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxCocoa; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -2837,6 +2839,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxCocoa/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxCocoa; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -2855,6 +2858,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxCocoa/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxCocoa; SDKROOT = macosx; SKIP_INSTALL = YES; @@ -2873,6 +2877,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxCocoa/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxCocoa; SDKROOT = macosx; SKIP_INSTALL = YES; @@ -2891,6 +2896,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxCocoa/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxCocoa; SDKROOT = macosx; SKIP_INSTALL = YES; @@ -2907,6 +2913,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxBlocking/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxBlocking; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -2923,6 +2930,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxBlocking/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxBlocking; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -2939,6 +2947,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxBlocking/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxBlocking; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -2957,6 +2966,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxBlocking/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxBlocking; SDKROOT = macosx; SKIP_INSTALL = YES; @@ -2975,6 +2985,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxBlocking/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxBlocking; SDKROOT = macosx; SKIP_INSTALL = YES; @@ -2993,6 +3004,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxBlocking/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxBlocking; SDKROOT = macosx; SKIP_INSTALL = YES; @@ -3003,6 +3015,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + APPLICATION_EXTENSION_API_ONLY = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -3056,6 +3069,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxSwift; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -3074,6 +3088,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxSwift/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxSwift; SDKROOT = macosx; SKIP_INSTALL = YES; @@ -3092,6 +3107,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxSwift/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxSwift; SDKROOT = macosx; SKIP_INSTALL = YES; @@ -3110,6 +3126,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxSwift/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxSwift; SDKROOT = macosx; SKIP_INSTALL = YES; @@ -3120,6 +3137,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + APPLICATION_EXTENSION_API_ONLY = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -3173,6 +3191,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + APPLICATION_EXTENSION_API_ONLY = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -3224,6 +3243,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxSwift; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -3241,6 +3261,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxSwift; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -3258,6 +3279,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxSwift; SDKROOT = watchos; SKIP_INSTALL = YES; @@ -3276,6 +3298,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxSwift; SDKROOT = watchos; SKIP_INSTALL = YES; @@ -3294,6 +3317,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxSwift; SDKROOT = watchos; SKIP_INSTALL = YES; @@ -3312,6 +3336,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxCocoa; SDKROOT = watchos; SKIP_INSTALL = YES; @@ -3330,6 +3355,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxCocoa; SDKROOT = watchos; SKIP_INSTALL = YES; @@ -3348,6 +3374,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxCocoa; SDKROOT = watchos; SKIP_INSTALL = YES; @@ -3366,6 +3393,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxBlocking; SDKROOT = watchos; SKIP_INSTALL = YES; @@ -3384,6 +3412,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxBlocking; SDKROOT = watchos; SKIP_INSTALL = YES; @@ -3402,6 +3431,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxBlocking; SDKROOT = watchos; SKIP_INSTALL = YES; @@ -3423,6 +3453,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxCocoa/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxCocoa; SDKROOT = appletvos; SKIP_INSTALL = YES; @@ -3441,6 +3472,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxCocoa/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxCocoa; SDKROOT = appletvos; SKIP_INSTALL = YES; @@ -3459,6 +3491,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxCocoa/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxCocoa; SDKROOT = appletvos; SKIP_INSTALL = YES; @@ -3482,7 +3515,7 @@ INFOPLIST_FILE = RxSwift/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = ""; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxSwift; SDKROOT = appletvos; SKIP_INSTALL = YES; @@ -3501,7 +3534,7 @@ INFOPLIST_FILE = RxSwift/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = ""; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxSwift; SDKROOT = appletvos; SKIP_INSTALL = YES; @@ -3520,7 +3553,7 @@ INFOPLIST_FILE = RxSwift/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = ""; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxSwift; SDKROOT = appletvos; SKIP_INSTALL = YES; @@ -3539,6 +3572,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxBlocking/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxBlocking; SDKROOT = appletvos; SKIP_INSTALL = YES; @@ -3557,6 +3591,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxBlocking/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxBlocking; SDKROOT = appletvos; SKIP_INSTALL = YES; @@ -3575,6 +3610,7 @@ INFOPLIST_FILE = "$(SRCROOT)/RxBlocking/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = RxBlocking; SDKROOT = appletvos; SKIP_INSTALL = YES; diff --git a/RxBlocking/Info.plist b/RxBlocking/Info.plist index c1620d0a..d3de8eef 100644 --- a/RxBlocking/Info.plist +++ b/RxBlocking/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/RxCocoa/Info.plist b/RxCocoa/Info.plist index c1620d0a..d3de8eef 100644 --- a/RxCocoa/Info.plist +++ b/RxCocoa/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/RxSwift/Info.plist b/RxSwift/Info.plist index c1620d0a..d3de8eef 100644 --- a/RxSwift/Info.plist +++ b/RxSwift/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - Krunoslav-Zaher.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName From 662d4dc549429a496ec3b2e4e685bd2b73be1de4 Mon Sep 17 00:00:00 2001 From: Junior B Date: Sun, 25 Oct 2015 10:45:08 +0100 Subject: [PATCH 113/210] Port of retryWhen tests from RxJS --- .../Implementations/RetryWhen.swift | 39 ++- .../Tests/Observable+SingleTest.swift | 289 ++++++++++++++++-- 2 files changed, 297 insertions(+), 31 deletions(-) diff --git a/RxSwift/Observables/Implementations/RetryWhen.swift b/RxSwift/Observables/Implementations/RetryWhen.swift index 5ee185b8..c32f1a93 100644 --- a/RxSwift/Observables/Implementations/RetryWhen.swift +++ b/RxSwift/Observables/Implementations/RetryWhen.swift @@ -23,6 +23,8 @@ class RetryTriggerSink? - - let handlerSubscription = SingleAssignmentDisposable() + let errorSubject = PublishSubject() + let handler: Observable + let handlerSubscription = SerialDisposable() init(parent: Parent, observer: O, cancel: Disposable) { self.parent = parent + self.handler = parent.notificationHandler(errorSubject.asObservable()) super.init(observer: observer, cancel: cancel) } - override func on(event: Event) { + override func on(event: Event) { + guard lastError == nil else { + return + } + switch event { case .Next: observer?.on(event) case .Error(let error): lock.performLocked() { - if errorSubject == nil { - errorSubject = BehaviorSubject(value: error as! Error) - let notifier = parent.notificationHandler(errorSubject!.asObservable()) - handlerSubscription.disposable = notifier.subscribeSafe(RetryTriggerSink(parent: self)) - } else { - errorSubject?.on(.Next(error as! Error)) - } - self.lastError = error + handlerSubscription.disposable = handler.subscribeSafe(RetryTriggerSink(parent: self)) + errorSubject.on(.Next(error as! Error)) } case .Completed: observer?.on(event) + handlerSubscription.dispose() dispose() } } @@ -81,6 +83,7 @@ class RetryWhenSequenceSink) -> S.Generator? { + override func extract(observable: Observable) -> S.Generator? { if let onError = observable as? RetryWhenSequence { return onError.sources.generate() } @@ -101,6 +104,14 @@ class RetryWhenSequenceSink Disposable { + _generators.append(sources) + + scheduleMoveNext() + + return handlerSubscription + } } class RetryWhenSequence : Producer { diff --git a/RxTests/RxSwiftTests/Tests/Observable+SingleTest.swift b/RxTests/RxSwiftTests/Tests/Observable+SingleTest.swift index f532cc13..a9c73511 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+SingleTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+SingleTest.swift @@ -767,41 +767,295 @@ extension ObservableSingleTest { ]) } - func testRetryWhen_Basic() { + + func testRetryWhen_Never() { let scheduler = TestScheduler(initialClock: 0) - let xs = scheduler.createColdObservable([ - next(5, 1), - next(10, 2), - error(20, testError) + let xs = scheduler.createHotObservable([ + next(150, 1), + completed(250) + ]) + + let empty = scheduler.createHotObservable([ + next(150, 1), + completed(210) ]) let res = scheduler.start(300) { xs.retryWhen({ (errors: Observable) in - return errors.delaySubscription(30, scheduler) - }).take(6) + return empty.asObservable() + }) } let correct: [Recorded] = [ - next(205, 1), - next(210, 2), - next(255, 1), - next(260, 2), - next(275, 1), - next(280, 2), - completed(280) + completed(250) ] XCTAssertEqual(res.messages, correct) XCTAssertEqual(xs.subscriptions, [ - Subscription(200, 250), - Subscription(250, 270), - Subscription(270, 280) + Subscription(200, 250) ]) } + func testRetryWhen_ObservableNever() { + + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5), + error(250, testError) + ]) + + let never = scheduler.createHotObservable([ + next(150, 1) + ]) + + let res = scheduler.start() { + xs.retryWhen({ (errors: Observable) in + return never.asObservable() + }) + } + + let correct: [Recorded] = [ + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5) + ] + + XCTAssertEqual(res.messages, correct) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 1000) + ]) + } + + func testRetryWhen_ObservableNeverComplete() { + + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5), + completed(250) + ]) + + let never = scheduler.createHotObservable([ + next(150, 1) + ]) + + let res = scheduler.start() { + xs.retryWhen({ (errors: Observable) in + return never.asObservable() + }) + } + + let correct: [Recorded] = [ + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5), + completed(250) + ] + + XCTAssertEqual(res.messages, correct) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 250) + ]) + } + + func testRetryWhen_ObservableEmpty() { + + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createColdObservable([ + next(100, 1), + next(150, 2), + next(200, 3), + completed(250) + ]) + + let empty = scheduler.createHotObservable([ + next(150, 0), + completed(0) + ]) + + let res = scheduler.start() { + xs.retryWhen({ (errors: Observable) in + return empty.asObservable() + }) + } + + let correct: [Recorded] = [ + next(300, 1), + next(350, 2), + next(400, 3), + completed(450) + ] + + XCTAssertEqual(res.messages, correct) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 450) + ]) + } + + + func testRetryWhen_ObservableNextError() { + + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createColdObservable([ + next(10, 1), + next(20, 2), + error(30, testError), + completed(40) + ]) + + let res = scheduler.start(300) { + xs.retryWhen({ (errors: Observable) in + return errors.scan(0) { (var a, e) in + if ++a == 2 { + throw testError + } + return a + } + }) + } + + let correct: [Recorded] = [ + next(210, 1), + next(220, 2), + next(240, 1), + next(250, 2), + error(260, testError) + ] + + XCTAssertEqual(res.messages, correct) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 230), + Subscription(230, 260) + ]) + } + + + func testRetryWhen_ObservableComplete() { + + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createColdObservable([ + next(10, 1), + next(20, 2), + error(30, testError), + completed(40) + ]) + + let empty = scheduler.createHotObservable([ + next(150, 1), + completed(230) + ]) + + let res = scheduler.start() { + xs.retryWhen({ (errors: Observable) in + return empty.asObservable() + }) + } + + let correct: [Recorded] = [ + next(210, 1), + next(220, 2), + completed(230) + ] + + XCTAssertEqual(res.messages, correct) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 230) + ]) + } + + func testRetryWhen_ObservableNextComplete() { + + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createColdObservable([ + next(10, 1), + next(20, 2), + error(30, testError), + completed(40) + ]) + + let res = scheduler.start(300) { + xs.retryWhen({ (errors: Observable) in + return errors.scan(0) { (a, e) in + return a + 1 + }.takeWhile { (num: Int) -> Bool in + return num < 2 + } + }) + } + + let correct: [Recorded] = [ + next(210, 1), + next(220, 2), + next(240, 1), + next(250, 2), + completed(260) + ] + + XCTAssertEqual(res.messages, correct) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 230), + Subscription(230, 260) + ]) + } + + func testRetryWhen_ObservableInfinite() { + + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createColdObservable([ + next(10, 1), + next(20, 2), + error(30, testError), + completed(40) + ]) + + let never = scheduler.createHotObservable([ + next(150, 1) + ]) + + let res = scheduler.start() { + xs.retryWhen({ (errors: Observable) in + return never.asObservable() + }) + } + + let correct: [Recorded] = [ + next(210, 1), + next(220, 2) + ] + + XCTAssertEqual(res.messages, correct) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 1000) + ]) + } + + /* func testRetryWhen_Incremental_BackOff() { let scheduler = TestScheduler(initialClock: 0) @@ -840,6 +1094,7 @@ extension ObservableSingleTest { Subscription(510, 710) ]) } + */ } From e99e6a611439b2faad0d9820687a7cdd1da8338e Mon Sep 17 00:00:00 2001 From: Pierre-Marc Airoldi Date: Fri, 30 Oct 2015 13:13:21 -0400 Subject: [PATCH 114/210] update os x framework target version --- Rx.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index 1e5b0745..f23e198c 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -3047,7 +3047,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.9; MTL_ENABLE_DEBUG_INFO = NO; OTHER_SWIFT_FLAGS = "-D TRACE_RESOURCES"; SDKROOT = ""; @@ -3175,7 +3175,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.9; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; OTHER_SWIFT_FLAGS = "-D TRACE_RESOURCES -D DEBUG"; @@ -3222,7 +3222,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.9; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = ""; TARGETED_DEVICE_FAMILY = "1,2"; From 451c2be94e90946004000aa917a2375b20b581d6 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Fri, 30 Oct 2015 23:56:52 +0100 Subject: [PATCH 115/210] Adds additional `UITableView` and `UICollectionView` unit tests. --- RxCocoa/Common/DelegateProxy.swift | 2 +- RxCocoa/Common/DelegateProxyType.swift | 39 ++-- .../Proxies/RxScrollViewDelegateProxy.swift | 13 +- RxCocoa/iOS/UITableView+Rx.swift | 4 +- RxTests/PerformanceTests/main.swift | 3 + .../RxCocoaTests/Control+RxTests+UIKit.swift | 185 ++++++++++++++++++ RxTests/RxCocoaTests/Control+RxTests.swift | 8 +- 7 files changed, 225 insertions(+), 29 deletions(-) diff --git a/RxCocoa/Common/DelegateProxy.swift b/RxCocoa/Common/DelegateProxy.swift index fa06f37e..0962ef09 100644 --- a/RxCocoa/Common/DelegateProxy.swift +++ b/RxCocoa/Common/DelegateProxy.swift @@ -23,7 +23,7 @@ public class DelegateProxy : _RXDelegateProxy { private var subjectsForSelector = [Selector: PublishSubject<[AnyObject]>]() - unowned let parentObject: AnyObject + weak var parentObject: AnyObject? /** Initializes new instance. diff --git a/RxCocoa/Common/DelegateProxyType.swift b/RxCocoa/Common/DelegateProxyType.swift index 5b7b77fa..f703c7fe 100644 --- a/RxCocoa/Common/DelegateProxyType.swift +++ b/RxCocoa/Common/DelegateProxyType.swift @@ -224,25 +224,28 @@ extension ObservableType { let proxy: P = proxyForObject(object) let disposable = installDelegate(proxy, delegate: dataSource, retainDelegate: retainDataSource, onProxyForObject: object) - // we should never let the subscriber to complete because it should retain data source - let source = sequenceOf(self.asObservable(), never()) as Observable> - let subscription = source.concat().subscribe { (event: Event) in - MainScheduler.ensureExecutingOnScheduler() - - assert(proxy === P.currentDelegateFor(object), "Proxy changed from the time it was first set.\nOriginal: \(proxy)\nExisting: \(P.currentDelegateFor(object))") - - binding(proxy, event) - - switch event { - case .Error(let error): - bindingErrorToInterface(error) - disposable.dispose() - case .Completed: - disposable.dispose() - default: - break + let subscription = self.asObservable() + // source can't ever end, otherwise it will release the subscriber + .concat(never()) + .subscribe { [weak object] (event: Event) in + MainScheduler.ensureExecutingOnScheduler() + + if let object = object { + assert(proxy === P.currentDelegateFor(object), "Proxy changed from the time it was first set.\nOriginal: \(proxy)\nExisting: \(P.currentDelegateFor(object))") + } + + binding(proxy, event) + + switch event { + case .Error(let error): + bindingErrorToInterface(error) + disposable.dispose() + case .Completed: + disposable.dispose() + default: + break + } } - } return StableCompositeDisposable.create(subscription, disposable) } diff --git a/RxCocoa/iOS/Proxies/RxScrollViewDelegateProxy.swift b/RxCocoa/iOS/Proxies/RxScrollViewDelegateProxy.swift index 4c429b46..4f8275bd 100644 --- a/RxCocoa/iOS/Proxies/RxScrollViewDelegateProxy.swift +++ b/RxCocoa/iOS/Proxies/RxScrollViewDelegateProxy.swift @@ -20,19 +20,20 @@ class RxScrollViewDelegateProxy : DelegateProxy , DelegateProxyType { private var _contentOffsetSubject: ReplaySubject? - unowned let scrollView: UIScrollView + weak var scrollView: UIScrollView? var contentOffsetSubject: Observable { if _contentOffsetSubject == nil { - _contentOffsetSubject = ReplaySubject.create(bufferSize: 1) - _contentOffsetSubject!.on(.Next(self.scrollView.contentOffset)) + let replaySubject = ReplaySubject.create(bufferSize: 1) + _contentOffsetSubject = replaySubject + replaySubject.on(.Next(self.scrollView?.contentOffset ?? CGPointZero)) } return _contentOffsetSubject! } required init(parentObject: AnyObject) { - self.scrollView = parentObject as! UIScrollView + self.scrollView = (parentObject as! UIScrollView) super.init(parentObject: parentObject) } @@ -40,7 +41,7 @@ class RxScrollViewDelegateProxy : DelegateProxy func scrollViewDidScroll(scrollView: UIScrollView) { if let contentOffset = _contentOffsetSubject { - contentOffset.on(.Next(self.scrollView.contentOffset)) + contentOffset.on(.Next(scrollView.contentOffset)) } self._forwardToDelegate?.scrollViewDidScroll?(scrollView) } @@ -48,7 +49,7 @@ class RxScrollViewDelegateProxy : DelegateProxy // delegate proxy override class func createProxyForObject(object: AnyObject) -> AnyObject { - let scrollView = object as! UIScrollView + let scrollView = (object as! UIScrollView) return castOrFatalError(scrollView.rx_createDelegateProxy()) } diff --git a/RxCocoa/iOS/UITableView+Rx.swift b/RxCocoa/iOS/UITableView+Rx.swift index 4c82b549..898300af 100644 --- a/RxCocoa/iOS/UITableView+Rx.swift +++ b/RxCocoa/iOS/UITableView+Rx.swift @@ -54,7 +54,7 @@ extension UITableView { return cell } - return self.rx_itemsWithDataSource(dataSource)(source: source) + return self.rx_itemsWithDataSource(dataSource)(source: source) } /** @@ -202,7 +202,7 @@ extension UITableView { Synchronous helper method for retrieving a model at indexPath through a reactive data source */ public func rx_modelAtIndexPath(indexPath: NSIndexPath) throws -> T { - let dataSource: RxCollectionViewReactiveArrayDataSource = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_items*` methods was used.") + let dataSource: RxTableViewReactiveArrayDataSource = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_items*` methods was used.") guard let element = dataSource.modelAtIndex(indexPath.item) else { throw rxError(.InvalidOperation, "Items not set yet.") diff --git a/RxTests/PerformanceTests/main.swift b/RxTests/PerformanceTests/main.swift index 57d74db5..8d5f179c 100644 --- a/RxTests/PerformanceTests/main.swift +++ b/RxTests/PerformanceTests/main.swift @@ -89,6 +89,9 @@ compareTwoImplementations(first: { publishSubject .shareReplay(1) + //.map { $0 } + //.filter { _ in true }// ){ x, _ in x } + //.map { $0 } .subscribeNext { _ in } diff --git a/RxTests/RxCocoaTests/Control+RxTests+UIKit.swift b/RxTests/RxCocoaTests/Control+RxTests+UIKit.swift index 82e91e9c..ceb6dff7 100644 --- a/RxTests/RxCocoaTests/Control+RxTests+UIKit.swift +++ b/RxTests/RxCocoaTests/Control+RxTests+UIKit.swift @@ -54,9 +54,104 @@ extension ControlTests { func testCollectionView_DelegateEventCompletesOnDealloc() { let layout = UICollectionViewFlowLayout() let createView: () -> UICollectionView = { UICollectionView(frame: CGRectMake(0, 0, 1, 1), collectionViewLayout: layout) } + ensureEventDeallocated(createView) { (view: UICollectionView) in view.rx_itemSelected } ensureEventDeallocated(createView) { (view: UICollectionView) in view.rx_modelSelected() as ControlEvent } } + + func testCollectionView_DelegateEventCompletesOnDealloc1() { + let items: Observable<[Int]> = just([1, 2, 3]) + + let layout = UICollectionViewFlowLayout() + let createView: () -> (UICollectionView, Disposable) = { + let collectionView = UICollectionView(frame: CGRectMake(0, 0, 1, 1), collectionViewLayout: layout) + let s = items.bindTo(collectionView.rx_itemsWithCellFactory) { (cv, index: Int, item: Int) -> UICollectionViewCell in + return UICollectionViewCell(frame: CGRectMake(1, 1, 1, 1)) + } + + return (collectionView, s) + } + ensureEventDeallocated(createView) { (view: UICollectionView) in view.rx_modelSelected() as ControlEvent } + } + + func testCollectionView_DelegateEventCompletesOnDealloc2() { + let items: Observable<[Int]> = just([1, 2, 3]) + + let layout = UICollectionViewFlowLayout() + + let createView: () -> (UICollectionView, Disposable) = { + let collectionView = UICollectionView(frame: CGRectMake(0, 0, 1, 1), collectionViewLayout: layout) + collectionView.registerClass(NSClassFromString("UICollectionViewCell"), forCellWithReuseIdentifier: "a") + let s = items.bindTo(collectionView.rx_itemsWithCellIdentifier("a")) { (index: Int, item: Int, cell) in + + } + + return (collectionView, s) + } + ensureEventDeallocated(createView) { (view: UICollectionView) in view.rx_modelSelected() as ControlEvent } + } + + func testCollectionView_ModelSelected1() { + let items: Observable<[Int]> = just([1, 2, 3]) + + let layout = UICollectionViewFlowLayout() + + let createView: () -> (UICollectionView, Disposable) = { + let collectionView = UICollectionView(frame: CGRectMake(0, 0, 1, 1), collectionViewLayout: layout) + let s = items.bindTo(collectionView.rx_itemsWithCellFactory) { (cv, index: Int, item: Int) -> UICollectionViewCell in + return UICollectionViewCell(frame: CGRectMake(1, 1, 1, 1)) + } + + return (collectionView, s) + } + + let (collectionView, dataSourceSubscription) = createView() + + var selectedItem: Int? = nil + + let s = collectionView.rx_modelSelected() + .subscribeNext { (item: Int) in + selectedItem = item + } + + collectionView.delegate!.collectionView!(collectionView, didSelectItemAtIndexPath: NSIndexPath(forRow: 1, inSection: 0)) + + XCTAssertEqual(selectedItem, 2) + + dataSourceSubscription.dispose() + s.dispose() + } + + func testCollectionView_ModelSelected2() { + let items: Observable<[Int]> = just([1, 2, 3]) + + let layout = UICollectionViewFlowLayout() + let createView: () -> (UICollectionView, Disposable) = { + let collectionView = UICollectionView(frame: CGRectMake(0, 0, 1, 1), collectionViewLayout: layout) + collectionView.registerClass(NSClassFromString("UICollectionViewCell"), forCellWithReuseIdentifier: "a") + let dataSourceSubscription = items.bindTo(collectionView.rx_itemsWithCellIdentifier("a")) { (index: Int, item: Int, cell) in + + } + + return (collectionView, dataSourceSubscription) + + } + let (collectionView, dataSourceSubscription) = createView() + + var selectedItem: Int? = nil + + let s = collectionView.rx_modelSelected() + .subscribeNext { (item: Int) in + selectedItem = item + } + + collectionView.delegate!.collectionView!(collectionView, didSelectItemAtIndexPath: NSIndexPath(forRow: 1, inSection: 0)) + + XCTAssertEqual(selectedItem, 2) + + s.dispose() + dataSourceSubscription.dispose() + } } @@ -64,11 +159,101 @@ extension ControlTests { extension ControlTests { func testTableView_DelegateEventCompletesOnDealloc() { let createView: () -> UITableView = { UITableView(frame: CGRectMake(0, 0, 1, 1)) } + ensureEventDeallocated(createView) { (view: UITableView) in view.rx_itemSelected } ensureEventDeallocated(createView) { (view: UITableView) in view.rx_modelSelected() as ControlEvent } ensureEventDeallocated(createView) { (view: UITableView) in view.rx_itemDeleted } ensureEventDeallocated(createView) { (view: UITableView) in view.rx_itemMoved } ensureEventDeallocated(createView) { (view: UITableView) in view.rx_itemInserted } + ensureEventDeallocated(createView) { (view: UITableView) in view.rx_modelSelected() as ControlEvent } + } + + func testTableView_DelegateEventCompletesOnDealloc1() { + let items: Observable<[Int]> = just([1, 2, 3]) + + let createView: () -> (UITableView, Disposable) = { + let tableView = UITableView(frame: CGRectMake(0, 0, 1, 1)) + let dataSourceSubscription = items.bindTo(tableView.rx_itemsWithCellFactory) { (tv, index: Int, item: Int) -> UITableViewCell in + return UITableViewCell(style: .Default, reuseIdentifier: "Identity") + } + + return (tableView, dataSourceSubscription) + } + ensureEventDeallocated(createView) { (view: UITableView) in view.rx_modelSelected() as ControlEvent } + } + + func testTableView_DelegateEventCompletesOnDealloc2() { + let items: Observable<[Int]> = just([1, 2, 3]) + + let createView: () -> (UITableView, Disposable) = { + let tableView = UITableView(frame: CGRectMake(0, 0, 1, 1)) + tableView.registerClass(NSClassFromString("UITableViewCell"), forCellReuseIdentifier: "a") + let dataSourceSubscription = items.bindTo(tableView.rx_itemsWithCellIdentifier("a")) { (index: Int, item: Int, cell) in + + } + + return (tableView, dataSourceSubscription) + } + ensureEventDeallocated(createView) { (view: UITableView) in view.rx_modelSelected() as ControlEvent } + } + + func testTableView_ModelSelected1() { + let items: Observable<[Int]> = just([1, 2, 3]) + + let createView: () -> (UITableView, Disposable) = { + let tableView = UITableView(frame: CGRectMake(0, 0, 1, 1)) + let dataSourceSubscription = items.bindTo(tableView.rx_itemsWithCellFactory) { (tv, index: Int, item: Int) -> UITableViewCell in + return UITableViewCell(style: .Default, reuseIdentifier: "Identity") + } + + return (tableView, dataSourceSubscription) + } + + let (tableView, dataSourceSubscription) = createView() + + var selectedItem: Int? = nil + + let s = tableView.rx_modelSelected() + .subscribeNext { (item: Int) in + selectedItem = item + } + + tableView.delegate!.tableView!(tableView, didSelectRowAtIndexPath: NSIndexPath(forRow: 1, inSection: 0)) + + XCTAssertEqual(selectedItem, 2) + + dataSourceSubscription.dispose() + s.dispose() + } + + func testTableView_ModelSelected2() { + let items: Observable<[Int]> = just([1, 2, 3]) + + let createView: () -> (UITableView, Disposable) = { + let tableView = UITableView(frame: CGRectMake(0, 0, 1, 1)) + tableView.registerClass(NSClassFromString("UITableViewCell"), forCellReuseIdentifier: "a") + let dataSourceSubscription = items.bindTo(tableView.rx_itemsWithCellIdentifier("a")) { (index: Int, item: Int, cell) in + + } + + return (tableView, dataSourceSubscription) + } + + let (tableView, dataSourceSubscription) = createView() + + var selectedItem: Int? = nil + + let s = tableView.rx_modelSelected() + .subscribeNext { (item: Int) in + selectedItem = item + } + + tableView.delegate!.tableView!(tableView, didSelectRowAtIndexPath: NSIndexPath(forRow: 1, inSection: 0)) + + XCTAssertEqual(selectedItem, 2) + + dataSourceSubscription.dispose() + s.dispose() } } diff --git a/RxTests/RxCocoaTests/Control+RxTests.swift b/RxTests/RxCocoaTests/Control+RxTests.swift index a409c687..5b322d5b 100644 --- a/RxTests/RxCocoaTests/Control+RxTests.swift +++ b/RxTests/RxCocoaTests/Control+RxTests.swift @@ -49,11 +49,15 @@ class ControlTests : RxTest { } func ensureEventDeallocated(createControl: () -> C, _ eventSelector: C -> ControlEvent) { + return ensureEventDeallocated({ () -> (C, Disposable) in (createControl(), NopDisposable.instance) }, eventSelector) + } + + func ensureEventDeallocated(createControl: () -> (C, Disposable), _ eventSelector: C -> ControlEvent) { var completed = false var deallocated = false autoreleasepool { - var control: C! = createControl() + let (control, disposable) = createControl() let eventObservable = eventSelector(control) _ = eventObservable.subscribe(onNext: { n in @@ -66,7 +70,7 @@ class ControlTests : RxTest { deallocated = true } - control = nil + disposable.dispose() } From 64788e9e3ccae2f8759e10e8da858fe2328de2e6 Mon Sep 17 00:00:00 2001 From: Vodovozov Gleb Date: Thu, 22 Oct 2015 22:52:04 +0800 Subject: [PATCH 116/210] added reachabilty.swift https://github.com/ashleymills/Reachability.swift --- RxExample/RxExample.xcodeproj/project.pbxproj | 4 + .../RxExample/Services/Reachability.swift | 388 ++++++++++++++++++ 2 files changed, 392 insertions(+) create mode 100644 RxExample/RxExample/Services/Reachability.swift diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index 75817321..0364b29a 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 07E300091B149A2A00F00100 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E300081B149A2A00F00100 /* User.swift */; }; 07E3C2331B03605B0010338D /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E3C2321B03605B0010338D /* Dependencies.swift */; }; B1B7C3D01BE006870076934E /* TakeLast.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B7C3CF1BE006870076934E /* TakeLast.swift */; }; + B18F3BBC1BD92EC8000AAC79 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */; }; C803973A1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; }; C803973B1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; }; C80397491BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */; }; @@ -453,6 +454,7 @@ 07E300081B149A2A00F00100 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 07E3C2321B03605B0010338D /* Dependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Dependencies.swift; path = Examples/Dependencies.swift; sourceTree = ""; }; B1B7C3CF1BE006870076934E /* TakeLast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TakeLast.swift; sourceTree = ""; }; + B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = ""; }; C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = ""; }; C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubSearchRepositoriesAPI.swift; sourceTree = ""; }; C80DDE7A1BCDA952006A1832 /* SkipWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipWhile.swift; sourceTree = ""; }; @@ -872,6 +874,7 @@ C83367101AD029AE00C668A7 /* Services */ = { isa = PBXGroup; children = ( + B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */, C83367111AD029AE00C668A7 /* HtmlParsing.swift */, C83367121AD029AE00C668A7 /* ImageService.swift */, C8A2A2C71B4049E300F11F09 /* PseudoRandomGenerator.swift */, @@ -1878,6 +1881,7 @@ 075F13101B4E9D5A000D7861 /* APIWrappersViewController.swift in Sources */, C83367311AD029AE00C668A7 /* Wireframe.swift in Sources */, 07E300071B14995F00F00100 /* TableViewController.swift in Sources */, + B18F3BBC1BD92EC8000AAC79 /* Reachability.swift in Sources */, C859B9A41B45C5D900D012D7 /* PartialUpdatesViewController.swift in Sources */, 07E3C2331B03605B0010338D /* Dependencies.swift in Sources */, C84B913A1B8A282000C9CCCF /* RxTableViewSectionedReloadDataSource.swift in Sources */, diff --git a/RxExample/RxExample/Services/Reachability.swift b/RxExample/RxExample/Services/Reachability.swift new file mode 100644 index 00000000..22bb54cd --- /dev/null +++ b/RxExample/RxExample/Services/Reachability.swift @@ -0,0 +1,388 @@ +/* +Copyright (c) 2014, Ashley Mills +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +import SystemConfiguration +import Foundation + +enum ReachabilityError: ErrorType { + case FailedToCreateWithAddress(sockaddr_in) + case FailedToCreateWithHostname(String) + case UnableToSetCallback + case UnableToSetDispatchQueue +} + +public let ReachabilityChangedNotification = "ReachabilityChangedNotification" + +func callback(reachability:SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutablePointer) { + let reachability = Unmanaged.fromOpaque(COpaquePointer(info)).takeUnretainedValue() + + dispatch_async(dispatch_get_main_queue()) { + reachability.reachabilityChanged(flags) + } +} + + +public class Reachability: NSObject { + + public typealias NetworkReachable = (Reachability) -> () + public typealias NetworkUnreachable = (Reachability) -> () + + public enum NetworkStatus: CustomStringConvertible { + + case NotReachable, ReachableViaWiFi, ReachableViaWWAN + + public var description: String { + switch self { + case .ReachableViaWWAN: + return "Cellular" + case .ReachableViaWiFi: + return "WiFi" + case .NotReachable: + return "No Connection" + } + } + } + + // MARK: - *** Public properties *** + + public var whenReachable: NetworkReachable? + public var whenUnreachable: NetworkUnreachable? + public var reachableOnWWAN: Bool + public var notificationCenter = NSNotificationCenter.defaultCenter() + + public var currentReachabilityStatus: NetworkStatus { + if isReachable() { + if isReachableViaWiFi() { + return .ReachableViaWiFi + } + if isRunningOnDevice { + return .ReachableViaWWAN + } + } + + return .NotReachable + } + + public var currentReachabilityString: String { + return "\(currentReachabilityStatus)" + } + + // MARK: - *** Initialisation methods *** + + required public init(reachabilityRef: SCNetworkReachability) { + reachableOnWWAN = true + self.reachabilityRef = reachabilityRef + } + + public convenience init(hostname: String) throws { + + let nodename = (hostname as NSString).UTF8String + guard let ref = SCNetworkReachabilityCreateWithName(nil, nodename) else { throw ReachabilityError.FailedToCreateWithHostname(hostname) } + + self.init(reachabilityRef: ref) + } + + public class func reachabilityForInternetConnection() throws -> Reachability { + + var zeroAddress = sockaddr_in() + zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress)) + zeroAddress.sin_family = sa_family_t(AF_INET) + + guard let ref = withUnsafePointer(&zeroAddress, { + SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0)) + }) else { throw ReachabilityError.FailedToCreateWithAddress(zeroAddress) } + + return Reachability(reachabilityRef: ref) + } + + public class func reachabilityForLocalWiFi() throws -> Reachability { + + var localWifiAddress: sockaddr_in = sockaddr_in(sin_len: __uint8_t(0), sin_family: sa_family_t(0), sin_port: in_port_t(0), sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)) + localWifiAddress.sin_len = UInt8(sizeofValue(localWifiAddress)) + localWifiAddress.sin_family = sa_family_t(AF_INET) + + // IN_LINKLOCALNETNUM is defined in as 169.254.0.0 + let address: UInt32 = 0xA9FE0000 + localWifiAddress.sin_addr.s_addr = in_addr_t(address.bigEndian) + + guard let ref = withUnsafePointer(&localWifiAddress, { + SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0)) + }) else { throw ReachabilityError.FailedToCreateWithAddress(localWifiAddress) } + + return Reachability(reachabilityRef: ref) + } + + // MARK: - *** Notifier methods *** + public func startNotifier() throws { + + if notifierRunning { return } + + var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) + context.info = UnsafeMutablePointer(Unmanaged.passUnretained(self).toOpaque()) + + if !SCNetworkReachabilitySetCallback(reachabilityRef!, callback, &context) { + stopNotifier() + throw ReachabilityError.UnableToSetCallback + } + + if !SCNetworkReachabilitySetDispatchQueue(reachabilityRef!, reachabilitySerialQueue) { + stopNotifier() + throw ReachabilityError.UnableToSetDispatchQueue + } + + notifierRunning = true + } + + + public func stopNotifier() { + if let reachabilityRef = reachabilityRef { + SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil) + SCNetworkReachabilitySetDispatchQueue(reachabilityRef, nil) + } + notifierRunning = false + } + + // MARK: - *** Connection test methods *** + public func isReachable() -> Bool { + return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in + return self.isReachableWithFlags(flags) + }) + } + + public func isReachableViaWWAN() -> Bool { + + if isRunningOnDevice { + return isReachableWithTest() { flags -> Bool in + // Check we're REACHABLE + if self.isReachable(flags) { + + // Now, check we're on WWAN + if self.isOnWWAN(flags) { + return true + } + } + return false + } + } + return false + } + + public func isReachableViaWiFi() -> Bool { + + return isReachableWithTest() { flags -> Bool in + + // Check we're reachable + if self.isReachable(flags) { + + if self.isRunningOnDevice { + // Check we're NOT on WWAN + if self.isOnWWAN(flags) { + return false + } + } + return true + } + + return false + } + } + + // MARK: - *** Private methods *** + private var isRunningOnDevice: Bool = { + #if (arch(i386) || arch(x86_64)) && os(iOS) + return false + #else + return true + #endif + }() + + private var notifierRunning = false + private var reachabilityRef: SCNetworkReachability? + private let reachabilitySerialQueue = dispatch_queue_create("uk.co.ashleymills.reachability", DISPATCH_QUEUE_SERIAL) + + private func reachabilityChanged(flags: SCNetworkReachabilityFlags) { + if isReachableWithFlags(flags) { + if let block = whenReachable { + block(self) + } + } else { + if let block = whenUnreachable { + block(self) + } + } + + notificationCenter.postNotificationName(ReachabilityChangedNotification, object:self) + } + + private func isReachableWithFlags(flags: SCNetworkReachabilityFlags) -> Bool { + + let reachable = isReachable(flags) + + if !reachable { + return false + } + + if isConnectionRequiredOrTransient(flags) { + return false + } + + if isRunningOnDevice { + if isOnWWAN(flags) && !reachableOnWWAN { + // We don't want to connect when on 3G. + return false + } + } + + return true + } + + private func isReachableWithTest(test: (SCNetworkReachabilityFlags) -> (Bool)) -> Bool { + + if let reachabilityRef = reachabilityRef { + + var flags = SCNetworkReachabilityFlags(rawValue: 0) + let gotFlags = withUnsafeMutablePointer(&flags) { + SCNetworkReachabilityGetFlags(reachabilityRef, UnsafeMutablePointer($0)) + } + + if gotFlags { + return test(flags) + } + } + + return false + } + + // WWAN may be available, but not active until a connection has been established. + // WiFi may require a connection for VPN on Demand. + private func isConnectionRequired() -> Bool { + return connectionRequired() + } + + private func connectionRequired() -> Bool { + return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in + return self.isConnectionRequired(flags) + }) + } + + // Dynamic, on demand connection? + private func isConnectionOnDemand() -> Bool { + return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in + return self.isConnectionRequired(flags) && self.isConnectionOnTrafficOrDemand(flags) + }) + } + + // Is user intervention required? + private func isInterventionRequired() -> Bool { + return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in + return self.isConnectionRequired(flags) && self.isInterventionRequired(flags) + }) + } + + private func isOnWWAN(flags: SCNetworkReachabilityFlags) -> Bool { + #if os(iOS) + return flags.contains(.IsWWAN) + #else + return false + #endif + } + private func isReachable(flags: SCNetworkReachabilityFlags) -> Bool { + return flags.contains(.Reachable) + } + private func isConnectionRequired(flags: SCNetworkReachabilityFlags) -> Bool { + return flags.contains(.ConnectionRequired) + } + private func isInterventionRequired(flags: SCNetworkReachabilityFlags) -> Bool { + return flags.contains(.InterventionRequired) + } + private func isConnectionOnTraffic(flags: SCNetworkReachabilityFlags) -> Bool { + return flags.contains(.ConnectionOnTraffic) + } + private func isConnectionOnDemand(flags: SCNetworkReachabilityFlags) -> Bool { + return flags.contains(.ConnectionOnDemand) + } + func isConnectionOnTrafficOrDemand(flags: SCNetworkReachabilityFlags) -> Bool { + return !flags.intersect([.ConnectionOnTraffic, .ConnectionOnDemand]).isEmpty + } + private func isTransientConnection(flags: SCNetworkReachabilityFlags) -> Bool { + return flags.contains(.TransientConnection) + } + private func isLocalAddress(flags: SCNetworkReachabilityFlags) -> Bool { + return flags.contains(.IsLocalAddress) + } + private func isDirect(flags: SCNetworkReachabilityFlags) -> Bool { + return flags.contains(.IsDirect) + } + private func isConnectionRequiredOrTransient(flags: SCNetworkReachabilityFlags) -> Bool { + let testcase:SCNetworkReachabilityFlags = [.ConnectionRequired, .TransientConnection] + return flags.intersect(testcase) == testcase + } + + private var reachabilityFlags: SCNetworkReachabilityFlags { + if let reachabilityRef = reachabilityRef { + + var flags = SCNetworkReachabilityFlags(rawValue: 0) + let gotFlags = withUnsafeMutablePointer(&flags) { + SCNetworkReachabilityGetFlags(reachabilityRef, UnsafeMutablePointer($0)) + } + + if gotFlags { + return flags + } + } + + return [] + } + + override public var description: String { + + var W: String + if isRunningOnDevice { + W = isOnWWAN(reachabilityFlags) ? "W" : "-" + } else { + W = "X" + } + let R = isReachable(reachabilityFlags) ? "R" : "-" + let c = isConnectionRequired(reachabilityFlags) ? "c" : "-" + let t = isTransientConnection(reachabilityFlags) ? "t" : "-" + let i = isInterventionRequired(reachabilityFlags) ? "i" : "-" + let C = isConnectionOnTraffic(reachabilityFlags) ? "C" : "-" + let D = isConnectionOnDemand(reachabilityFlags) ? "D" : "-" + let l = isLocalAddress(reachabilityFlags) ? "l" : "-" + let d = isDirect(reachabilityFlags) ? "d" : "-" + + return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)" + } + + deinit { + stopNotifier() + + reachabilityRef = nil + whenReachable = nil + whenUnreachable = nil + } +} From e8cb6721b7e039af4de367e8b06e255c2dd1c54c Mon Sep 17 00:00:00 2001 From: Vodovozov Gleb Date: Fri, 23 Oct 2015 12:21:13 +0800 Subject: [PATCH 117/210] added Reachability+Rx - contains reactive wrapper --- RxExample/RxExample.xcodeproj/project.pbxproj | 14 ++++++- .../RxExample/Services/Reachability+Rx.swift | 39 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 RxExample/RxExample/Services/Reachability+Rx.swift diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index 0364b29a..fc535fc5 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -19,6 +19,11 @@ 07E3C2331B03605B0010338D /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E3C2321B03605B0010338D /* Dependencies.swift */; }; B1B7C3D01BE006870076934E /* TakeLast.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B7C3CF1BE006870076934E /* TakeLast.swift */; }; B18F3BBC1BD92EC8000AAC79 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */; }; + B18F3BBE1BD93C2E000AAC79 /* Reachability+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBD1BD93C2E000AAC79 /* Reachability+Rx.swift */; }; + B18F3BBF1BD93DFF000AAC79 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */; }; + B18F3BC01BD93DFF000AAC79 /* Reachability+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBD1BD93C2E000AAC79 /* Reachability+Rx.swift */; }; + B18F3BC11BD93E00000AAC79 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */; }; + B18F3BC21BD93E00000AAC79 /* Reachability+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBD1BD93C2E000AAC79 /* Reachability+Rx.swift */; }; C803973A1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; }; C803973B1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; }; C80397491BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */; }; @@ -455,6 +460,7 @@ 07E3C2321B03605B0010338D /* Dependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Dependencies.swift; path = Examples/Dependencies.swift; sourceTree = ""; }; B1B7C3CF1BE006870076934E /* TakeLast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TakeLast.swift; sourceTree = ""; }; B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = ""; }; + B18F3BBD1BD93C2E000AAC79 /* Reachability+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Reachability+Rx.swift"; sourceTree = ""; }; C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = ""; }; C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubSearchRepositoriesAPI.swift; sourceTree = ""; }; C80DDE7A1BCDA952006A1832 /* SkipWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipWhile.swift; sourceTree = ""; }; @@ -874,12 +880,13 @@ C83367101AD029AE00C668A7 /* Services */ = { isa = PBXGroup; children = ( - B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */, C83367111AD029AE00C668A7 /* HtmlParsing.swift */, C83367121AD029AE00C668A7 /* ImageService.swift */, C8A2A2C71B4049E300F11F09 /* PseudoRandomGenerator.swift */, C8A2A2CA1B404A1200F11F09 /* Randomizer.swift */, C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */, + B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */, + B18F3BBD1BD93C2E000AAC79 /* Reachability+Rx.swift */, ); path = Services; sourceTree = ""; @@ -1690,6 +1697,7 @@ C89464D61BC6C2B00055219D /* Producer.swift in Sources */, C894658A1BC6C2BC0055219D /* RxTableViewDataSourceProxy.swift in Sources */, C894656F1BC6C2BC0055219D /* DelegateProxyType.swift in Sources */, + B18F3BC21BD93E00000AAC79 /* Reachability+Rx.swift in Sources */, C89465721BC6C2BC0055219D /* ControlTarget.swift in Sources */, C89464EC1BC6C2B00055219D /* Observable+Binding.swift in Sources */, C8297E3A1B6CF905000589EA /* WikipediaSearchViewController.swift in Sources */, @@ -1759,6 +1767,7 @@ C89465861BC6C2BC0055219D /* RxCollectionViewDataSourceProxy.swift in Sources */, C89465981BC6C2BC0055219D /* UISearchBar+Rx.swift in Sources */, C89464E21BC6C2B00055219D /* Take.swift in Sources */, + B18F3BC11BD93E00000AAC79 /* Reachability.swift in Sources */, C89465901BC6C2BC0055219D /* UIButton+Rx.swift in Sources */, C89464DD1BC6C2B00055219D /* Sink.swift in Sources */, C89464FE1BC6C2B00055219D /* CurrentThreadScheduler.swift in Sources */, @@ -1865,6 +1874,7 @@ 0706E1961B14AF5100BA2D3A /* DetailViewController.swift in Sources */, C88C78991B4012A90061C5AB /* SectionModelType.swift in Sources */, C83367251AD029AE00C668A7 /* ImageService.swift in Sources */, + B18F3BBE1BD93C2E000AAC79 /* Reachability+Rx.swift in Sources */, C86E2F471AE5A0CA00C31024 /* WikipediaSearchResult.swift in Sources */, C80397491BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift in Sources */, C890A65A1AEBD28A00AFF7E6 /* GitHubAPI.swift in Sources */, @@ -1903,8 +1913,10 @@ C88BB8BD1B07E6C90064D411 /* SearchViewModel.swift in Sources */, C8E9D2AF1BD3FD960079D0DB /* ActivityIndicator.swift in Sources */, C88BB8BE1B07E6C90064D411 /* ImageService.swift in Sources */, + B18F3BC01BD93DFF000AAC79 /* Reachability+Rx.swift in Sources */, C88BB8BF1B07E6C90064D411 /* WikipediaSearchResult.swift in Sources */, C8DF92F61B0B43A4009BCF9A /* IntroductionExampleViewController.swift in Sources */, + B18F3BBF1BD93DFF000AAC79 /* Reachability.swift in Sources */, C88BB8C31B07E6C90064D411 /* Example.swift in Sources */, C88BB8C41B07E6C90064D411 /* ViewController.swift in Sources */, C88BB8C61B07E6C90064D411 /* Wireframe.swift in Sources */, diff --git a/RxExample/RxExample/Services/Reachability+Rx.swift b/RxExample/RxExample/Services/Reachability+Rx.swift new file mode 100644 index 00000000..36ce6805 --- /dev/null +++ b/RxExample/RxExample/Services/Reachability+Rx.swift @@ -0,0 +1,39 @@ +// +// Reachability+Rx.swift +// RxExample +// +// Created by Vodovozov Gleb on 22.10.2015. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import RxSwift + +public enum ReachabilityStatus{ + case Reachable,Unreachable +} + +extension Reachability{ + + /** + Reactive wrapper for reachability state changes + */ + public var rx_reachable: Observable { + + return create { observer in + + self.whenReachable = { reachability in + observer.on(.Next(.Reachable)) + } + self.whenUnreachable = { reachability in + observer.on(.Next(.Unreachable)) + } + do{ + try self.startNotifier() + }catch let error{ + observer.on(.Error(error)) + } + return NopDisposable.instance + } + + } +} \ No newline at end of file From ea628496f1451b607bc899dcb98adc5cc48c7266 Mon Sep 17 00:00:00 2001 From: Vodovozov Gleb Date: Fri, 23 Oct 2015 12:24:07 +0800 Subject: [PATCH 118/210] when receiving error from GitHubSearchRepositoriesAPI wait while network is reachable again and retry query --- ...tHubSearchRepositoriesViewController.swift | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift index 4bef1f77..c68c4e98 100644 --- a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift +++ b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift @@ -35,6 +35,15 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat let tableView = self.tableView let searchBar = self.searchBar + // init reachability to check internet connection + let reachability:Reachability? + do{ + reachability = try Reachability.reachabilityForInternetConnection() + }catch let error{ + print("cannot create reachability - \(error)") + reachability = nil + } + let allRepositories = repositories .map { repositories in return [SectionModel(model: "Repositories", items: repositories)] @@ -52,6 +61,7 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat return section.items.count > 0 ? "Repositories (\(section.items.count))" : "No repositories found" } + // reactive data source allRepositories .bindTo(tableView.rx_itemsWithDataSource(dataSource)) @@ -72,7 +82,15 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat return just(.Repositories([])) } else { return GitHubSearchRepositoriesAPI.sharedAPI.search(query, loadNextPageTrigger: loadNextPageTrigger) - .catchErrorJustReturn(.Repositories([])) + .retry(3) + .catchError{ (e) -> Observable in + reachability? + .rx_reachable + .skipWhile { $0 != .Reachable } + .flatMap { _ in failWith(e)} + ?? failWith(e) + } + .retry() } } .switchLatest() From a35c77c0e04c03ef4807e3dc51e04e39c32e766f Mon Sep 17 00:00:00 2001 From: Vodovozov Gleb Date: Sat, 24 Oct 2015 11:20:18 +0800 Subject: [PATCH 119/210] Reachability+Rx now is ReachabilityService --- RxExample/RxExample.xcodeproj/project.pbxproj | 12 ++--- .../RxExample/Services/Reachability+Rx.swift | 39 -------------- .../Services/ReachabilityService.swift | 54 +++++++++++++++++++ 3 files changed, 58 insertions(+), 47 deletions(-) delete mode 100644 RxExample/RxExample/Services/Reachability+Rx.swift create mode 100644 RxExample/RxExample/Services/ReachabilityService.swift diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index fc535fc5..2b1ac8ce 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -19,11 +19,9 @@ 07E3C2331B03605B0010338D /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E3C2321B03605B0010338D /* Dependencies.swift */; }; B1B7C3D01BE006870076934E /* TakeLast.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B7C3CF1BE006870076934E /* TakeLast.swift */; }; B18F3BBC1BD92EC8000AAC79 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */; }; - B18F3BBE1BD93C2E000AAC79 /* Reachability+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBD1BD93C2E000AAC79 /* Reachability+Rx.swift */; }; B18F3BBF1BD93DFF000AAC79 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */; }; - B18F3BC01BD93DFF000AAC79 /* Reachability+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBD1BD93C2E000AAC79 /* Reachability+Rx.swift */; }; B18F3BC11BD93E00000AAC79 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */; }; - B18F3BC21BD93E00000AAC79 /* Reachability+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBD1BD93C2E000AAC79 /* Reachability+Rx.swift */; }; + B18F3BE21BDB2E8F000AAC79 /* ReachabilityService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BE11BDB2E8F000AAC79 /* ReachabilityService.swift */; }; C803973A1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; }; C803973B1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; }; C80397491BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */; }; @@ -460,7 +458,7 @@ 07E3C2321B03605B0010338D /* Dependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Dependencies.swift; path = Examples/Dependencies.swift; sourceTree = ""; }; B1B7C3CF1BE006870076934E /* TakeLast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TakeLast.swift; sourceTree = ""; }; B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = ""; }; - B18F3BBD1BD93C2E000AAC79 /* Reachability+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Reachability+Rx.swift"; sourceTree = ""; }; + B18F3BE11BDB2E8F000AAC79 /* ReachabilityService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReachabilityService.swift; sourceTree = ""; }; C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = ""; }; C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubSearchRepositoriesAPI.swift; sourceTree = ""; }; C80DDE7A1BCDA952006A1832 /* SkipWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipWhile.swift; sourceTree = ""; }; @@ -886,7 +884,7 @@ C8A2A2CA1B404A1200F11F09 /* Randomizer.swift */, C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */, B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */, - B18F3BBD1BD93C2E000AAC79 /* Reachability+Rx.swift */, + B18F3BE11BDB2E8F000AAC79 /* ReachabilityService.swift */, ); path = Services; sourceTree = ""; @@ -1697,7 +1695,6 @@ C89464D61BC6C2B00055219D /* Producer.swift in Sources */, C894658A1BC6C2BC0055219D /* RxTableViewDataSourceProxy.swift in Sources */, C894656F1BC6C2BC0055219D /* DelegateProxyType.swift in Sources */, - B18F3BC21BD93E00000AAC79 /* Reachability+Rx.swift in Sources */, C89465721BC6C2BC0055219D /* ControlTarget.swift in Sources */, C89464EC1BC6C2B00055219D /* Observable+Binding.swift in Sources */, C8297E3A1B6CF905000589EA /* WikipediaSearchViewController.swift in Sources */, @@ -1874,7 +1871,6 @@ 0706E1961B14AF5100BA2D3A /* DetailViewController.swift in Sources */, C88C78991B4012A90061C5AB /* SectionModelType.swift in Sources */, C83367251AD029AE00C668A7 /* ImageService.swift in Sources */, - B18F3BBE1BD93C2E000AAC79 /* Reachability+Rx.swift in Sources */, C86E2F471AE5A0CA00C31024 /* WikipediaSearchResult.swift in Sources */, C80397491BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift in Sources */, C890A65A1AEBD28A00AFF7E6 /* GitHubAPI.swift in Sources */, @@ -1891,6 +1887,7 @@ 075F13101B4E9D5A000D7861 /* APIWrappersViewController.swift in Sources */, C83367311AD029AE00C668A7 /* Wireframe.swift in Sources */, 07E300071B14995F00F00100 /* TableViewController.swift in Sources */, + B18F3BE21BDB2E8F000AAC79 /* ReachabilityService.swift in Sources */, B18F3BBC1BD92EC8000AAC79 /* Reachability.swift in Sources */, C859B9A41B45C5D900D012D7 /* PartialUpdatesViewController.swift in Sources */, 07E3C2331B03605B0010338D /* Dependencies.swift in Sources */, @@ -1913,7 +1910,6 @@ C88BB8BD1B07E6C90064D411 /* SearchViewModel.swift in Sources */, C8E9D2AF1BD3FD960079D0DB /* ActivityIndicator.swift in Sources */, C88BB8BE1B07E6C90064D411 /* ImageService.swift in Sources */, - B18F3BC01BD93DFF000AAC79 /* Reachability+Rx.swift in Sources */, C88BB8BF1B07E6C90064D411 /* WikipediaSearchResult.swift in Sources */, C8DF92F61B0B43A4009BCF9A /* IntroductionExampleViewController.swift in Sources */, B18F3BBF1BD93DFF000AAC79 /* Reachability.swift in Sources */, diff --git a/RxExample/RxExample/Services/Reachability+Rx.swift b/RxExample/RxExample/Services/Reachability+Rx.swift deleted file mode 100644 index 36ce6805..00000000 --- a/RxExample/RxExample/Services/Reachability+Rx.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// Reachability+Rx.swift -// RxExample -// -// Created by Vodovozov Gleb on 22.10.2015. -// Copyright © 2015 Krunoslav Zaher. All rights reserved. -// - -import RxSwift - -public enum ReachabilityStatus{ - case Reachable,Unreachable -} - -extension Reachability{ - - /** - Reactive wrapper for reachability state changes - */ - public var rx_reachable: Observable { - - return create { observer in - - self.whenReachable = { reachability in - observer.on(.Next(.Reachable)) - } - self.whenUnreachable = { reachability in - observer.on(.Next(.Unreachable)) - } - do{ - try self.startNotifier() - }catch let error{ - observer.on(.Error(error)) - } - return NopDisposable.instance - } - - } -} \ No newline at end of file diff --git a/RxExample/RxExample/Services/ReachabilityService.swift b/RxExample/RxExample/Services/ReachabilityService.swift new file mode 100644 index 00000000..5edd9520 --- /dev/null +++ b/RxExample/RxExample/Services/ReachabilityService.swift @@ -0,0 +1,54 @@ +// +// ReachabilityService.swift +// RxExample +// +// Created by Vodovozov Gleb on 22.10.2015. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import RxSwift + +public enum ReachabilityStatus{ + case Reachable,Unreachable +} + +class ReachabilityService{ + + private let reachabilityRef = try! Reachability.reachabilityForInternetConnection() + + private let _reachabilityChangedSubject = PublishSubject() + private var reachabilityChanged:Observable { + get{ + return _reachabilityChangedSubject.asObservable() + } + } + + // singleton + static let sharedReachabilityService = ReachabilityService() + + init(){ + reachabilityRef.whenReachable = { reachability in + self._reachabilityChangedSubject.on(.Next(.Reachable)) + } + + reachabilityRef.whenUnreachable = { reachability in + self._reachabilityChangedSubject.on(.Next(.Unreachable)) + } + + try! reachabilityRef.startNotifier() + + } +} + +extension ObservableConvertibleType { + func retryOnBecomesReachable(valueOnFailure:E, reachabilityService:ReachabilityService)->Observable{ + return self.asObservable() + .catchError { (e) -> Observable in + reachabilityService + .reachabilityChanged + .flatMap { _ in failWith(e) } + .startWith(valueOnFailure) + } + .retry() + } +} \ No newline at end of file From 5dfd604f2957e7b1481bf2d2882486ad8693d2d4 Mon Sep 17 00:00:00 2001 From: Vodovozov Gleb Date: Sat, 24 Oct 2015 11:21:14 +0800 Subject: [PATCH 120/210] use new reachability service to handle network errors in AutoLoading example --- ...itHubSearchRepositoriesViewController.swift | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift index c68c4e98..8ddb7326 100644 --- a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift +++ b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift @@ -35,15 +35,6 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat let tableView = self.tableView let searchBar = self.searchBar - // init reachability to check internet connection - let reachability:Reachability? - do{ - reachability = try Reachability.reachabilityForInternetConnection() - }catch let error{ - print("cannot create reachability - \(error)") - reachability = nil - } - let allRepositories = repositories .map { repositories in return [SectionModel(model: "Repositories", items: repositories)] @@ -83,14 +74,7 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat } else { return GitHubSearchRepositoriesAPI.sharedAPI.search(query, loadNextPageTrigger: loadNextPageTrigger) .retry(3) - .catchError{ (e) -> Observable in - reachability? - .rx_reachable - .skipWhile { $0 != .Reachable } - .flatMap { _ in failWith(e)} - ?? failWith(e) - } - .retry() + .retryOnBecomesReachable(.Repositories([]), reachabilityService: ReachabilityService.sharedReachabilityService) } } .switchLatest() From d070c0876269aa11061efe90ff259d6aec880129 Mon Sep 17 00:00:00 2001 From: Vodovozov Gleb Date: Sat, 24 Oct 2015 11:22:46 +0800 Subject: [PATCH 121/210] use reachability service to handle network errors in Wikipedia image search example: handle main search and loading titles --- .../WikipediaImageSearch/ViewModels/SearchResultViewModel.swift | 1 + .../WikipediaImageSearch/ViewModels/SearchViewModel.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchResultViewModel.swift b/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchResultViewModel.swift index a6677789..6a4e0d12 100644 --- a/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchResultViewModel.swift +++ b/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchResultViewModel.swift @@ -51,6 +51,7 @@ class SearchResultViewModel { return "\(searchResult.title) loading ..." } } + .retryOnBecomesReachable("loading...", reachabilityService: ReachabilityService.sharedReachabilityService) } func configureImageURLs() -> Observable<[NSURL]> { diff --git a/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchViewModel.swift b/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchViewModel.swift index 4e7ffbd9..4f611abd 100644 --- a/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchViewModel.swift +++ b/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchViewModel.swift @@ -34,6 +34,7 @@ class SearchViewModel { .map { query in API.getSearchResults(query) .retry(3) + .retryOnBecomesReachable([], reachabilityService: ReachabilityService.sharedReachabilityService) .startWith([]) // clears results on new search term .asDriver(onErrorJustReturn: []) } From a7ea80a632d3be87f56a28494338ef7bc2ef9423 Mon Sep 17 00:00:00 2001 From: Vodovozov Gleb Date: Sat, 24 Oct 2015 11:31:08 +0800 Subject: [PATCH 122/210] use reachability service to handle network errors in Wikipedia image search example: for cells with image. If connection is lost image with label Loading is displayed until connection is reestablished. Image is exported from paintcode --- .../Views/WikipediaSearchCell.swift | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchCell.swift b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchCell.swift index 8503322a..5e672128 100644 --- a/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchCell.swift +++ b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchCell.swift @@ -41,11 +41,12 @@ public class WikipediaSearchCell: UITableViewCell { viewModel.imageURLs .drive(self.imagesOutlet.rx_itemsWithCellIdentifier("ImageCell")) { [unowned self] (_, URL, cell: CollectionViewImageCell) in - let loadingPlaceholder: UIImage? = nil + let loadingPlaceholder:UIImage? = nil + let loadingPlaceholderOnError:UIImage? = self.loadingPlaceholderWithFrame(cell.bounds) cell.image = self.imageService.imageFromURL(URL) .map { $0 as UIImage? } - .catchErrorJustReturn(nil) + .retryOnBecomesReachable(loadingPlaceholderOnError, reachabilityService: ReachabilityService.sharedReachabilityService) .startWith(loadingPlaceholder) } .addDisposableTo(disposeBag) @@ -60,6 +61,33 @@ public class WikipediaSearchCell: UITableViewCell { self.disposeBag = nil } + private func loadingPlaceholderWithFrame(frame:CGRect)->UIImage{ + UIGraphicsBeginImageContextWithOptions(frame.size, false, 0) + + //// General Declarations + let context = UIGraphicsGetCurrentContext() + + //// Text Drawing + let textRect = CGRectMake(frame.minX + floor((frame.width - 100) / 2 + 0.5), frame.minY + floor((frame.height - 21) * 0.49367 + 0.5), 100, 21) + let textTextContent = NSString(string: "Loading") + let textStyle = NSParagraphStyle.defaultParagraphStyle().mutableCopy() as! NSMutableParagraphStyle + textStyle.alignment = .Center + + let textFontAttributes = [NSFontAttributeName: UIFont.systemFontOfSize(UIFont.smallSystemFontSize()), NSForegroundColorAttributeName: UIColor.blackColor(), NSParagraphStyleAttributeName: textStyle] + + let textTextHeight: CGFloat = textTextContent.boundingRectWithSize(CGSizeMake(textRect.width, CGFloat.infinity), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: textFontAttributes, context: nil).size.height + CGContextSaveGState(context) + CGContextClipToRect(context, textRect); + textTextContent.drawInRect(CGRectMake(textRect.minX, textRect.minY + (textRect.height - textTextHeight) / 2, textRect.width, textTextHeight), withAttributes: textFontAttributes) + CGContextRestoreGState(context) + let imageOfCanvas1 = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + return imageOfCanvas1 + + } + deinit { } + } From 942b617c1063ebbdd9c70d5f95a39a54e2c11d99 Mon Sep 17 00:00:00 2001 From: Vodovozov Gleb Date: Sun, 1 Nov 2015 10:30:55 +0800 Subject: [PATCH 123/210] =?UTF-8?q?introducing=20DownloadableImage=20?= =?UTF-8?q?=E2=80=93=20enum=20with=202=20possible=20values=20=E2=80=93=20C?= =?UTF-8?q?ontent(image:UIImage)=20and=20OfflinePlaceholder.=20Use=20it=20?= =?UTF-8?q?in=20ImageService=20to=20implement=20retries=20on=20network=20f?= =?UTF-8?q?ailures.=20Also=20added=20rxex=5FdownloadableImage=20extension?= =?UTF-8?q?=20for=20UIImageView=20which=20shows=20either=20content=20or=20?= =?UTF-8?q?placeholder=20image.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- RxExample/RxExample.xcodeproj/project.pbxproj | 8 ++- .../Services/DownloadableImage.swift | 59 +++++++++++++++++++ .../RxExample/Services/ImageService.swift | 12 +++- 3 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 RxExample/RxExample/Services/DownloadableImage.swift diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index 2b1ac8ce..febf13ce 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -17,11 +17,12 @@ 07E300071B14995F00F00100 /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E300061B14995F00F00100 /* TableViewController.swift */; }; 07E300091B149A2A00F00100 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E300081B149A2A00F00100 /* User.swift */; }; 07E3C2331B03605B0010338D /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E3C2321B03605B0010338D /* Dependencies.swift */; }; - B1B7C3D01BE006870076934E /* TakeLast.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B7C3CF1BE006870076934E /* TakeLast.swift */; }; + B1604CB51BE49F8D002E1279 /* DownloadableImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1604CB41BE49F8D002E1279 /* DownloadableImage.swift */; }; B18F3BBC1BD92EC8000AAC79 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */; }; B18F3BBF1BD93DFF000AAC79 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */; }; B18F3BC11BD93E00000AAC79 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */; }; B18F3BE21BDB2E8F000AAC79 /* ReachabilityService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BE11BDB2E8F000AAC79 /* ReachabilityService.swift */; }; + B1B7C3D01BE006870076934E /* TakeLast.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B7C3CF1BE006870076934E /* TakeLast.swift */; }; C803973A1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; }; C803973B1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; }; C80397491BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */; }; @@ -456,9 +457,10 @@ 07E300061B14995F00F00100 /* TableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; }; 07E300081B149A2A00F00100 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 07E3C2321B03605B0010338D /* Dependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Dependencies.swift; path = Examples/Dependencies.swift; sourceTree = ""; }; - B1B7C3CF1BE006870076934E /* TakeLast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TakeLast.swift; sourceTree = ""; }; + B1604CB41BE49F8D002E1279 /* DownloadableImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadableImage.swift; sourceTree = ""; }; B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = ""; }; B18F3BE11BDB2E8F000AAC79 /* ReachabilityService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReachabilityService.swift; sourceTree = ""; }; + B1B7C3CF1BE006870076934E /* TakeLast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TakeLast.swift; sourceTree = ""; }; C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = ""; }; C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubSearchRepositoriesAPI.swift; sourceTree = ""; }; C80DDE7A1BCDA952006A1832 /* SkipWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipWhile.swift; sourceTree = ""; }; @@ -885,6 +887,7 @@ C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */, B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */, B18F3BE11BDB2E8F000AAC79 /* ReachabilityService.swift */, + B1604CB41BE49F8D002E1279 /* DownloadableImage.swift */, ); path = Services; sourceTree = ""; @@ -1884,6 +1887,7 @@ C88C78951B3F20DB0061C5AB /* Differentiator.swift in Sources */, C8C46DAA1B47F7110020D71E /* WikipediaSearchCell.swift in Sources */, C890A6581AEBD26B00AFF7E6 /* GitHubSignupViewController.swift in Sources */, + B1604CB51BE49F8D002E1279 /* DownloadableImage.swift in Sources */, 075F13101B4E9D5A000D7861 /* APIWrappersViewController.swift in Sources */, C83367311AD029AE00C668A7 /* Wireframe.swift in Sources */, 07E300071B14995F00F00100 /* TableViewController.swift in Sources */, diff --git a/RxExample/RxExample/Services/DownloadableImage.swift b/RxExample/RxExample/Services/DownloadableImage.swift new file mode 100644 index 00000000..fb617272 --- /dev/null +++ b/RxExample/RxExample/Services/DownloadableImage.swift @@ -0,0 +1,59 @@ +// +// DownloadableImage.swift +// RxExample +// +// Created by Vodovozov Gleb on 31.10.2015. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +#if os(iOS) || os(tvOS) + +import Foundation +#if !RX_NO_MODULE +import RxSwift +#endif +import UIKit + +public enum DownloadableImage{ + case Content(image:UIImage) + case OfflinePlaceholder + +} + +extension UIImageView{ + + public var rxex_downloadableImage: AnyObserver{ + return self.rxex_downloadableImageAnimated(nil) + } + + public func rxex_downloadableImageAnimated(transitionType:String?) -> AnyObserver { + + return AnyObserver { [weak self] event in + MainScheduler.ensureExecutingOnScheduler() + + switch event{ + case .Next(let value): + for subview in self?.subviews ?? [] { + subview.removeFromSuperview() + } + switch value{ + case .Content(let image): + self?.rx_image.onNext(image) + case .OfflinePlaceholder: + let label = UILabel(frame: self!.frame) + label.textAlignment = .Center + label.font = UIFont.systemFontOfSize(35) + label.text = "⚠️📶" + self?.addSubview(label) + } + case .Error(let error): + bindingErrorToInterface(error) + break + case .Completed: + break + } + } + } +} + +#endif diff --git a/RxExample/RxExample/Services/ImageService.swift b/RxExample/RxExample/Services/ImageService.swift index 0f09a2e4..2b0c2864 100644 --- a/RxExample/RxExample/Services/ImageService.swift +++ b/RxExample/RxExample/Services/ImageService.swift @@ -20,7 +20,7 @@ import RxCocoa protocol ImageService { func imageFromURL(URL: NSURL) -> Observable -} + func downloadableImageFromURLWithRetryIfUnreachable(URL: NSURL) -> Observable} class DefaultImageService: ImageService { @@ -90,4 +90,14 @@ class DefaultImageService: ImageService { } .observeOn($.mainScheduler) } + + func downloadableImageFromURLWithRetryIfUnreachable(URL: NSURL) -> Observable { + return deferred{ + self.imageFromURL(URL) + .map { DownloadableImage.Content(image: $0) } + .retryOnBecomesReachable( DownloadableImage.OfflinePlaceholder, reachabilityService: ReachabilityService.sharedReachabilityService) + .startWith(.Content(image: UIImage())) + } + .observeOn($.mainScheduler) + } } From 85197b9a7ce1bf5e97507b116d3256102498827a Mon Sep 17 00:00:00 2001 From: Vodovozov Gleb Date: Sun, 1 Nov 2015 10:31:25 +0800 Subject: [PATCH 124/210] Use DownloadableImage in WikipediaImageServiceExample to handle network errors and bind result to collection view cells. --- .../Views/CollectionViewImageCell.swift | 14 ++++---- .../Views/WikipediaSearchCell.swift | 33 +------------------ 2 files changed, 8 insertions(+), 39 deletions(-) diff --git a/RxExample/RxExample/Examples/WikipediaImageSearch/Views/CollectionViewImageCell.swift b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/CollectionViewImageCell.swift index c004f398..25c703cf 100644 --- a/RxExample/RxExample/Examples/WikipediaImageSearch/Views/CollectionViewImageCell.swift +++ b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/CollectionViewImageCell.swift @@ -17,15 +17,15 @@ public class CollectionViewImageCell: UICollectionViewCell { @IBOutlet var imageOutlet: UIImageView! var disposeBag: DisposeBag! - - var image: Observable! { - didSet { + + var downloadableImage: Observable?{ + didSet{ let disposeBag = DisposeBag() - - self.image - .subscribe(imageOutlet.rx_imageAnimated(kCATransitionFade)) + + self.downloadableImage? + .bindTo(imageOutlet.rxex_downloadableImageAnimated(kCATransitionFade)) .addDisposableTo(disposeBag) - + self.disposeBag = disposeBag } } diff --git a/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchCell.swift b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchCell.swift index 5e672128..62ccfe0a 100644 --- a/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchCell.swift +++ b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchCell.swift @@ -41,13 +41,8 @@ public class WikipediaSearchCell: UITableViewCell { viewModel.imageURLs .drive(self.imagesOutlet.rx_itemsWithCellIdentifier("ImageCell")) { [unowned self] (_, URL, cell: CollectionViewImageCell) in - let loadingPlaceholder:UIImage? = nil - let loadingPlaceholderOnError:UIImage? = self.loadingPlaceholderWithFrame(cell.bounds) - cell.image = self.imageService.imageFromURL(URL) - .map { $0 as UIImage? } - .retryOnBecomesReachable(loadingPlaceholderOnError, reachabilityService: ReachabilityService.sharedReachabilityService) - .startWith(loadingPlaceholder) + cell.downloadableImage = self.imageService.downloadableImageFromURLWithRetryIfUnreachable(URL) } .addDisposableTo(disposeBag) @@ -61,32 +56,6 @@ public class WikipediaSearchCell: UITableViewCell { self.disposeBag = nil } - private func loadingPlaceholderWithFrame(frame:CGRect)->UIImage{ - UIGraphicsBeginImageContextWithOptions(frame.size, false, 0) - - //// General Declarations - let context = UIGraphicsGetCurrentContext() - - //// Text Drawing - let textRect = CGRectMake(frame.minX + floor((frame.width - 100) / 2 + 0.5), frame.minY + floor((frame.height - 21) * 0.49367 + 0.5), 100, 21) - let textTextContent = NSString(string: "Loading") - let textStyle = NSParagraphStyle.defaultParagraphStyle().mutableCopy() as! NSMutableParagraphStyle - textStyle.alignment = .Center - - let textFontAttributes = [NSFontAttributeName: UIFont.systemFontOfSize(UIFont.smallSystemFontSize()), NSForegroundColorAttributeName: UIColor.blackColor(), NSParagraphStyleAttributeName: textStyle] - - let textTextHeight: CGFloat = textTextContent.boundingRectWithSize(CGSizeMake(textRect.width, CGFloat.infinity), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: textFontAttributes, context: nil).size.height - CGContextSaveGState(context) - CGContextClipToRect(context, textRect); - textTextContent.drawInRect(CGRectMake(textRect.minX, textRect.minY + (textRect.height - textTextHeight) / 2, textRect.width, textTextHeight), withAttributes: textFontAttributes) - CGContextRestoreGState(context) - let imageOfCanvas1 = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - - return imageOfCanvas1 - - } - deinit { } From 5c148de3e2e13c4f454972d0f4767f76748f3a25 Mon Sep 17 00:00:00 2001 From: Vodovozov Gleb Date: Sun, 1 Nov 2015 11:28:09 +0800 Subject: [PATCH 125/210] moved out extension of UIImageView to separate file to have OS X support for DownloadableImage. Also changed UIImage to Image typealias --- RxExample/RxExample.xcodeproj/project.pbxproj | 14 +++++ .../Services/DownloadableImage.swift | 50 +++--------------- .../RxExample/Services/ImageService.swift | 2 +- .../Services/ReachabilityService.swift | 4 +- .../UIImageView+DownloadableImage.swift | 52 +++++++++++++++++++ 5 files changed, 77 insertions(+), 45 deletions(-) create mode 100644 RxExample/RxExample/Services/UIImageView+DownloadableImage.swift diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index febf13ce..09ff701e 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -18,6 +18,12 @@ 07E300091B149A2A00F00100 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E300081B149A2A00F00100 /* User.swift */; }; 07E3C2331B03605B0010338D /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E3C2321B03605B0010338D /* Dependencies.swift */; }; B1604CB51BE49F8D002E1279 /* DownloadableImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1604CB41BE49F8D002E1279 /* DownloadableImage.swift */; }; + B1604CC21BE5B895002E1279 /* ReachabilityService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BE11BDB2E8F000AAC79 /* ReachabilityService.swift */; }; + B1604CC31BE5B8BD002E1279 /* ReachabilityService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BE11BDB2E8F000AAC79 /* ReachabilityService.swift */; }; + B1604CC41BE5B8CE002E1279 /* DownloadableImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1604CB41BE49F8D002E1279 /* DownloadableImage.swift */; }; + B1604CC91BE5BBFA002E1279 /* UIImageView+DownloadableImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1604CC81BE5BBFA002E1279 /* UIImageView+DownloadableImage.swift */; }; + B1604CCA1BE5BC18002E1279 /* DownloadableImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1604CB41BE49F8D002E1279 /* DownloadableImage.swift */; }; + B1604CCB1BE5BC45002E1279 /* UIImageView+DownloadableImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1604CC81BE5BBFA002E1279 /* UIImageView+DownloadableImage.swift */; }; B18F3BBC1BD92EC8000AAC79 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */; }; B18F3BBF1BD93DFF000AAC79 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */; }; B18F3BC11BD93E00000AAC79 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */; }; @@ -458,6 +464,7 @@ 07E300081B149A2A00F00100 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 07E3C2321B03605B0010338D /* Dependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Dependencies.swift; path = Examples/Dependencies.swift; sourceTree = ""; }; B1604CB41BE49F8D002E1279 /* DownloadableImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadableImage.swift; sourceTree = ""; }; + B1604CC81BE5BBFA002E1279 /* UIImageView+DownloadableImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImageView+DownloadableImage.swift"; sourceTree = ""; }; B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = ""; }; B18F3BE11BDB2E8F000AAC79 /* ReachabilityService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReachabilityService.swift; sourceTree = ""; }; B1B7C3CF1BE006870076934E /* TakeLast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TakeLast.swift; sourceTree = ""; }; @@ -888,6 +895,7 @@ B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */, B18F3BE11BDB2E8F000AAC79 /* ReachabilityService.swift */, B1604CB41BE49F8D002E1279 /* DownloadableImage.swift */, + B1604CC81BE5BBFA002E1279 /* UIImageView+DownloadableImage.swift */, ); path = Services; sourceTree = ""; @@ -1662,6 +1670,7 @@ C89465751BC6C2BC0055219D /* KVOObservable.swift in Sources */, C89464CB1BC6C2B00055219D /* FailWith.swift in Sources */, C8297E341B6CF905000589EA /* UIImageView+Extensions.swift in Sources */, + B1604CC41BE5B8CE002E1279 /* DownloadableImage.swift in Sources */, C80DDED61BCE9046006A1832 /* Driver+Operators.swift in Sources */, C89465051BC6C2B00055219D /* SerialDispatchQueueScheduler.swift in Sources */, C89464B81BC6C2B00055219D /* Observable.swift in Sources */, @@ -1716,6 +1725,7 @@ C89465891BC6C2BC0055219D /* RxSearchBarDelegateProxy.swift in Sources */, C89464C21BC6C2B00055219D /* CombineLatest.swift in Sources */, C89464E71BC6C2B00055219D /* Zip+arity.swift in Sources */, + B1604CC21BE5B895002E1279 /* ReachabilityService.swift in Sources */, C89464DE1BC6C2B00055219D /* Skip.swift in Sources */, C89464DB1BC6C2B00055219D /* Sample.swift in Sources */, C89465011BC6C2B00055219D /* OperationQueueScheduler.swift in Sources */, @@ -1773,6 +1783,7 @@ C89464FE1BC6C2B00055219D /* CurrentThreadScheduler.swift in Sources */, C89464BE1BC6C2B00055219D /* Catch.swift in Sources */, C89CDB721BCC45EE002063D9 /* SkipUntil.swift in Sources */, + B1604CCB1BE5BC45002E1279 /* UIImageView+DownloadableImage.swift in Sources */, C8297E471B6CF905000589EA /* ViewController.swift in Sources */, C89464E41BC6C2B00055219D /* TakeWhile.swift in Sources */, C89464F71BC6C2B00055219D /* ObserverBase.swift in Sources */, @@ -1852,6 +1863,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B1604CC91BE5BBFA002E1279 /* UIImageView+DownloadableImage.swift in Sources */, 0706E19F1B17703E00BA2D3A /* RandomUserAPI.swift in Sources */, C86E2F3E1AE5A0CA00C31024 /* SearchResultViewModel.swift in Sources */, C83367241AD029AE00C668A7 /* HtmlParsing.swift in Sources */, @@ -1914,6 +1926,7 @@ C88BB8BD1B07E6C90064D411 /* SearchViewModel.swift in Sources */, C8E9D2AF1BD3FD960079D0DB /* ActivityIndicator.swift in Sources */, C88BB8BE1B07E6C90064D411 /* ImageService.swift in Sources */, + B1604CC31BE5B8BD002E1279 /* ReachabilityService.swift in Sources */, C88BB8BF1B07E6C90064D411 /* WikipediaSearchResult.swift in Sources */, C8DF92F61B0B43A4009BCF9A /* IntroductionExampleViewController.swift in Sources */, B18F3BBF1BD93DFF000AAC79 /* Reachability.swift in Sources */, @@ -1922,6 +1935,7 @@ C88BB8C61B07E6C90064D411 /* Wireframe.swift in Sources */, C88BB8C71B07E6C90064D411 /* Dependencies.swift in Sources */, C88BB8CA1B07E6C90064D411 /* WikipediaAPI.swift in Sources */, + B1604CCA1BE5BC18002E1279 /* DownloadableImage.swift in Sources */, C88BB8CC1B07E6C90064D411 /* WikipediaPage.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/RxExample/RxExample/Services/DownloadableImage.swift b/RxExample/RxExample/Services/DownloadableImage.swift index fb617272..5df0e019 100644 --- a/RxExample/RxExample/Services/DownloadableImage.swift +++ b/RxExample/RxExample/Services/DownloadableImage.swift @@ -6,54 +6,18 @@ // Copyright © 2015 Krunoslav Zaher. All rights reserved. // -#if os(iOS) || os(tvOS) - import Foundation #if !RX_NO_MODULE import RxSwift #endif -import UIKit +#if os(iOS) + import UIKit +#elseif os(OSX) + import Cocoa +#endif -public enum DownloadableImage{ - case Content(image:UIImage) +enum DownloadableImage{ + case Content(image:Image) case OfflinePlaceholder } - -extension UIImageView{ - - public var rxex_downloadableImage: AnyObserver{ - return self.rxex_downloadableImageAnimated(nil) - } - - public func rxex_downloadableImageAnimated(transitionType:String?) -> AnyObserver { - - return AnyObserver { [weak self] event in - MainScheduler.ensureExecutingOnScheduler() - - switch event{ - case .Next(let value): - for subview in self?.subviews ?? [] { - subview.removeFromSuperview() - } - switch value{ - case .Content(let image): - self?.rx_image.onNext(image) - case .OfflinePlaceholder: - let label = UILabel(frame: self!.frame) - label.textAlignment = .Center - label.font = UIFont.systemFontOfSize(35) - label.text = "⚠️📶" - self?.addSubview(label) - } - case .Error(let error): - bindingErrorToInterface(error) - break - case .Completed: - break - } - } - } -} - -#endif diff --git a/RxExample/RxExample/Services/ImageService.swift b/RxExample/RxExample/Services/ImageService.swift index 2b0c2864..538209fa 100644 --- a/RxExample/RxExample/Services/ImageService.swift +++ b/RxExample/RxExample/Services/ImageService.swift @@ -96,7 +96,7 @@ class DefaultImageService: ImageService { self.imageFromURL(URL) .map { DownloadableImage.Content(image: $0) } .retryOnBecomesReachable( DownloadableImage.OfflinePlaceholder, reachabilityService: ReachabilityService.sharedReachabilityService) - .startWith(.Content(image: UIImage())) + .startWith(.Content(image: Image())) } .observeOn($.mainScheduler) } diff --git a/RxExample/RxExample/Services/ReachabilityService.swift b/RxExample/RxExample/Services/ReachabilityService.swift index 5edd9520..46031054 100644 --- a/RxExample/RxExample/Services/ReachabilityService.swift +++ b/RxExample/RxExample/Services/ReachabilityService.swift @@ -6,7 +6,9 @@ // Copyright © 2015 Krunoslav Zaher. All rights reserved. // +#if !RX_NO_MODULE import RxSwift +#endif public enum ReachabilityStatus{ case Reachable,Unreachable @@ -51,4 +53,4 @@ extension ObservableConvertibleType { } .retry() } -} \ No newline at end of file +} diff --git a/RxExample/RxExample/Services/UIImageView+DownloadableImage.swift b/RxExample/RxExample/Services/UIImageView+DownloadableImage.swift new file mode 100644 index 00000000..808efd49 --- /dev/null +++ b/RxExample/RxExample/Services/UIImageView+DownloadableImage.swift @@ -0,0 +1,52 @@ +// +// UIImageView+DownloadableImage.swift +// RxExample +// +// Created by Vodovozov Gleb on 01.11.2015. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +#if os(iOS) || os(tvOS) +import Foundation +#if !RX_NO_MODULE +import RxSwift +import RxCocoa +#endif +import UIKit + +extension UIImageView{ + + var rxex_downloadableImage: AnyObserver{ + return self.rxex_downloadableImageAnimated(nil) + } + + func rxex_downloadableImageAnimated(transitionType:String?) -> AnyObserver { + + return AnyObserver { [weak self] event in + MainScheduler.ensureExecutingOnScheduler() + + switch event{ + case .Next(let value): + for subview in self?.subviews ?? [] { + subview.removeFromSuperview() + } + switch value{ + case .Content(let image): + self?.rx_image.onNext(image) + case .OfflinePlaceholder: + let label = UILabel(frame: self!.frame) + label.textAlignment = .Center + label.font = UIFont.systemFontOfSize(35) + label.text = "⚠️📶" + self?.addSubview(label) + } + case .Error(let error): + bindingErrorToInterface(error) + break + case .Completed: + break + } + } + } +} +#endif From 72b0904fea46901edefc9f9882bbe034505cbd8e Mon Sep 17 00:00:00 2001 From: Vodovozov Gleb Date: Sun, 1 Nov 2015 11:31:22 +0800 Subject: [PATCH 126/210] simplified text in OfflinePlaceholder --- .../RxExample/Services/UIImageView+DownloadableImage.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RxExample/RxExample/Services/UIImageView+DownloadableImage.swift b/RxExample/RxExample/Services/UIImageView+DownloadableImage.swift index 808efd49..e8070c19 100644 --- a/RxExample/RxExample/Services/UIImageView+DownloadableImage.swift +++ b/RxExample/RxExample/Services/UIImageView+DownloadableImage.swift @@ -37,7 +37,7 @@ extension UIImageView{ let label = UILabel(frame: self!.frame) label.textAlignment = .Center label.font = UIFont.systemFontOfSize(35) - label.text = "⚠️📶" + label.text = "⚠️" self?.addSubview(label) } case .Error(let error): From 9a08915ff131c43bc0fdf5a2997d9a14f06c4d0a Mon Sep 17 00:00:00 2001 From: Vodovozov Gleb Date: Sun, 1 Nov 2015 12:58:46 +0800 Subject: [PATCH 127/210] ensure that self is not nil in rxex_downloadableImageAnimated --- .../Services/UIImageView+DownloadableImage.swift | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/RxExample/RxExample/Services/UIImageView+DownloadableImage.swift b/RxExample/RxExample/Services/UIImageView+DownloadableImage.swift index e8070c19..0a427599 100644 --- a/RxExample/RxExample/Services/UIImageView+DownloadableImage.swift +++ b/RxExample/RxExample/Services/UIImageView+DownloadableImage.swift @@ -23,22 +23,24 @@ extension UIImageView{ func rxex_downloadableImageAnimated(transitionType:String?) -> AnyObserver { return AnyObserver { [weak self] event in + + guard let strongSelf = self else { return } MainScheduler.ensureExecutingOnScheduler() switch event{ case .Next(let value): - for subview in self?.subviews ?? [] { + for subview in strongSelf.subviews { subview.removeFromSuperview() } switch value{ case .Content(let image): - self?.rx_image.onNext(image) + strongSelf.rx_image.onNext(image) case .OfflinePlaceholder: - let label = UILabel(frame: self!.frame) + let label = UILabel(frame: strongSelf.bounds) label.textAlignment = .Center label.font = UIFont.systemFontOfSize(35) label.text = "⚠️" - self?.addSubview(label) + strongSelf.addSubview(label) } case .Error(let error): bindingErrorToInterface(error) From d7414d575be22bfcc0092c48c598c850c6c351a0 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 1 Nov 2015 17:15:54 +0100 Subject: [PATCH 128/210] Polishing of public interface. --- .../Implementations/WithLatestFrom.swift | 26 ++++++++----------- .../Observables/Observable+Aggregate.swift | 2 +- RxSwift/Observables/Observable+Binding.swift | 10 +++---- .../Observables/Observable+Concurrency.swift | 2 +- RxSwift/Observables/Observable+Debug.swift | 2 +- RxSwift/Observables/Observable+Multiple.swift | 20 +++++++------- RxSwift/Observables/Observable+Single.swift | 12 ++++----- ...Observable+StandardSequenceOperators.swift | 18 ++++++------- RxSwift/Observables/Observable+Time.swift | 12 ++++----- ...rvable+StandardSequenceOperatorsTest.swift | 22 +++++++++------- 10 files changed, 62 insertions(+), 64 deletions(-) diff --git a/RxSwift/Observables/Implementations/WithLatestFrom.swift b/RxSwift/Observables/Implementations/WithLatestFrom.swift index d809dbfa..b2cf3233 100644 --- a/RxSwift/Observables/Implementations/WithLatestFrom.swift +++ b/RxSwift/Observables/Implementations/WithLatestFrom.swift @@ -8,16 +8,15 @@ import Foundation -class WithLatestFromSink : Sink, ObserverType { +class WithLatestFromSink : Sink, ObserverType { - typealias Parent = WithLatestFrom - typealias SecondType = SecondO.E - typealias E = FirstO.E + typealias Parent = WithLatestFrom + typealias E = FirstType private let _parent: Parent private var _lock = NSRecursiveLock() - private var _latest: SecondO.E? + private var _latest: SecondType? init(parent: Parent, observer: O, cancel: Disposable) { @@ -60,10 +59,10 @@ class WithLatestFromSink: ObserverType { +class WithLatestFromSecond: ObserverType { - typealias Parent = WithLatestFromSink - typealias E = SecondO.E + typealias Parent = WithLatestFromSink + typealias E = SecondType private let _parent: Parent private let _disposable: Disposable @@ -91,17 +90,14 @@ class WithLatestFromSecond: Producer { - - typealias FirstType = FirstO.E - typealias SecondType = SecondO.E +class WithLatestFrom: Producer { typealias ResultSelector = (FirstType, SecondType) throws -> ResultType - private let _first: FirstO - private let _second: SecondO + private let _first: Observable + private let _second: Observable private let _resultSelector: ResultSelector - init(first: FirstO, second: SecondO, resultSelector: ResultSelector) { + init(first: Observable, second: Observable, resultSelector: ResultSelector) { _first = first _second = second _resultSelector = resultSelector diff --git a/RxSwift/Observables/Observable+Aggregate.swift b/RxSwift/Observables/Observable+Aggregate.swift index 4f14ad5a..c2a93053 100644 --- a/RxSwift/Observables/Observable+Aggregate.swift +++ b/RxSwift/Observables/Observable+Aggregate.swift @@ -10,7 +10,7 @@ import Foundation // MARK: reduce -extension ObservableType { +extension ObservableConvertibleType { /** Applies an `accumulator` function over an observable sequence, returning the result of the aggregation as a single element in the result sequence. The specified `seed` value is used as the initial accumulator value. diff --git a/RxSwift/Observables/Observable+Binding.swift b/RxSwift/Observables/Observable+Binding.swift index c0912cd2..9ccad52e 100644 --- a/RxSwift/Observables/Observable+Binding.swift +++ b/RxSwift/Observables/Observable+Binding.swift @@ -10,7 +10,7 @@ import Foundation // MARK: multicast -extension ObservableType { +extension ObservableConvertibleType { /** Multicasts the source sequence notifications through the specified subject to the resulting connectable observable. @@ -52,7 +52,7 @@ extension ObservableType { // MARK: publish -extension ObservableType { +extension ObservableConvertibleType { /** Returns a connectable observable sequence that shares a single subscription to the underlying sequence. @@ -69,7 +69,7 @@ extension ObservableType { // MARK: replay -extension ObservableType { +extension ObservableConvertibleType { /** Returns a connectable observable sequence that shares a single subscription to the underlying sequence replaying bufferSize elements. @@ -103,7 +103,7 @@ extension ConnectableObservableType { // MARK: share -extension ObservableType { +extension ObservableConvertibleType { /** Returns an observable sequence that shares a single subscription to the underlying sequence. @@ -120,7 +120,7 @@ extension ObservableType { // MARK: shareReplay -extension ObservableType { +extension ObservableConvertibleType { /** Returns an observable sequence that shares a single subscription to the underlying sequence replaying notifications subject to a maximum time length for the replay buffer. diff --git a/RxSwift/Observables/Observable+Concurrency.swift b/RxSwift/Observables/Observable+Concurrency.swift index 2bf4de71..1f5de591 100644 --- a/RxSwift/Observables/Observable+Concurrency.swift +++ b/RxSwift/Observables/Observable+Concurrency.swift @@ -10,7 +10,7 @@ import Foundation // MARK: observeOn -extension ObservableType { +extension ObservableConvertibleType { /** Wraps the source sequence in order to run its observer callbacks on the specified scheduler. diff --git a/RxSwift/Observables/Observable+Debug.swift b/RxSwift/Observables/Observable+Debug.swift index 41df23f8..4090c993 100644 --- a/RxSwift/Observables/Observable+Debug.swift +++ b/RxSwift/Observables/Observable+Debug.swift @@ -10,7 +10,7 @@ import Foundation // MARK: debug -extension ObservableType { +extension ObservableConvertibleType { /** Prints received events for all observers on standard output. diff --git a/RxSwift/Observables/Observable+Multiple.swift b/RxSwift/Observables/Observable+Multiple.swift index e35fb045..97efbf72 100644 --- a/RxSwift/Observables/Observable+Multiple.swift +++ b/RxSwift/Observables/Observable+Multiple.swift @@ -42,7 +42,7 @@ extension CollectionType where Generator.Element : ObservableConvertibleType { // MARK: switch -extension ObservableType where E : ObservableConvertibleType { +extension ObservableConvertibleType where E : ObservableConvertibleType { /** Transforms an observable sequence of observable sequences into an observable sequence @@ -61,7 +61,7 @@ extension ObservableType where E : ObservableConvertibleType { // MARK: concat -extension ObservableType { +extension ObservableConvertibleType { /** Concatenates the second observable sequence to `self` upon successful termination of `self`. @@ -89,7 +89,7 @@ extension SequenceType where Generator.Element : ObservableConvertibleType { } } -extension ObservableType where E : ObservableConvertibleType { +extension ObservableConvertibleType where E : ObservableConvertibleType { /** Concatenates all inner observable sequences, as long as the previous observable sequence terminated successfully. @@ -104,7 +104,7 @@ extension ObservableType where E : ObservableConvertibleType { // MARK: merge -extension ObservableType where E : ObservableConvertibleType { +extension ObservableConvertibleType where E : ObservableConvertibleType { /** Merges elements from all observable sequences in the given enumerable sequence into a single observable sequence. @@ -131,7 +131,7 @@ extension ObservableType where E : ObservableConvertibleType { // MARK: catch -extension ObservableType { +extension ObservableConvertibleType { /** Continues an observable sequence that is terminated by an error with the observable sequence produced by the handler. @@ -174,7 +174,7 @@ extension SequenceType where Generator.Element : ObservableConvertibleType { // MARK: takeUntil -extension ObservableType { +extension ObservableConvertibleType { /** Returns the elements from the source observable sequence until the other observable sequence produces an element. @@ -191,7 +191,7 @@ extension ObservableType { // MARK: skipUntil -extension ObservableType { +extension ObservableConvertibleType { /** Returns the elements from the source observable sequence until the other observable sequence produces an element. @@ -208,7 +208,7 @@ extension ObservableType { // MARK: amb -extension ObservableType { +extension ObservableConvertibleType { /** Propagates the observable sequence that reacts first. @@ -242,7 +242,7 @@ extension SequenceType where Generator.Element : ObservableConvertibleType { // withLatestFrom -extension ObservableType { +extension ObservableConvertibleType { /** Merges two observable sequences into one observable sequence by combining each element from self with the latest element from the second source, if any. @@ -252,6 +252,6 @@ extension ObservableType { - returns: An observable sequence containing the result of combining each element of the self with the latest element from the second source, if any, using the specified result selector function. */ public func withLatestFrom(second: SecondO, resultSelector: (E, SecondO.E) throws -> ResultType) -> Observable { - return WithLatestFrom(first: self, second: second, resultSelector: resultSelector) + return WithLatestFrom(first: self.asObservable(), second: second.asObservable(), resultSelector: resultSelector) } } diff --git a/RxSwift/Observables/Observable+Single.swift b/RxSwift/Observables/Observable+Single.swift index 4cf01d78..b1434a2b 100644 --- a/RxSwift/Observables/Observable+Single.swift +++ b/RxSwift/Observables/Observable+Single.swift @@ -10,7 +10,7 @@ import Foundation // MARK: distinct until changed -extension ObservableType where E: Equatable { +extension ObservableConvertibleType where E: Equatable { /** Returns an observable sequence that contains only distinct contiguous elements according to equality operator. @@ -24,7 +24,7 @@ extension ObservableType where E: Equatable { } } -extension ObservableType { +extension ObservableConvertibleType { /** Returns an observable sequence that contains only distinct contiguous elements according to the `keySelector`. @@ -65,7 +65,7 @@ extension ObservableType { // MARK: do -extension ObservableType { +extension ObservableConvertibleType { /** Invokes an action for each event in the observable sequence, and propagates all observer messages through the result sequence. @@ -105,7 +105,7 @@ extension ObservableType { // MARK: startWith -extension ObservableType { +extension ObservableConvertibleType { /** Prepends a sequence of values to an observable sequence. @@ -122,7 +122,7 @@ extension ObservableType { // MARK: retry -extension ObservableType { +extension ObservableConvertibleType { /** Repeats the source observable sequence until it successfully terminates. @@ -153,7 +153,7 @@ extension ObservableType { // MARK: scan -extension ObservableType { +extension ObservableConvertibleType { /** Applies an accumulator function over an observable sequence and returns each intermediate result. The specified seed value is used as the initial accumulator value. diff --git a/RxSwift/Observables/Observable+StandardSequenceOperators.swift b/RxSwift/Observables/Observable+StandardSequenceOperators.swift index 5278c3de..6ff01147 100644 --- a/RxSwift/Observables/Observable+StandardSequenceOperators.swift +++ b/RxSwift/Observables/Observable+StandardSequenceOperators.swift @@ -10,7 +10,7 @@ import Foundation // MARK: filter aka where -extension ObservableType { +extension ObservableConvertibleType { /** Filters the elements of an observable sequence based on a predicate. @@ -27,7 +27,7 @@ extension ObservableType { // MARK: takeWhile -extension ObservableType { +extension ObservableConvertibleType { /** Returns elements from an observable sequence as long as a specified condition is true. @@ -58,7 +58,7 @@ extension ObservableType { // MARK: take -extension ObservableType { +extension ObservableConvertibleType { /** Returns a specified number of contiguous elements from the start of an observable sequence. @@ -80,7 +80,7 @@ extension ObservableType { // MARK: takeLast -extension ObservableType { +extension ObservableConvertibleType { /** Returns a specified number of contiguous elements from the end of an observable sequence. @@ -100,7 +100,7 @@ extension ObservableType { // MARK: skip -extension ObservableType { +extension ObservableConvertibleType { /** Bypasses a specified number of elements in an observable sequence and then returns the remaining elements. @@ -117,7 +117,7 @@ extension ObservableType { // MARK: SkipWhile -extension ObservableType { +extension ObservableConvertibleType { /** Bypasses elements in an observable sequence as long as a specified condition is true and then returns the remaining elements. @@ -145,7 +145,7 @@ extension ObservableType { // MARK: map aka select -extension ObservableType { +extension ObservableConvertibleType { /** Projects each element of an observable sequence into a new form. @@ -174,7 +174,7 @@ extension ObservableType { // MARK: flatMap -extension ObservableType { +extension ObservableConvertibleType { /** Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence. @@ -203,7 +203,7 @@ extension ObservableType { // elementAt -extension ObservableType { +extension ObservableConvertibleType { /** Returns a sequence emitting only item _n_ emitted by an Observable diff --git a/RxSwift/Observables/Observable+Time.swift b/RxSwift/Observables/Observable+Time.swift index 6917b79a..262df3bf 100644 --- a/RxSwift/Observables/Observable+Time.swift +++ b/RxSwift/Observables/Observable+Time.swift @@ -9,7 +9,7 @@ import Foundation // MARK: throttle -extension ObservableType { +extension ObservableConvertibleType { /** Ignores elements from an observable sequence which are followed by another element within a specified relative time duration, using the specified scheduler to run throttling timers. @@ -44,7 +44,7 @@ extension ObservableType { // MARK: sample -extension ObservableType { +extension ObservableConvertibleType { /** Samples the source observable sequence using a samper observable sequence producing sampling ticks. @@ -136,7 +136,7 @@ public func timer(dueTime: S.TimeInterval, _ scheduler: S) // MARK: take -extension ObservableType { +extension ObservableConvertibleType { /** Takes elements for the specified duration from the start of the observable source sequence, using the specified scheduler to run timers. @@ -154,7 +154,7 @@ extension ObservableType { // MARK: skip -extension ObservableType { +extension ObservableConvertibleType { /** Skips elements for the specified duration from the start of the observable source sequence, using the specified scheduler to run timers. @@ -173,7 +173,7 @@ extension ObservableType { // MARK: delaySubscription -extension ObservableType { +extension ObservableConvertibleType { /** Time shifts the observable sequence by delaying the subscription with the specified relative time duration, using the specified scheduler to run timers. @@ -191,7 +191,7 @@ extension ObservableType { // MARK: buffer -extension ObservableType { +extension ObservableConvertibleType { /** Projects each element of an observable sequence into a buffer that's sent out when either it's full or a given amount of time has elapsed, using the specified scheduler to run timers. diff --git a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift index 5015bac7..3440ee91 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift @@ -11,7 +11,7 @@ import XCTest import RxSwift -class ObservableStandardSequenceOperators : RxTest { +class ObservableStandardSequenceOperatorsTest : RxTest { override func setUp() { super.setUp() } @@ -37,7 +37,7 @@ func isPrime(i: Int) -> Bool { } // MARK: where -extension ObservableStandardSequenceOperators { +extension ObservableStandardSequenceOperatorsTest { func test_filterComplete() { let scheduler = TestScheduler(initialClock: 0) @@ -210,7 +210,7 @@ extension ObservableStandardSequenceOperators { } // MARK: takeWhile -extension ObservableStandardSequenceOperators { +extension ObservableStandardSequenceOperatorsTest { func testTakeWhile_Complete_Before() { let scheduler = TestScheduler(initialClock: 0) @@ -702,7 +702,7 @@ extension ObservableStandardSequenceOperators { // MARK: map // these test are not port from Rx -extension ObservableStandardSequenceOperators { +extension ObservableStandardSequenceOperatorsTest { func testMap_Never() { let scheduler = TestScheduler(initialClock: 0) @@ -1043,7 +1043,7 @@ extension ObservableStandardSequenceOperators { } // MARK: flatMap -extension ObservableStandardSequenceOperators { +extension ObservableStandardSequenceOperatorsTest { func testFlatMap_Complete() { let scheduler = TestScheduler(initialClock: 0) @@ -2354,7 +2354,7 @@ extension ObservableStandardSequenceOperators { // MARK: take -extension ObservableStandardSequenceOperators { +extension ObservableStandardSequenceOperatorsTest { func testTake_Complete_After() { let scheduler = TestScheduler(initialClock: 0) @@ -2818,7 +2818,7 @@ extension ObservableStandardSequenceOperators { // MARK: takeLast -extension ObservableStandardSequenceOperators { +extension ObservableStandardSequenceOperatorsTest { func testTakeLast_Complete_Less() { let scheduler = TestScheduler(initialClock: 0) @@ -3061,11 +3061,13 @@ extension ObservableStandardSequenceOperators { _ = k.takeLast(1).subscribeNext { n in k.on(.Next(!n)) } + + k.on(.Completed) } } // MARK: skip -extension ObservableStandardSequenceOperators { +extension ObservableStandardSequenceOperatorsTest { func testSkip_Complete_After() { let scheduler = TestScheduler(initialClock: 0) @@ -3459,7 +3461,7 @@ extension ObservableStandardSequenceOperators { } // MARK: SkipWhile -extension ObservableStandardSequenceOperators { +extension ObservableStandardSequenceOperatorsTest { func testSkipWhile_Complete_Before() { let scheduler = TestScheduler(initialClock: 0) @@ -3894,7 +3896,7 @@ extension ObservableStandardSequenceOperators { } // MARK: elementAt -extension ObservableStandardSequenceOperators { +extension ObservableStandardSequenceOperatorsTest { func testElementAt_Complete_After() { let scheduler = TestScheduler(initialClock: 0) From 1dbe06a08223eb07b80f75de77fff4c364ec2785 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 1 Nov 2015 20:12:41 +0100 Subject: [PATCH 129/210] Improves example app. --- RxExample/RxExample.xcodeproj/project.pbxproj | 24 ++- .../GitHubSearchRepositoriesAPI.swift | 159 ++++++++++++++---- ...tHubSearchRepositoriesViewController.swift | 76 ++++++--- .../ViewModels/SearchResultViewModel.swift | 2 +- .../Views/CollectionViewImageCell.swift | 3 +- .../Views/WikipediaSearchCell.swift | 5 +- .../RxExample/Services/ImageService.swift | 89 +++++----- .../Services/ReachabilityService.swift | 27 ++- .../Services/UIImage+Extensions.swift | 23 +++ RxExample/RxExample/Services/Wireframe.swift | 90 ++++++++++ RxExample/RxExample/Wireframe.swift | 30 ---- RxExample/RxExample/iOS/Main.storyboard | 10 +- 12 files changed, 380 insertions(+), 158 deletions(-) create mode 100644 RxExample/RxExample/Services/UIImage+Extensions.swift create mode 100644 RxExample/RxExample/Services/Wireframe.swift delete mode 100644 RxExample/RxExample/Wireframe.swift diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index 09ff701e..a9eff03c 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -33,6 +33,12 @@ C803973B1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; }; C80397491BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */; }; C803974A1BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */; }; + C809E97A1BE6841C0058D948 /* Wireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = C809E9791BE6841C0058D948 /* Wireframe.swift */; }; + C809E97B1BE6841C0058D948 /* Wireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = C809E9791BE6841C0058D948 /* Wireframe.swift */; }; + C809E97D1BE697100058D948 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C809E97C1BE697100058D948 /* UIImage+Extensions.swift */; }; + C809E97E1BE697100058D948 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C809E97C1BE697100058D948 /* UIImage+Extensions.swift */; }; + C809E97F1BE69B660058D948 /* Wireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = C809E9791BE6841C0058D948 /* Wireframe.swift */; }; + C809E9801BE69BA30058D948 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C809E97C1BE697100058D948 /* UIImage+Extensions.swift */; }; C80DDE881BCDAA0F006A1832 /* SkipWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE7A1BCDA952006A1832 /* SkipWhile.swift */; }; C80DDED21BCE9046006A1832 /* ControlEvent+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC21BCE9041006A1832 /* ControlEvent+Driver.swift */; }; C80DDED31BCE9046006A1832 /* ControlProperty+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC31BCE9041006A1832 /* ControlProperty+Driver.swift */; }; @@ -73,7 +79,6 @@ C8297E4C1B6CF905000589EA /* APIWrappersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075F130F1B4E9D5A000D7861 /* APIWrappersViewController.swift */; }; C8297E4D1B6CF905000589EA /* RxTableViewSectionedReloadDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88C78651B3EB0A00061C5AB /* RxTableViewSectionedReloadDataSource.swift */; }; C8297E4E1B6CF905000589EA /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C859B9A91B45CB0900D012D7 /* RxCollectionViewSectionedAnimatedDataSource.swift */; }; - C8297E4F1B6CF905000589EA /* Wireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83367211AD029AE00C668A7 /* Wireframe.swift */; }; C8297E501B6CF905000589EA /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E300061B14995F00F00100 /* TableViewController.swift */; }; C8297E511B6CF905000589EA /* PartialUpdatesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C859B9A31B45C5D900D012D7 /* PartialUpdatesViewController.swift */; }; C8297E521B6CF905000589EA /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E3C2321B03605B0010338D /* Dependencies.swift */; }; @@ -91,7 +96,6 @@ C83367231AD029AE00C668A7 /* Example.swift in Sources */ = {isa = PBXBuildFile; fileRef = C833670F1AD029AE00C668A7 /* Example.swift */; }; C83367241AD029AE00C668A7 /* HtmlParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83367111AD029AE00C668A7 /* HtmlParsing.swift */; }; C83367251AD029AE00C668A7 /* ImageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83367121AD029AE00C668A7 /* ImageService.swift */; }; - C83367311AD029AE00C668A7 /* Wireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83367211AD029AE00C668A7 /* Wireframe.swift */; }; C84B91381B8A282000C9CCCF /* RxTableViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88C78631B3EB0A00061C5AB /* RxTableViewSectionedAnimatedDataSource.swift */; }; C84B91391B8A282000C9CCCF /* RxTableViewSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88C78641B3EB0A00061C5AB /* RxTableViewSectionedDataSource.swift */; }; C84B913A1B8A282000C9CCCF /* RxTableViewSectionedReloadDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88C78651B3EB0A00061C5AB /* RxTableViewSectionedReloadDataSource.swift */; }; @@ -114,7 +118,6 @@ C88BB8BF1B07E6C90064D411 /* WikipediaSearchResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86E2F3D1AE5A0CA00C31024 /* WikipediaSearchResult.swift */; }; C88BB8C31B07E6C90064D411 /* Example.swift in Sources */ = {isa = PBXBuildFile; fileRef = C833670F1AD029AE00C668A7 /* Example.swift */; }; C88BB8C41B07E6C90064D411 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C890A65C1AEC084100AFF7E6 /* ViewController.swift */; }; - C88BB8C61B07E6C90064D411 /* Wireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83367211AD029AE00C668A7 /* Wireframe.swift */; }; C88BB8C71B07E6C90064D411 /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E3C2321B03605B0010338D /* Dependencies.swift */; }; C88BB8CA1B07E6C90064D411 /* WikipediaAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86E2F3B1AE5A0CA00C31024 /* WikipediaAPI.swift */; }; C88BB8CC1B07E6C90064D411 /* WikipediaPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86E2F3C1AE5A0CA00C31024 /* WikipediaPage.swift */; }; @@ -470,6 +473,8 @@ B1B7C3CF1BE006870076934E /* TakeLast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TakeLast.swift; sourceTree = ""; }; C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = ""; }; C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubSearchRepositoriesAPI.swift; sourceTree = ""; }; + C809E9791BE6841C0058D948 /* Wireframe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Wireframe.swift; sourceTree = ""; }; + C809E97C1BE697100058D948 /* UIImage+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Extensions.swift"; sourceTree = ""; }; C80DDE7A1BCDA952006A1832 /* SkipWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipWhile.swift; sourceTree = ""; }; C80DDEC21BCE9041006A1832 /* ControlEvent+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControlEvent+Driver.swift"; sourceTree = ""; }; C80DDEC31BCE9041006A1832 /* ControlProperty+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControlProperty+Driver.swift"; sourceTree = ""; }; @@ -485,7 +490,6 @@ C833670F1AD029AE00C668A7 /* Example.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Example.swift; sourceTree = ""; }; C83367111AD029AE00C668A7 /* HtmlParsing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HtmlParsing.swift; sourceTree = ""; }; C83367121AD029AE00C668A7 /* ImageService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageService.swift; sourceTree = ""; }; - C83367211AD029AE00C668A7 /* Wireframe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Wireframe.swift; sourceTree = ""; }; C84CC52D1BDC344100E06A64 /* ElementAt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElementAt.swift; sourceTree = ""; }; C859B9A31B45C5D900D012D7 /* PartialUpdatesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PartialUpdatesViewController.swift; sourceTree = ""; }; C859B9A51B45C80700D012D7 /* RxCollectionViewSectionedReloadDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewSectionedReloadDataSource.swift; sourceTree = ""; }; @@ -867,7 +871,6 @@ 07E3C2321B03605B0010338D /* Dependencies.swift */, C890A65C1AEC084100AFF7E6 /* ViewController.swift */, C833670F1AD029AE00C668A7 /* Example.swift */, - C83367211AD029AE00C668A7 /* Wireframe.swift */, C8DF92D11B0B2F8C009BCF9A /* OSX */, C8DF92C71B0B2F84009BCF9A /* iOS */, C83366E01AD0293800C668A7 /* Supporting Files */, @@ -887,6 +890,7 @@ C83367101AD029AE00C668A7 /* Services */ = { isa = PBXGroup; children = ( + C809E9791BE6841C0058D948 /* Wireframe.swift */, C83367111AD029AE00C668A7 /* HtmlParsing.swift */, C83367121AD029AE00C668A7 /* ImageService.swift */, C8A2A2C71B4049E300F11F09 /* PseudoRandomGenerator.swift */, @@ -896,6 +900,7 @@ B18F3BE11BDB2E8F000AAC79 /* ReachabilityService.swift */, B1604CB41BE49F8D002E1279 /* DownloadableImage.swift */, B1604CC81BE5BBFA002E1279 /* UIImageView+DownloadableImage.swift */, + C809E97C1BE697100058D948 /* UIImage+Extensions.swift */, ); path = Services; sourceTree = ""; @@ -1740,6 +1745,7 @@ C89465871BC6C2BC0055219D /* RxCollectionViewDelegateProxy.swift in Sources */, D2245A191BD5654C00E7146F /* WithLatestFrom.swift in Sources */, C8297E3D1B6CF905000589EA /* SearchViewModel.swift in Sources */, + C809E97E1BE697100058D948 /* UIImage+Extensions.swift in Sources */, C89464E61BC6C2B00055219D /* Timer.swift in Sources */, C8297E3E1B6CF905000589EA /* DetailViewController.swift in Sources */, C8297E3F1B6CF905000589EA /* SectionModelType.swift in Sources */, @@ -1773,6 +1779,7 @@ C8297E461B6CF905000589EA /* Example.swift in Sources */, C89465081BC6C2B00055219D /* PublishSubject.swift in Sources */, C89464FC1BC6C2B00055219D /* RxBox.swift in Sources */, + C809E97B1BE6841C0058D948 /* Wireframe.swift in Sources */, C89465811BC6C2BC0055219D /* ItemEvents.swift in Sources */, C89465861BC6C2BC0055219D /* RxCollectionViewDataSourceProxy.swift in Sources */, C89465981BC6C2BC0055219D /* UISearchBar+Rx.swift in Sources */, @@ -1814,7 +1821,6 @@ C89464EF1BC6C2B00055219D /* Observable+Debug.swift in Sources */, C89464E91BC6C2B00055219D /* Zip+CollectionType.swift in Sources */, C89465761BC6C2BC0055219D /* KVOObserver.swift in Sources */, - C8297E4F1B6CF905000589EA /* Wireframe.swift in Sources */, C89465641BC6C2BC0055219D /* _RXSwizzling.m in Sources */, C89464CA1BC6C2B00055219D /* Empty.swift in Sources */, C803973B1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */, @@ -1883,6 +1889,7 @@ C803973A1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */, C84B913D1B8A282000C9CCCF /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */, C8A750201B94E78200D8D046 /* RxDataSourceStarterKit.swift in Sources */, + C809E97A1BE6841C0058D948 /* Wireframe.swift in Sources */, 0706E1961B14AF5100BA2D3A /* DetailViewController.swift in Sources */, C88C78991B4012A90061C5AB /* SectionModelType.swift in Sources */, C83367251AD029AE00C668A7 /* ImageService.swift in Sources */, @@ -1901,7 +1908,6 @@ C890A6581AEBD26B00AFF7E6 /* GitHubSignupViewController.swift in Sources */, B1604CB51BE49F8D002E1279 /* DownloadableImage.swift in Sources */, 075F13101B4E9D5A000D7861 /* APIWrappersViewController.swift in Sources */, - C83367311AD029AE00C668A7 /* Wireframe.swift in Sources */, 07E300071B14995F00F00100 /* TableViewController.swift in Sources */, B18F3BE21BDB2E8F000AAC79 /* ReachabilityService.swift in Sources */, B18F3BBC1BD92EC8000AAC79 /* Reachability.swift in Sources */, @@ -1911,6 +1917,7 @@ C86E2F451AE5A0CA00C31024 /* WikipediaAPI.swift in Sources */, C8DF92CD1B0B2F84009BCF9A /* AppDelegate.swift in Sources */, C86E2F461AE5A0CA00C31024 /* WikipediaPage.swift in Sources */, + C809E97D1BE697100058D948 /* UIImage+Extensions.swift in Sources */, C8A2A2CB1B404A1200F11F09 /* Randomizer.swift in Sources */, 07E300091B149A2A00F00100 /* User.swift in Sources */, ); @@ -1920,6 +1927,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C809E97F1BE69B660058D948 /* Wireframe.swift in Sources */, C8DF92DF1B0B328B009BCF9A /* AppDelegate.swift in Sources */, C88BB8BB1B07E6C90064D411 /* SearchResultViewModel.swift in Sources */, C88BB8BC1B07E6C90064D411 /* HtmlParsing.swift in Sources */, @@ -1932,9 +1940,9 @@ B18F3BBF1BD93DFF000AAC79 /* Reachability.swift in Sources */, C88BB8C31B07E6C90064D411 /* Example.swift in Sources */, C88BB8C41B07E6C90064D411 /* ViewController.swift in Sources */, - C88BB8C61B07E6C90064D411 /* Wireframe.swift in Sources */, C88BB8C71B07E6C90064D411 /* Dependencies.swift in Sources */, C88BB8CA1B07E6C90064D411 /* WikipediaAPI.swift in Sources */, + C809E9801BE69BA30058D948 /* UIImage+Extensions.swift in Sources */, B1604CCA1BE5BC18002E1279 /* DownloadableImage.swift in Sources */, C88BB8CC1B07E6C90064D411 /* WikipediaPage.swift in Sources */, ); diff --git a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift index 2a131e86..36323cc8 100644 --- a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift +++ b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift @@ -11,6 +11,9 @@ import Foundation import RxSwift #endif +/** + Parsed GitHub respository. +*/ struct Repository: CustomStringConvertible { var name: String var url: String @@ -25,19 +28,76 @@ struct Repository: CustomStringConvertible { } } +/** +ServiceState state. +*/ +enum ServiceState { + case Online + case Offline +} + +/** + Raw response from GitHub API +*/ enum SearchRepositoryResponse { - case Repositories([Repository]) + /** + New repositories just fetched + */ + case Repositories(repositories: [Repository], nextURL: NSURL?) + + /** + In case there was some problem fetching data from service, this will be returned. + It really doesn't matter if that is a failure in network layer, parsing error or something else. + In case data can't be read and parsed properly, something is wrong with server response. + */ + case ServiceOffline + + /** + This example uses unauthenticated GitHub API. That API does have throttling policy and you won't + be able to make more then 10 requests per minute. + + That is actually an awesome scenario to demonstrate complex retries using alert views and combination of timers. + + Just search like mad, and everything will be handled right. + */ case LimitExceeded } +/** + This is the final result of loading. Crème de la crème. +*/ +struct RepositoriesState { + /** + List of parsed repositories ready to be shown in the UI. + */ + let repositories: [Repository] + + /** + Current network state. + */ + let serviceState: ServiceState? + + /** + Limit exceeded + */ + let limitExceeded: Bool + + static let empty = RepositoriesState(repositories: [], serviceState: nil, limitExceeded: false) +} + class GitHubSearchRepositoriesAPI { - static let sharedAPI = GitHubSearchRepositoriesAPI() + static let sharedAPI = GitHubSearchRepositoriesAPI(wireframe: DefaultWireframe()) let activityIndicator = ActivityIndicator() - private init() { + // Why would network service have wireframe service? It's here to abstract promting user + // Do we really want to make this example project factory/fascade/service competition? :) + private let _wireframe: Wireframe + + private init(wireframe: Wireframe) { + _wireframe = wireframe } private static let parseLinksPattern = "\\s*,?\\s*<([^\\>]*)>\\s*;\\s*rel=\"([^\"]*)\"" @@ -90,53 +150,82 @@ class GitHubSearchRepositoriesAPI { /** Public fascade for search. */ - func search(query: String, loadNextPageTrigger: Observable) -> Observable { + func search(query: String, loadNextPageTrigger: Observable) -> Observable { let escapedQuery = URLEscape(query) let url = NSURL(string: "https://api.github.com/search/repositories?q=\(escapedQuery)")! return recursivelySearch([], loadNextURL: url, loadNextPageTrigger: loadNextPageTrigger) - .startWith(.Repositories([])) + // Here we go again + .startWith(RepositoriesState.empty) } - private func recursivelySearch(loadedSoFar: [Repository], loadNextURL: NSURL, loadNextPageTrigger: Observable) -> Observable { - return loadSearchURL(loadNextURL) - .retry(3) - .flatMap { (newPageRepositoriesResponse, nextURL) -> Observable in - // in case access denied, just stop - guard case .Repositories(let newPageRepositories) = newPageRepositoriesResponse else { - return just(newPageRepositoriesResponse) - } - - var loadedRepositories = loadedSoFar - loadedRepositories.appendContentsOf(newPageRepositories) - - // if next page can't be loaded, just return what was loaded, and stop - guard let nextURL = nextURL else { - return just(.Repositories(loadedRepositories)) - } - - return [ - // return loaded immediately - just(.Repositories(loadedRepositories)), - // wait until next page can be loaded - never().takeUntil(loadNextPageTrigger), - // load next page - self.recursivelySearch(loadedRepositories, loadNextURL: nextURL, loadNextPageTrigger: loadNextPageTrigger) - ].concat() + private func recursivelySearch(loadedSoFar: [Repository], loadNextURL: NSURL, loadNextPageTrigger: Observable) -> Observable { + return loadSearchURL(loadNextURL).flatMap { searchResponse -> Observable in + switch searchResponse { + /** + If service is offline, that's ok, that means that this isn't the last thing we've heard from that API. + It will retry until either battery drains, you become angry and close the app or evil machine comes back + from the future, steals your device and Googles Sarah Connor's address. + */ + case .ServiceOffline: + return just(RepositoriesState(repositories: loadedSoFar, serviceState: .Offline, limitExceeded: false)) + case .LimitExceeded: + return just(RepositoriesState(repositories: loadedSoFar, serviceState: .Online, limitExceeded: true)) + case .Repositories: + break } + + // Repositories without next url? The party is done. + guard case .Repositories(let newPageRepositories, let maybeNextURL) = searchResponse else { + fatalError("Some fourth case?") + } + + var loadedRepositories = loadedSoFar + loadedRepositories.appendContentsOf(newPageRepositories) + + let appenedRepositories = RepositoriesState(repositories: loadedRepositories, serviceState: .Online, limitExceeded: false) + + // if next page can't be loaded, just return what was loaded, and stop + guard let nextURL = maybeNextURL else { + return just(appenedRepositories) + } + + return [ + // return loaded immediately + just(appenedRepositories), + // wait until next page can be loaded + never().takeUntil(loadNextPageTrigger), + // load next page + self.recursivelySearch(loadedRepositories, loadNextURL: nextURL, loadNextPageTrigger: loadNextPageTrigger) + ].concat() + } } - private func loadSearchURL(searchURL: NSURL) -> Observable<(response: SearchRepositoryResponse, nextURL: NSURL?)> { + /** + Displays UI that prompts the user when to retry. + */ + private func buildRetryPrompt() -> Observable { + return _wireframe.promptFor( + "Exceeded limit of 10 non authenticated requests per minute for GitHub API. Please wait a minute. :(\nhttps://developer.github.com/v3/#rate-limiting", + cancelAction: RetryResult.Cancel, + actions: [RetryResult.Retry] + ) + .filter { (x: RetryResult) in x == .Retry } + .map { _ in () } + } + + private func loadSearchURL(searchURL: NSURL) -> Observable { return NSURLSession.sharedSession() .rx_response(NSURLRequest(URL: searchURL)) + .retry(3) .trackActivity(self.activityIndicator) .observeOn(Dependencies.sharedDependencies.backgroundWorkScheduler) - .map { data, response in + .map { data, response -> SearchRepositoryResponse in guard let httpResponse = response as? NSHTTPURLResponse else { throw exampleError("not getting http response") } if httpResponse.statusCode == 403 { - return (response: .LimitExceeded, nextURL: nil) + return .LimitExceeded } let jsonRoot = try GitHubSearchRepositoriesAPI.parseJSON(httpResponse, data: data) @@ -149,9 +238,9 @@ class GitHubSearchRepositoriesAPI { let nextURL = try GitHubSearchRepositoriesAPI.parseNextURL(httpResponse) - return (response: .Repositories(repositories), nextURL: nextURL) + return .Repositories(repositories: repositories, nextURL: nextURL) } - .observeOn(Dependencies.sharedDependencies.mainScheduler) + .retryOnBecomesReachable(.ServiceOffline, reachabilityService: ReachabilityService.sharedReachabilityService) } private static func parseJSON(httpResponse: NSHTTPURLResponse, data: NSData) throws -> AnyObject { diff --git a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift index 8ddb7326..328fd062 100644 --- a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift +++ b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift @@ -12,6 +12,33 @@ import RxSwift import RxCocoa #endif +struct Colors { + static let OfflineColor = UIColor(red: 1.0, green: 0.6, blue: 0.6, alpha: 1.0) + static let OnlineColor = nil as UIColor? +} + +extension UINavigationController { + var rx_serviceState: AnyObserver { + return AnyObserver { event in + switch event { + case .Next(let maybeServiceState): + // if nil is being bound, then don't change color, it's not perfect, but :) + if let serviceState = maybeServiceState { + let isOffline = serviceState ?? .Online == .Offline + + self.navigationBar.backgroundColor = isOffline + ? Colors.OfflineColor + : Colors.OnlineColor + } + case .Error(let error): + bindingErrorToInterface(error) + case .Completed: + break + } + } + } +} + class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegate { static let startLoadingOffset: CGFloat = 20.0 @@ -19,12 +46,10 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat return contentOffset.y + tableView.frame.size.height + startLoadingOffset > tableView.contentSize.height } - @IBOutlet weak var tableView: UITableView! @IBOutlet weak var searchBar: UISearchBar! var disposeBag = DisposeBag() - let repositories = Variable([Repository]()) let dataSource = RxTableViewSectionedReloadDataSource>() override func viewDidLoad() { @@ -35,11 +60,6 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat let tableView = self.tableView let searchBar = self.searchBar - let allRepositories = repositories - .map { repositories in - return [SectionModel(model: "Repositories", items: repositories)] - } - dataSource.cellFactory = { (tv, ip, repository: Repository) in let cell = tv.dequeueReusableCellWithIdentifier("Cell")! cell.textLabel?.text = repository.name @@ -53,11 +73,6 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat } - // reactive data source - allRepositories - .bindTo(tableView.rx_itemsWithDataSource(dataSource)) - .addDisposableTo(disposeBag) - let loadNextPageTrigger = tableView.rx_contentOffset .flatMap { offset in GitHubSearchRepositoriesViewController.isNearTheBottomEdge(offset, tableView) @@ -65,27 +80,33 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat : empty() } - searchBar.rx_text + let searchResult = searchBar.rx_text.asDriver() .throttle(0.3, $.mainScheduler) .distinctUntilChanged() - .map { query -> Observable in + .map { query -> Driver in if query.isEmpty { - return just(.Repositories([])) + return Drive.just(RepositoriesState.empty) } else { return GitHubSearchRepositoriesAPI.sharedAPI.search(query, loadNextPageTrigger: loadNextPageTrigger) - .retry(3) - .retryOnBecomesReachable(.Repositories([]), reachabilityService: ReachabilityService.sharedReachabilityService) + .asDriver(onErrorJustReturn: RepositoriesState.empty) } } .switchLatest() - .subscribeNext { [unowned self] result in - switch result { - case .Repositories(let repositories): - self.repositories.value = repositories - case .LimitExceeded: - self.repositories.value = [] - showAlert("Exceeded limit of 10 non authenticated requests per minute for GitHub API. Please wait a minute. :(\nhttps://developer.github.com/v3/#rate-limiting") - } + + searchResult + .map { $0.serviceState } + .drive(navigationController!.rx_serviceState) + .addDisposableTo(disposeBag) + + searchResult + .map { [SectionModel(model: "Repositories", items: $0.repositories)] } + .drive(tableView.rx_itemsWithDataSource(dataSource)) + .addDisposableTo(disposeBag) + + searchResult + .flatMap { $0.limitExceeded ? Drive.just() : Drive.empty() } + .driveNext { n in + showAlert("Exceeded limit of 10 non authenticated requests per minute for GitHub API. Please wait a minute. :(\nhttps://developer.github.com/v3/#rate-limiting") } .addDisposableTo(disposeBag) @@ -118,4 +139,9 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 30 } + + deinit { + // I know, I know, this isn't a good place of truth, but it's no + self.navigationController?.navigationBar.backgroundColor = nil + } } diff --git a/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchResultViewModel.swift b/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchResultViewModel.swift index 6a4e0d12..e69aa90d 100644 --- a/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchResultViewModel.swift +++ b/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchResultViewModel.swift @@ -51,7 +51,7 @@ class SearchResultViewModel { return "\(searchResult.title) loading ..." } } - .retryOnBecomesReachable("loading...", reachabilityService: ReachabilityService.sharedReachabilityService) + .retryOnBecomesReachable("⚠️ Service offline ⚠️", reachabilityService: ReachabilityService.sharedReachabilityService) } func configureImageURLs() -> Observable<[NSURL]> { diff --git a/RxExample/RxExample/Examples/WikipediaImageSearch/Views/CollectionViewImageCell.swift b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/CollectionViewImageCell.swift index 25c703cf..daa97598 100644 --- a/RxExample/RxExample/Examples/WikipediaImageSearch/Views/CollectionViewImageCell.swift +++ b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/CollectionViewImageCell.swift @@ -23,7 +23,8 @@ public class CollectionViewImageCell: UICollectionViewCell { let disposeBag = DisposeBag() self.downloadableImage? - .bindTo(imageOutlet.rxex_downloadableImageAnimated(kCATransitionFade)) + .asDriver(onErrorJustReturn: DownloadableImage.OfflinePlaceholder) + .drive(imageOutlet.rxex_downloadableImageAnimated(kCATransitionFade)) .addDisposableTo(disposeBag) self.disposeBag = disposeBag diff --git a/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchCell.swift b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchCell.swift index 62ccfe0a..4d0dfa62 100644 --- a/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchCell.swift +++ b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchCell.swift @@ -41,9 +41,8 @@ public class WikipediaSearchCell: UITableViewCell { viewModel.imageURLs .drive(self.imagesOutlet.rx_itemsWithCellIdentifier("ImageCell")) { [unowned self] (_, URL, cell: CollectionViewImageCell) in - - cell.downloadableImage = self.imageService.downloadableImageFromURLWithRetryIfUnreachable(URL) - } + cell.downloadableImage = self.imageService.imageFromURL(URL) + } .addDisposableTo(disposeBag) self.disposeBag = disposeBag diff --git a/RxExample/RxExample/Services/ImageService.swift b/RxExample/RxExample/Services/ImageService.swift index 538209fa..d8b4358e 100644 --- a/RxExample/RxExample/Services/ImageService.swift +++ b/RxExample/RxExample/Services/ImageService.swift @@ -19,8 +19,8 @@ import RxCocoa #endif protocol ImageService { - func imageFromURL(URL: NSURL) -> Observable - func downloadableImageFromURLWithRetryIfUnreachable(URL: NSURL) -> Observable} + func imageFromURL(URL: NSURL) -> Observable +} class DefaultImageService: ImageService { @@ -29,21 +29,21 @@ class DefaultImageService: ImageService { let $: Dependencies = Dependencies.sharedDependencies // 1st level cache - let imageCache = NSCache() - + private let _imageCache = NSCache() + // 2nd level cache - let imageDataCache = NSCache() + private let _imageDataCache = NSCache() let loadingImage = ActivityIndicator() private init() { // cost is approx memory usage - self.imageDataCache.totalCostLimit = 10 * MB + _imageDataCache.totalCostLimit = 10 * MB - self.imageCache.countLimit = 20 + _imageCache.countLimit = 20 } - func decodeImage(imageData: NSData) -> Observable { + private func decodeImage(imageData: NSData) -> Observable { return just(imageData) .observeOn($.backgroundWorkScheduler) .map { data in @@ -51,53 +51,56 @@ class DefaultImageService: ImageService { // some error throw apiError("Decoding image error") } - return image + return image.forceLazyImageDecompression() } - .observeOn($.mainScheduler) } - func imageFromURL(URL: NSURL) -> Observable { + private func _imageFromURL(URL: NSURL) -> Observable { return deferred { - let maybeImage = self.imageCache.objectForKey(URL) as? Image - - let decodedImage: Observable - - // best case scenario, it's already decoded an in memory - if let image = maybeImage { - decodedImage = just(image) - } - else { - let cachedData = self.imageDataCache.objectForKey(URL) as? NSData + let maybeImage = self._imageCache.objectForKey(URL) as? Image + + let decodedImage: Observable - // does image data cache contain anything - if let cachedData = cachedData { - decodedImage = self.decodeImage(cachedData) + // best case scenario, it's already decoded an in memory + if let image = maybeImage { + decodedImage = just(image) } else { - // fetch from network - decodedImage = self.$.URLSession.rx_data(NSURLRequest(URL: URL)) - .doOn(onNext: { data in - self.imageDataCache.setObject(data, forKey: URL) - }) - .flatMap(self.decodeImage) - .trackActivity(self.loadingImage) + let cachedData = self._imageDataCache.objectForKey(URL) as? NSData + + // does image data cache contain anything + if let cachedData = cachedData { + decodedImage = self.decodeImage(cachedData) + } + else { + // fetch from network + decodedImage = self.$.URLSession.rx_data(NSURLRequest(URL: URL)) + .doOn(onNext: { data in + self._imageDataCache.setObject(data, forKey: URL) + }) + .flatMap(self.decodeImage) + .trackActivity(self.loadingImage) + } } + + return decodedImage.doOn(onNext: { image in + self._imageCache.setObject(image, forKey: URL) + }) } - - return decodedImage.doOn(onNext: { image in - self.imageCache.setObject(image, forKey: URL) - }) - } - .observeOn($.mainScheduler) } - func downloadableImageFromURLWithRetryIfUnreachable(URL: NSURL) -> Observable { - return deferred{ - self.imageFromURL(URL) + /** + Service that tries to download image from URL. + + In case there were some problems with network connectivity and image wasn't downloaded, automatic retry will be fired when networks becomes + available. + + After image is sucessfully downloaded, sequence is completed. + */ + func imageFromURL(URL: NSURL) -> Observable { + return _imageFromURL(URL) .map { DownloadableImage.Content(image: $0) } - .retryOnBecomesReachable( DownloadableImage.OfflinePlaceholder, reachabilityService: ReachabilityService.sharedReachabilityService) + .retryOnBecomesReachable(DownloadableImage.OfflinePlaceholder, reachabilityService: ReachabilityService.sharedReachabilityService) .startWith(.Content(image: Image())) - } - .observeOn($.mainScheduler) } } diff --git a/RxExample/RxExample/Services/ReachabilityService.swift b/RxExample/RxExample/Services/ReachabilityService.swift index 46031054..8b702585 100644 --- a/RxExample/RxExample/Services/ReachabilityService.swift +++ b/RxExample/RxExample/Services/ReachabilityService.swift @@ -11,7 +11,7 @@ import RxSwift #endif public enum ReachabilityStatus{ - case Reachable,Unreachable + case Reachable, Unreachable } class ReachabilityService{ @@ -19,8 +19,8 @@ class ReachabilityService{ private let reachabilityRef = try! Reachability.reachabilityForInternetConnection() private let _reachabilityChangedSubject = PublishSubject() - private var reachabilityChanged:Observable { - get{ + private var reachabilityChanged: Observable { + get { return _reachabilityChangedSubject.asObservable() } } @@ -43,11 +43,24 @@ class ReachabilityService{ } extension ObservableConvertibleType { - func retryOnBecomesReachable(valueOnFailure:E, reachabilityService:ReachabilityService)->Observable{ - return self.asObservable() + func retryOnBecomesReachable(valueOnFailure:E, reachabilityService: ReachabilityService) -> Observable{ + return retryOnBecomesReachable(valueOnFailure, reachabilityService: reachabilityService, orExternalTrigger: empty()) + } + + func retryOnBecomesReachable(valueOnFailure:E, reachabilityService:ReachabilityService, orExternalTrigger: Observable) -> Observable{ + return self .catchError { (e) -> Observable in - reachabilityService - .reachabilityChanged + let retryBecauseOfNeworkAvailability = reachabilityService.reachabilityChanged + .flatMap { event -> Observable in + if event == .Reachable { + return just() + } else { + return empty() + } + } + + return sequenceOf(retryBecauseOfNeworkAvailability, orExternalTrigger) + .merge() .flatMap { _ in failWith(e) } .startWith(valueOnFailure) } diff --git a/RxExample/RxExample/Services/UIImage+Extensions.swift b/RxExample/RxExample/Services/UIImage+Extensions.swift new file mode 100644 index 00000000..14635568 --- /dev/null +++ b/RxExample/RxExample/Services/UIImage+Extensions.swift @@ -0,0 +1,23 @@ +// +// UIImage+Extensions.swift +// RxExample +// +// Created by Krunoslav Zaher on 11/1/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if os(iOS) +import UIKit +#endif + +extension Image { + func forceLazyImageDecompression() -> Image { + #if os(iOS) + UIGraphicsBeginImageContext(CGSizeMake(1, 1)) + self.drawAtPoint(CGPointZero) + UIGraphicsEndImageContext() + #endif + return self + } +} \ No newline at end of file diff --git a/RxExample/RxExample/Services/Wireframe.swift b/RxExample/RxExample/Services/Wireframe.swift new file mode 100644 index 00000000..af224854 --- /dev/null +++ b/RxExample/RxExample/Services/Wireframe.swift @@ -0,0 +1,90 @@ +// +// Wireframe.swift +// Example +// +// Created by Krunoslav Zaher on 4/3/15. +// Copyright (c) 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE +import RxSwift +#endif + +#if os(iOS) +import UIKit +#elseif os(OSX) +import Cocoa +#endif + +enum RetryResult { + case Retry + case Cancel +} + +protocol Wireframe { + func openURL(URL: NSURL) + func promptFor(message: String, cancelAction: Action, actions: [Action]) -> Observable +} + + +class DefaultWireframe: Wireframe { + func openURL(URL: NSURL) { + #if os(iOS) + UIApplication.sharedApplication().openURL(URL) + #elseif os(OSX) + NSWorkspace.sharedWorkspace().openURL(URL) + #endif + } + + func promptFor(message: String, cancelAction: Action, actions: [Action]) -> Observable { + #if os(iOS) + return create { observer in + let alertView = UIAlertView( + title: "RxExample", + message: message, + delegate: nil, + cancelButtonTitle: cancelAction.description + ) + + for action in actions { + alertView.addButtonWithTitle(action.description) + } + + alertView.show() + + observer.on(.Next(alertView)) + + return AnonymousDisposable { + alertView.dismissWithClickedButtonIndex(-1, animated: true) + } + }.flatMap { (alertView: UIAlertView) -> Observable in + return alertView.rx_didDismissWithButtonIndex.flatMap { index -> Observable in + if index < 0 { + return empty() + } + + if index == 0 { + return just(cancelAction) + } + + return just(actions[index - 1]) + } + } + #elseif os(OSX) + return failWith(NSError(domain: "Unimplemented", code: -1, userInfo: nil)) + #endif + } +} + + +extension RetryResult : CustomStringConvertible { + var description: String { + switch self { + case .Retry: + return "Retry" + case .Cancel: + return "Cancel" + } + } +} \ No newline at end of file diff --git a/RxExample/RxExample/Wireframe.swift b/RxExample/RxExample/Wireframe.swift deleted file mode 100644 index 61df7afe..00000000 --- a/RxExample/RxExample/Wireframe.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// Wireframe.swift -// Example -// -// Created by Krunoslav Zaher on 4/3/15. -// Copyright (c) 2015 Krunoslav Zaher. All rights reserved. -// - -import Foundation - -#if os(iOS) -import UIKit -#elseif os(OSX) -import Cocoa -#endif - -protocol Wireframe { - func openURL(URL: NSURL) -} - - -class DefaultWireframe: Wireframe { - func openURL(URL: NSURL) { - #if os(iOS) - UIApplication.sharedApplication().openURL(URL) - #elseif os(OSX) - NSWorkspace.sharedWorkspace().openURL(URL) - #endif - } -} \ No newline at end of file diff --git a/RxExample/RxExample/iOS/Main.storyboard b/RxExample/RxExample/iOS/Main.storyboard index 325d7582..900c30d0 100644 --- a/RxExample/RxExample/iOS/Main.storyboard +++ b/RxExample/RxExample/iOS/Main.storyboard @@ -1,7 +1,7 @@ - + - + @@ -24,7 +24,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -70,7 +70,7 @@ - + From b12e97abda5a4f60cb3ca43c2eec6dbab8843dc8 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 1 Nov 2015 20:17:48 +0100 Subject: [PATCH 130/210] Adds `Driver` to `RxExample` app. --- RxExample/RxExample.xcodeproj/project.pbxproj | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index a9eff03c..580da957 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -39,6 +39,16 @@ C809E97E1BE697100058D948 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C809E97C1BE697100058D948 /* UIImage+Extensions.swift */; }; C809E97F1BE69B660058D948 /* Wireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = C809E9791BE6841C0058D948 /* Wireframe.swift */; }; C809E9801BE69BA30058D948 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C809E97C1BE697100058D948 /* UIImage+Extensions.swift */; }; + C809E9811BE69C310058D948 /* ControlEvent+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC21BCE9041006A1832 /* ControlEvent+Driver.swift */; }; + C809E9821BE69C310058D948 /* ControlProperty+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC31BCE9041006A1832 /* ControlProperty+Driver.swift */; }; + C809E9831BE69C310058D948 /* Driver+Operators+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC41BCE9041006A1832 /* Driver+Operators+arity.swift */; }; + C809E9841BE69C350058D948 /* Driver+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC61BCE9041006A1832 /* Driver+Operators.swift */; }; + C809E9851BE69C350058D948 /* Driver+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC71BCE9041006A1832 /* Driver+Subscription.swift */; }; + C809E9861BE69C350058D948 /* Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC81BCE9041006A1832 /* Driver.swift */; }; + C809E9871BE69C350058D948 /* ObservableConvertibleType+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC91BCE9041006A1832 /* ObservableConvertibleType+Driver.swift */; }; + C809E9881BE69C3F0058D948 /* ControlEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465191BC6C2BC0055219D /* ControlEvent.swift */; }; + C809E9891BE69C3F0058D948 /* ControlProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894651B1BC6C2BC0055219D /* ControlProperty.swift */; }; + C809E98A1BE69C530058D948 /* RxCocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465311BC6C2BC0055219D /* RxCocoa.swift */; }; C80DDE881BCDAA0F006A1832 /* SkipWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDE7A1BCDA952006A1832 /* SkipWhile.swift */; }; C80DDED21BCE9046006A1832 /* ControlEvent+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC21BCE9041006A1832 /* ControlEvent+Driver.swift */; }; C80DDED31BCE9046006A1832 /* ControlProperty+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEC31BCE9041006A1832 /* ControlProperty+Driver.swift */; }; @@ -1928,22 +1938,32 @@ buildActionMask = 2147483647; files = ( C809E97F1BE69B660058D948 /* Wireframe.swift in Sources */, + C809E9841BE69C350058D948 /* Driver+Operators.swift in Sources */, C8DF92DF1B0B328B009BCF9A /* AppDelegate.swift in Sources */, + C809E9861BE69C350058D948 /* Driver.swift in Sources */, C88BB8BB1B07E6C90064D411 /* SearchResultViewModel.swift in Sources */, C88BB8BC1B07E6C90064D411 /* HtmlParsing.swift in Sources */, C88BB8BD1B07E6C90064D411 /* SearchViewModel.swift in Sources */, + C809E98A1BE69C530058D948 /* RxCocoa.swift in Sources */, C8E9D2AF1BD3FD960079D0DB /* ActivityIndicator.swift in Sources */, + C809E9881BE69C3F0058D948 /* ControlEvent.swift in Sources */, C88BB8BE1B07E6C90064D411 /* ImageService.swift in Sources */, B1604CC31BE5B8BD002E1279 /* ReachabilityService.swift in Sources */, + C809E9821BE69C310058D948 /* ControlProperty+Driver.swift in Sources */, + C809E9811BE69C310058D948 /* ControlEvent+Driver.swift in Sources */, + C809E9831BE69C310058D948 /* Driver+Operators+arity.swift in Sources */, C88BB8BF1B07E6C90064D411 /* WikipediaSearchResult.swift in Sources */, C8DF92F61B0B43A4009BCF9A /* IntroductionExampleViewController.swift in Sources */, B18F3BBF1BD93DFF000AAC79 /* Reachability.swift in Sources */, C88BB8C31B07E6C90064D411 /* Example.swift in Sources */, C88BB8C41B07E6C90064D411 /* ViewController.swift in Sources */, C88BB8C71B07E6C90064D411 /* Dependencies.swift in Sources */, + C809E9871BE69C350058D948 /* ObservableConvertibleType+Driver.swift in Sources */, C88BB8CA1B07E6C90064D411 /* WikipediaAPI.swift in Sources */, C809E9801BE69BA30058D948 /* UIImage+Extensions.swift in Sources */, + C809E9891BE69C3F0058D948 /* ControlProperty.swift in Sources */, B1604CCA1BE5BC18002E1279 /* DownloadableImage.swift in Sources */, + C809E9851BE69C350058D948 /* Driver+Subscription.swift in Sources */, C88BB8CC1B07E6C90064D411 /* WikipediaPage.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; From 0f50808bc63da8ca7e41d731a4ac5e85c40d870f Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 1 Nov 2015 20:35:38 +0100 Subject: [PATCH 131/210] Adds `withLatestFrom` to `Driver`. --- .../CocoaUnits/Driver/Driver+Operators.swift | 30 ++++++++++ RxSwift/Observables/Observable+Multiple.swift | 12 +++- RxTests/RxSwiftTests/Tests/Driver+Test.swift | 56 +++++++++++++++++++ .../Tests/Observable+MultipleTest.swift | 48 ++++++++++++++++ 4 files changed, 145 insertions(+), 1 deletion(-) diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift index 1d1a629d..12b2bf14 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift @@ -327,4 +327,34 @@ extension CollectionType where Generator.Element : DriverConvertibleType { let source : Observable = self.map { $0.asDriver() }.combineLatest(resultSelector) return Driver(source) } +} + +extension DriverConvertibleType { + + /** + Merges two observable sequences into one observable sequence by combining each element from self with the latest element from the second source, if any. + + - parameter second: Second observable source. + - parameter resultSelector: Function to invoke for each element from the self combined with the latest element from the second source, if any. + - returns: An observable sequence containing the result of combining each element of the self with the latest element from the second source, if any, using the specified result selector function. + */ + public func withLatestFrom(second: SecondO, resultSelector: (E, SecondO.E) -> ResultType) -> Driver { + let source = self.asObservable() + .withLatestFrom(second.asDriver(), resultSelector: resultSelector) + + return Driver(source) + } + + /** + Merges two observable sequences into one observable sequence by using latest element from the second sequence every time when `self` emitts an element. + + - parameter second: Second observable source. + - returns: An observable sequence containing the result of combining each element of the self with the latest element from the second source, if any, using the specified result selector function. + */ + public func withLatestFrom(second: SecondO) -> Driver { + let source = self.asObservable() + .withLatestFrom(second.asDriver()) + + return Driver(source) + } } \ No newline at end of file diff --git a/RxSwift/Observables/Observable+Multiple.swift b/RxSwift/Observables/Observable+Multiple.swift index 97efbf72..bba42c2a 100644 --- a/RxSwift/Observables/Observable+Multiple.swift +++ b/RxSwift/Observables/Observable+Multiple.swift @@ -251,7 +251,17 @@ extension ObservableConvertibleType { - parameter resultSelector: Function to invoke for each element from the self combined with the latest element from the second source, if any. - returns: An observable sequence containing the result of combining each element of the self with the latest element from the second source, if any, using the specified result selector function. */ - public func withLatestFrom(second: SecondO, resultSelector: (E, SecondO.E) throws -> ResultType) -> Observable { + public func withLatestFrom(second: SecondO, resultSelector: (E, SecondO.E) throws -> ResultType) -> Observable { return WithLatestFrom(first: self.asObservable(), second: second.asObservable(), resultSelector: resultSelector) } + + /** + Merges two observable sequences into one observable sequence by using latest element from the second sequence every time when `self` emitts an element. + + - parameter second: Second observable source. + - returns: An observable sequence containing the result of combining each element of the self with the latest element from the second source, if any, using the specified result selector function. + */ + public func withLatestFrom(second: SecondO) -> Observable { + return WithLatestFrom(first: self.asObservable(), second: second.asObservable(), resultSelector: { $1 }) + } } diff --git a/RxTests/RxSwiftTests/Tests/Driver+Test.swift b/RxTests/RxSwiftTests/Tests/Driver+Test.swift index afeac06a..045a2314 100644 --- a/RxTests/RxSwiftTests/Tests/Driver+Test.swift +++ b/RxTests/RxSwiftTests/Tests/Driver+Test.swift @@ -606,4 +606,60 @@ extension DriverTest { XCTAssertEqual(results, [5, 7, -3]) } +} + +// withLatestFrom +extension DriverTest { + func testAsDriver_withLatestFrom() { + let hotObservable1 = BackgroundThreadPrimitiveHotObservable() + let hotObservable2 = BackgroundThreadPrimitiveHotObservable() + + let driver = hotObservable1.asDriver(onErrorJustReturn: -1).withLatestFrom(hotObservable2.asDriver(onErrorJustReturn: -2)) { f, s in "\(f)\(s)" } + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(driver) { + XCTAssertTrue(hotObservable1.subscriptions == [SubscribedToHotObservable]) + XCTAssertTrue(hotObservable2.subscriptions == [SubscribedToHotObservable]) + + hotObservable1.on(.Next(1)) + hotObservable2.on(.Next(4)) + + hotObservable1.on(.Next(2)) + hotObservable2.on(.Next(5)) + + hotObservable1.on(.Error(testError)) + hotObservable2.on(.Error(testError)) + + XCTAssertTrue(hotObservable1.subscriptions == [UnsunscribedFromHotObservable]) + XCTAssertTrue(hotObservable2.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, ["24", "-15"]) + } + + func testAsDriver_withLatestFromDefaultOverload() { + let hotObservable1 = BackgroundThreadPrimitiveHotObservable() + let hotObservable2 = BackgroundThreadPrimitiveHotObservable() + + let driver = hotObservable1.asDriver(onErrorJustReturn: -1).withLatestFrom(hotObservable2.asDriver(onErrorJustReturn: -2)) + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(driver) { + XCTAssertTrue(hotObservable1.subscriptions == [SubscribedToHotObservable]) + XCTAssertTrue(hotObservable2.subscriptions == [SubscribedToHotObservable]) + + hotObservable1.on(.Next(1)) + hotObservable2.on(.Next(4)) + + hotObservable1.on(.Next(2)) + hotObservable2.on(.Next(5)) + + hotObservable1.on(.Error(testError)) + hotObservable2.on(.Error(testError)) + + XCTAssertTrue(hotObservable1.subscriptions == [UnsunscribedFromHotObservable]) + XCTAssertTrue(hotObservable2.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [4, 5]) + + } } \ No newline at end of file diff --git a/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift b/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift index 341ebf7f..80f38fdb 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift @@ -4217,4 +4217,52 @@ extension ObservableMultipleTest { Subscription(200, 310) ]) } + + func testWithLatestFrom_MakeSureDefaultOverloadTakesSecondSequenceValues() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(90, 1), + next(180, 2), + next(250, 3), + next(260, 4), + next(310, 5), + next(340, 6), + next(410, 7), + next(420, 8), + next(470, 9), + next(550, 10), + completed(590) + ]) + + let ys = scheduler.createHotObservable([ + next(255, "bar"), + next(330, "foo"), + next(350, "qux"), + completed(400) + ]) + + let res = scheduler.start { + xs.withLatestFrom(ys) + } + + XCTAssertEqual(res.messages, [ + next(260, "bar"), + next(310, "bar"), + next(340, "foo"), + next(410, "qux"), + next(420, "qux"), + next(470, "qux"), + next(550, "qux"), + completed(590) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 590) + ]) + + XCTAssertEqual(ys.subscriptions, [ + Subscription(200, 400) + ]) + } } \ No newline at end of file From bd3c5a77b93a4ab763764d210c74f1deb197d2ae Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 25 Oct 2015 10:46:41 +0100 Subject: [PATCH 132/210] Removes hidden lamba allocations from disposables. --- RxSwift/Concurrency/Lock.swift | 8 +++ RxSwift/DataStructures/Bag.swift | 48 +++++++++++--- RxSwift/Disposables/CompositeDisposable.swift | 61 +++++++++--------- RxSwift/Disposables/DisposeBag.swift | 47 +++++++------- RxSwift/Disposables/SerialDisposable.swift | 23 +++---- .../SingleAssignmentDisposable.swift | 63 +++++++++---------- .../Observables/Implementations/Sink.swift | 40 ++++++------ 7 files changed, 163 insertions(+), 127 deletions(-) diff --git a/RxSwift/Concurrency/Lock.swift b/RxSwift/Concurrency/Lock.swift index 4fdfe3d8..5ec4a06d 100644 --- a/RxSwift/Concurrency/Lock.swift +++ b/RxSwift/Concurrency/Lock.swift @@ -17,6 +17,14 @@ struct SpinLock { init() { } + + mutating func lock() { + OSSpinLockLock(&_lock) + } + + mutating func unlock() { + OSSpinLockUnlock(&_lock) + } mutating func performLocked(@noescape action: () -> Void) { OSSpinLockLock(&_lock) diff --git a/RxSwift/DataStructures/Bag.swift b/RxSwift/DataStructures/Bag.swift index 0386e2f8..e9023b69 100644 --- a/RxSwift/DataStructures/Bag.swift +++ b/RxSwift/DataStructures/Bag.swift @@ -53,7 +53,7 @@ public struct Bag : CustomStringConvertible { private var _uniqueIdentity: Identity? private var _nextKey: ScopeUniqueTokenType = 0 - var pairs = [Entry]() + private var _pairs = [Entry]() /** Creates new empty `Bag`. @@ -89,7 +89,7 @@ public struct Bag : CustomStringConvertible { let key = BagKey(uniqueIdentity: _uniqueIdentity, key: _nextKey) - pairs.append(key: key, value: element) + _pairs.append(key: key, value: element) return key } @@ -98,14 +98,14 @@ public struct Bag : CustomStringConvertible { - returns: Number of elements in bag. */ public var count: Int { - return pairs.count + return _pairs.count } /** Removes all elements from bag and clears capacity. */ public mutating func removeAll() { - pairs.removeAll(keepCapacity: false) + _pairs.removeAll(keepCapacity: false) } /** @@ -115,10 +115,10 @@ public struct Bag : CustomStringConvertible { - returns: Element that bag contained, or nil in case element was already removed. */ public mutating func removeKey(key: BagKey) -> T? { - for i in 0 ..< pairs.count { - if pairs[i].key == key { - let value = pairs[i].value - pairs.removeAtIndex(i) + for i in 0 ..< _pairs.count { + if _pairs[i].key == key { + let value = _pairs[i].value + _pairs.removeAtIndex(i) return value } } @@ -127,6 +127,8 @@ public struct Bag : CustomStringConvertible { } } +// MARK: forEach + extension Bag { /** Enumerates elements inside the bag. @@ -134,10 +136,36 @@ extension Bag { - parameter action: Enumeration closure. */ public func forEach(@noescape action: (T) -> Void) { - let pairs = self.pairs + let pairs = self._pairs for i in 0 ..< pairs.count { action(pairs[i].value) } } -} \ No newline at end of file +} + +extension Bag where T: ObserverType { + /** + Dispatches `event` to app observers contained inside bag. + + - parameter action: Enumeration closure. + */ + public func on(event: Event) { + let pairs = self._pairs + + for i in 0 ..< pairs.count { + pairs[i].value.on(event) + } + } +} + +/** +Dispatches `dispose` to all disposables contained inside bag. +*/ +func disposeFromBag(bag: Bag) { + let pairs = bag._pairs + + for i in 0 ..< pairs.count { + pairs[i].value.dispose() + } +} diff --git a/RxSwift/Disposables/CompositeDisposable.swift b/RxSwift/Disposables/CompositeDisposable.swift index c49bad3c..b832f4c2 100644 --- a/RxSwift/Disposables/CompositeDisposable.swift +++ b/RxSwift/Disposables/CompositeDisposable.swift @@ -21,9 +21,8 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable { public var disposed: Bool { get { - return _lock.calculateLocked { - return _disposables == nil - } + _lock.lock(); defer { _lock.unlock() } + return _disposables == nil } } @@ -55,7 +54,7 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable { _disposables!.insert(disposable) } } - + /** Adds a disposable to the CompositeDisposable or disposes the disposable if the CompositeDisposable is disposed. @@ -64,27 +63,28 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable { disposed `nil` will be returned. */ public func addDisposable(disposable: Disposable) -> DisposeKey? { - // this should be let - // bucause of compiler bug it's var - let key = _lock.calculateLocked { () -> DisposeKey? in - return _disposables?.insert(disposable) - } - + let key = _addDisposable(disposable) + if key == nil { disposable.dispose() } return key } + + private func _addDisposable(disposable: Disposable) -> DisposeKey? { + _lock.lock(); defer { _lock.unlock() } + + return _disposables?.insert(disposable) + } /** - returns: Gets the number of disposables contained in the `CompositeDisposable`. */ public var count: Int { get { - return _lock.calculateLocked { - return _disposables?.count ?? 0 - } + _lock.lock(); defer { _lock.unlock() } + return _disposables?.count ?? 0 } } @@ -94,30 +94,29 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable { - parameter disposeKey: Key used to identify disposable to be removed. */ public func removeDisposable(disposeKey: DisposeKey) { - let disposable = _lock.calculateLocked { () -> Disposable? in - return _disposables?.removeKey(disposeKey) - } - - if let disposable = disposable { - disposable.dispose() - } + _removeDisposable(disposeKey)?.dispose() + } + + private func _removeDisposable(disposeKey: DisposeKey) -> Disposable? { + _lock.lock(); defer { _lock.unlock() } + return _disposables?.removeKey(disposeKey) } /** Disposes all disposables in the group and removes them from the group. */ public func dispose() { - let oldDisposables = _lock.calculateLocked { () -> Bag? in - let disposeBag = _disposables - _disposables = nil - - return disposeBag - } - - if let oldDisposables = oldDisposables { - oldDisposables.forEach { d in - d.dispose() - } + if let disposables = _dispose() { + disposeFromBag(disposables) } } + + private func _dispose() -> Bag? { + _lock.lock(); defer { _lock.unlock() } + + let disposeBag = _disposables + _disposables = nil + + return disposeBag + } } \ No newline at end of file diff --git a/RxSwift/Disposables/DisposeBag.swift b/RxSwift/Disposables/DisposeBag.swift index 28f6c09e..2c24a60e 100644 --- a/RxSwift/Disposables/DisposeBag.swift +++ b/RxSwift/Disposables/DisposeBag.swift @@ -52,38 +52,41 @@ public class DisposeBag: DisposeBase { - parameter disposable: Disposable to add. */ public func addDisposable(disposable: Disposable) { - let dispose = _lock.calculateLocked { () -> Bool in - if _disposed { - return true - } - - _disposables.append(disposable) - - return false - } - - if dispose { - disposable.dispose() + _addDisposable(disposable)?.dispose() + } + + private func _addDisposable(disposable: Disposable) -> Disposable? { + _lock.lock(); defer { _lock.unlock() } + if _disposed { + return disposable } + + _disposables.append(disposable) + + return nil } /** This is internal on purpose, take a look at `CompositeDisposable` instead. */ - func dispose() { - let oldDisposables = _lock.calculateLocked { () -> [Disposable] in - let disposables = _disposables - - _disposables.removeAll(keepCapacity: false) - _disposed = true - - return disposables - } - + private func dispose() { + let oldDisposables = _dispose() + for disposable in oldDisposables { disposable.dispose() } } + + private func _dispose() -> [Disposable] { + _lock.lock(); defer { _lock.unlock() } + + let disposables = _disposables + + _disposables.removeAll(keepCapacity: false) + _disposed = true + + return disposables + } deinit { dispose() diff --git a/RxSwift/Disposables/SerialDisposable.swift b/RxSwift/Disposables/SerialDisposable.swift index 571000cf..0f30324e 100644 --- a/RxSwift/Disposables/SerialDisposable.swift +++ b/RxSwift/Disposables/SerialDisposable.swift @@ -69,18 +69,19 @@ public class SerialDisposable : DisposeBase, Cancelable { Disposes the underlying disposable as well as all future replacements. */ public func dispose() { - let disposable: Disposable? = _lock.calculateLocked { - if _disposed { - return nil - } - else { - _disposed = true - return _current - } + _dispose()?.dispose() + } + + private func _dispose() -> Disposable? { + _lock.lock(); defer { _lock.unlock() } + if _disposed { + return nil } - - if let disposable = disposable { - disposable.dispose() + else { + _disposed = true + let current = _current + _current = nil + return current } } } \ No newline at end of file diff --git a/RxSwift/Disposables/SingleAssignmentDisposable.swift b/RxSwift/Disposables/SingleAssignmentDisposable.swift index bf7e3f21..8b2858bd 100644 --- a/RxSwift/Disposables/SingleAssignmentDisposable.swift +++ b/RxSwift/Disposables/SingleAssignmentDisposable.swift @@ -26,9 +26,7 @@ public class SingleAssignmentDisposable : DisposeBase, Disposable, Cancelable { */ public var disposed: Bool { get { - return _lock.calculateLocked { - return _disposed - } + return _disposed } } @@ -46,47 +44,44 @@ public class SingleAssignmentDisposable : DisposeBase, Disposable, Cancelable { */ public var disposable: Disposable { get { - return _lock.calculateLocked { - return _disposable ?? NopDisposable.instance - } + _lock.lock(); defer { _lock.unlock() } + return _disposable ?? NopDisposable.instance } set { - let disposable: Disposable? = _lock.calculateLocked { - if _disposableSet { - rxFatalError("oldState.disposable != nil") - } - - _disposableSet = true - - if _disposed { - return newValue - } - - _disposable = newValue - - return nil - } - - if let disposable = disposable { - disposable.dispose() - } + _setDisposable(newValue)?.dispose() } } + private func _setDisposable(newValue: Disposable) -> Disposable? { + if _disposableSet { + rxFatalError("oldState.disposable != nil") + } + + _disposableSet = true + + if _disposed { + return newValue + } + + _disposable = newValue + + return nil + } + /** Disposes the underlying disposable. */ public func dispose() { - let disposable: Disposable? = _lock.calculateLocked { - _disposed = true - let dispose = _disposable - _disposable = nil + _dispose()?.dispose() + } - return dispose - } + private func _dispose() -> Disposable? { + _lock.lock(); defer { _lock.unlock() } - if let disposable = disposable { - disposable.dispose() - } + _disposed = true + let disposable = _disposable + _disposable = nil + + return disposable } } diff --git a/RxSwift/Observables/Implementations/Sink.swift b/RxSwift/Observables/Implementations/Sink.swift index c7b16261..7aeaee53 100644 --- a/RxSwift/Observables/Implementations/Sink.swift +++ b/RxSwift/Observables/Implementations/Sink.swift @@ -18,13 +18,15 @@ class Sink : Disposable { var observer: O? { get { - return _lock.calculateLocked { _observer } + _lock.lock(); defer { _lock.unlock() } + return _observer } } var cancel: Disposable { get { - return _lock.calculateLocked { _cancel } + _lock.lock(); defer { _lock.unlock() } + return _cancel } } @@ -35,25 +37,25 @@ class Sink : Disposable { _observer = observer _cancel = cancel } - - func dispose() { - let cancel: Disposable? = _lock.calculateLocked { - if _disposed { - return nil - } - - let cancel = _cancel - - _disposed = true - _observer = nil - _cancel = NopDisposable.instance - - return cancel + + func _disposeInternal() -> Disposable? { + _lock.lock(); defer { _lock.unlock() } + + if _disposed { + return nil } - if let cancel = cancel { - cancel.dispose() - } + let cancel = _cancel + + _disposed = true + _observer = nil + _cancel = NopDisposable.instance + + return cancel + } + + func dispose() { + _disposeInternal()?.dispose() } deinit { From 26a34c4ac3d7647af4475590fd97ebfacc54dcd2 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 25 Oct 2015 11:43:21 +0100 Subject: [PATCH 133/210] Makes `Producer` internal. --- .../Observables/Implementations/KVOObservable.swift | 9 ++++++--- RxCocoa/Common/Observables/NSObject+Rx.swift | 2 +- RxSwift/Observables/Implementations/Producer.swift | 12 +++--------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/RxCocoa/Common/Observables/Implementations/KVOObservable.swift b/RxCocoa/Common/Observables/Implementations/KVOObservable.swift index 06577002..c5a0bac2 100644 --- a/RxCocoa/Common/Observables/Implementations/KVOObservable.swift +++ b/RxCocoa/Common/Observables/Implementations/KVOObservable.swift @@ -11,8 +11,11 @@ import Foundation import RxSwift #endif -class KVOObservable : _Producer - , KVOObservableProtocol { +class KVOObservable + : ObservableType + , KVOObservableProtocol { + typealias E = Element? + unowned var target: AnyObject var strongTarget: AnyObject? @@ -30,7 +33,7 @@ class KVOObservable : _Producer } } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + func subscribe(observer: O) -> Disposable { let observer = KVOObserver(parent: self) { (value) in if value as? NSNull != nil { observer.on(.Next(nil)) diff --git a/RxCocoa/Common/Observables/NSObject+Rx.swift b/RxCocoa/Common/Observables/NSObject+Rx.swift index d61992c6..4f3acd33 100644 --- a/RxCocoa/Common/Observables/NSObject+Rx.swift +++ b/RxCocoa/Common/Observables/NSObject+Rx.swift @@ -59,7 +59,7 @@ extension NSObject { - returns: Observable sequence of objects on `keyPath`. */ public func rx_observe(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { - return KVOObservable(object: self, keyPath: keyPath, options: options, retainTarget: retainSelf) + return KVOObservable(object: self, keyPath: keyPath, options: options, retainTarget: retainSelf).asObservable() } } diff --git a/RxSwift/Observables/Implementations/Producer.swift b/RxSwift/Observables/Implementations/Producer.swift index c1d32a08..63c38c76 100644 --- a/RxSwift/Observables/Implementations/Producer.swift +++ b/RxSwift/Observables/Implementations/Producer.swift @@ -8,18 +8,12 @@ import Foundation -public class _Producer : Producer { - public override init() { - super.init() - } -} - -public class Producer : Observable { +class Producer : Observable { override init() { super.init() } - public override func subscribe(observer: O) -> Disposable { + override func subscribe(observer: O) -> Disposable { let sink = SingleAssignmentDisposable() let subscription = SingleAssignmentDisposable() @@ -43,7 +37,7 @@ public class Producer : Observable { return d } - public func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { abstractMethod() } } \ No newline at end of file From 2e8dbb7af9417e45165b377fb16a79097160b7f7 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 25 Oct 2015 11:43:38 +0100 Subject: [PATCH 134/210] Turns on `-OWhole-module-optimization` for `Release` configuration. --- Rx.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index f23e198c..2d4a3dea 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -3051,6 +3051,7 @@ MTL_ENABLE_DEBUG_INFO = NO; OTHER_SWIFT_FLAGS = "-D TRACE_RESOURCES"; SDKROOT = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -3225,6 +3226,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; From dcc65e8fe000a12eb1337e22a5b10c7b67e5dd23 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 25 Oct 2015 21:16:36 +0100 Subject: [PATCH 135/210] Adds synchronization protocols to reduce allocations. --- Rx.xcodeproj/project.pbxproj | 60 +++++++++ RxExample/RxExample.xcodeproj/project.pbxproj | 28 +++- RxSwift/Concurrency/Lock.swift | 7 +- RxSwift/Concurrency/LockOwnerType.swift | 23 ++++ .../Concurrency/SynchronizedDisposeType.swift | 20 +++ RxSwift/Concurrency/SynchronizedOnType.swift | 20 +++ .../SynchronizedSubscribeType.swift | 20 +++ .../SynchronizedUnsubscribeType.swift | 22 ++++ RxSwift/DataStructures/Bag.swift | 2 +- .../Disposables/SubscriptionDisposable.swift | 23 ++++ .../Implementations/CombineLatest.swift | 33 +++-- .../Implementations/ShareReplay1.swift | 93 +++++++------ .../Schedulers/CurrentThreadScheduler.swift | 94 +++++++++---- RxSwift/Subjects/BehaviorSubject.swift | 2 +- RxSwift/Subjects/PublishSubject.swift | 123 ++++++++---------- RxSwift/Subjects/ReplaySubject.swift | 4 +- RxSwift/Subjects/Variable.swift | 12 +- RxTests/PerformanceTests/main.swift | 77 +---------- .../Mocks/HotObservable.swift | 2 +- .../Mocks/PrimitiveHotObservable.swift | 2 +- RxTests/RxTests.xcodeproj/project.pbxproj | 3 + RxTests/Tests/PerformanceTools.swift | 114 ++++++++++++++++ 22 files changed, 550 insertions(+), 234 deletions(-) create mode 100644 RxSwift/Concurrency/LockOwnerType.swift create mode 100644 RxSwift/Concurrency/SynchronizedDisposeType.swift create mode 100644 RxSwift/Concurrency/SynchronizedOnType.swift create mode 100644 RxSwift/Concurrency/SynchronizedSubscribeType.swift create mode 100644 RxSwift/Concurrency/SynchronizedUnsubscribeType.swift create mode 100644 RxSwift/Disposables/SubscriptionDisposable.swift diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index 2d4a3dea..e7949ad6 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -298,6 +298,30 @@ C84CC5411BDC3B3E00E06A64 /* ElementAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC53F1BDC3B3700E06A64 /* ElementAt.swift */; }; C84CC5421BDC3B3E00E06A64 /* ElementAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC53F1BDC3B3700E06A64 /* ElementAt.swift */; }; C84CC5431BDC3B3E00E06A64 /* ElementAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC53F1BDC3B3700E06A64 /* ElementAt.swift */; }; + C84CC54E1BDCF48200E06A64 /* LockOwnerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC54D1BDCF48200E06A64 /* LockOwnerType.swift */; }; + C84CC54F1BDCF48200E06A64 /* LockOwnerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC54D1BDCF48200E06A64 /* LockOwnerType.swift */; }; + C84CC5501BDCF48200E06A64 /* LockOwnerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC54D1BDCF48200E06A64 /* LockOwnerType.swift */; }; + C84CC5511BDCF48200E06A64 /* LockOwnerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC54D1BDCF48200E06A64 /* LockOwnerType.swift */; }; + C84CC5531BDCF49300E06A64 /* SynchronizedOnType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC5521BDCF49300E06A64 /* SynchronizedOnType.swift */; }; + C84CC5541BDCF49300E06A64 /* SynchronizedOnType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC5521BDCF49300E06A64 /* SynchronizedOnType.swift */; }; + C84CC5551BDCF49300E06A64 /* SynchronizedOnType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC5521BDCF49300E06A64 /* SynchronizedOnType.swift */; }; + C84CC5561BDCF49300E06A64 /* SynchronizedOnType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC5521BDCF49300E06A64 /* SynchronizedOnType.swift */; }; + C84CC5581BDCF51200E06A64 /* SynchronizedSubscribeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC5571BDCF51200E06A64 /* SynchronizedSubscribeType.swift */; }; + C84CC5591BDCF51200E06A64 /* SynchronizedSubscribeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC5571BDCF51200E06A64 /* SynchronizedSubscribeType.swift */; }; + C84CC55A1BDCF51200E06A64 /* SynchronizedSubscribeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC5571BDCF51200E06A64 /* SynchronizedSubscribeType.swift */; }; + C84CC55B1BDCF51200E06A64 /* SynchronizedSubscribeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC5571BDCF51200E06A64 /* SynchronizedSubscribeType.swift */; }; + C84CC55D1BDD010800E06A64 /* SynchronizedUnsubscribeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC55C1BDD010800E06A64 /* SynchronizedUnsubscribeType.swift */; }; + C84CC55E1BDD010800E06A64 /* SynchronizedUnsubscribeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC55C1BDD010800E06A64 /* SynchronizedUnsubscribeType.swift */; }; + C84CC55F1BDD010800E06A64 /* SynchronizedUnsubscribeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC55C1BDD010800E06A64 /* SynchronizedUnsubscribeType.swift */; }; + C84CC5601BDD010800E06A64 /* SynchronizedUnsubscribeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC55C1BDD010800E06A64 /* SynchronizedUnsubscribeType.swift */; }; + C84CC5621BDD037900E06A64 /* SynchronizedDisposeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC5611BDD037900E06A64 /* SynchronizedDisposeType.swift */; }; + C84CC5631BDD037900E06A64 /* SynchronizedDisposeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC5611BDD037900E06A64 /* SynchronizedDisposeType.swift */; }; + C84CC5641BDD037900E06A64 /* SynchronizedDisposeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC5611BDD037900E06A64 /* SynchronizedDisposeType.swift */; }; + C84CC5651BDD037900E06A64 /* SynchronizedDisposeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC5611BDD037900E06A64 /* SynchronizedDisposeType.swift */; }; + C84CC5671BDD08A500E06A64 /* SubscriptionDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC5661BDD08A500E06A64 /* SubscriptionDisposable.swift */; }; + C84CC5681BDD08A500E06A64 /* SubscriptionDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC5661BDD08A500E06A64 /* SubscriptionDisposable.swift */; }; + C84CC5691BDD08A500E06A64 /* SubscriptionDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC5661BDD08A500E06A64 /* SubscriptionDisposable.swift */; }; + C84CC56A1BDD08A500E06A64 /* SubscriptionDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC5661BDD08A500E06A64 /* SubscriptionDisposable.swift */; }; C86409FC1BA593F500D3C4E8 /* Range.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409FB1BA593F500D3C4E8 /* Range.swift */; }; C86409FD1BA593F500D3C4E8 /* Range.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409FB1BA593F500D3C4E8 /* Range.swift */; }; C8640A031BA5B12A00D3C4E8 /* Repeat.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8640A021BA5B12A00D3C4E8 /* Repeat.swift */; }; @@ -936,6 +960,12 @@ C84B38E71BA43380001B7D88 /* ScheduledItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduledItem.swift; sourceTree = ""; }; C84B38ED1BA433CD001B7D88 /* Generate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Generate.swift; sourceTree = ""; }; C84CC53F1BDC3B3700E06A64 /* ElementAt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElementAt.swift; sourceTree = ""; }; + C84CC54D1BDCF48200E06A64 /* LockOwnerType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockOwnerType.swift; sourceTree = ""; }; + C84CC5521BDCF49300E06A64 /* SynchronizedOnType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizedOnType.swift; sourceTree = ""; }; + C84CC5571BDCF51200E06A64 /* SynchronizedSubscribeType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizedSubscribeType.swift; sourceTree = ""; }; + C84CC55C1BDD010800E06A64 /* SynchronizedUnsubscribeType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizedUnsubscribeType.swift; sourceTree = ""; }; + C84CC5611BDD037900E06A64 /* SynchronizedDisposeType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizedDisposeType.swift; sourceTree = ""; }; + C84CC5661BDD08A500E06A64 /* SubscriptionDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionDisposable.swift; sourceTree = ""; }; C86409FB1BA593F500D3C4E8 /* Range.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Range.swift; sourceTree = ""; }; C8640A021BA5B12A00D3C4E8 /* Repeat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Repeat.swift; sourceTree = ""; }; C88253F11B8A752B00B02D69 /* RxCollectionViewReactiveArrayDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewReactiveArrayDataSource.swift; sourceTree = ""; }; @@ -1122,6 +1152,11 @@ children = ( C8093C4B1B8A72BE0088E94D /* AsyncLock.swift */, C8093C4C1B8A72BE0088E94D /* Lock.swift */, + C84CC54D1BDCF48200E06A64 /* LockOwnerType.swift */, + C84CC5521BDCF49300E06A64 /* SynchronizedOnType.swift */, + C84CC5571BDCF51200E06A64 /* SynchronizedSubscribeType.swift */, + C84CC55C1BDD010800E06A64 /* SynchronizedUnsubscribeType.swift */, + C84CC5611BDD037900E06A64 /* SynchronizedDisposeType.swift */, ); path = Concurrency; sourceTree = ""; @@ -1144,6 +1179,7 @@ C8093C571B8A72BE0088E94D /* CompositeDisposable.swift */, C8093C581B8A72BE0088E94D /* DisposeBag.swift */, C8093C591B8A72BE0088E94D /* DisposeBase.swift */, + C84CC5661BDD08A500E06A64 /* SubscriptionDisposable.swift */, C8093C5A1B8A72BE0088E94D /* NAryDisposable.swift */, C8093C5B1B8A72BE0088E94D /* NAryDisposable.tt */, C8093C5C1B8A72BE0088E94D /* NopDisposable.swift */, @@ -2150,7 +2186,9 @@ C89CDB371BCB0DD7002063D9 /* ShareReplay1.swift in Sources */, C8093D2A1B8A72BE0088E94D /* ObserveOn.swift in Sources */, C8093D361B8A72BE0088E94D /* Sample.swift in Sources */, + C84CC54F1BDCF48200E06A64 /* LockOwnerType.swift in Sources */, D2752D621BC5551A0070C418 /* SkipUntil.swift in Sources */, + C84CC5541BDCF49300E06A64 /* SynchronizedOnType.swift in Sources */, C8093CEA1B8A72BE0088E94D /* ScopedDisposable.swift in Sources */, C8093D261B8A72BE0088E94D /* Multicast.swift in Sources */, C8C3DA101B939767004D233E /* CurrentThreadScheduler.swift in Sources */, @@ -2165,6 +2203,7 @@ C8093D241B8A72BE0088E94D /* Merge.swift in Sources */, C8093D8E1B8A72BE0088E94D /* SchedulerType.swift in Sources */, C8093DA81B8A72BE0088E94D /* Variable.swift in Sources */, + C84CC5631BDD037900E06A64 /* SynchronizedDisposeType.swift in Sources */, C8093D961B8A72BE0088E94D /* OperationQueueScheduler.swift in Sources */, C8093D921B8A72BE0088E94D /* DispatchQueueSchedulerPriority.swift in Sources */, C8093D081B8A72BE0088E94D /* CombineLatest+arity.swift in Sources */, @@ -2197,6 +2236,7 @@ C8093D0E1B8A72BE0088E94D /* Concat.swift in Sources */, C8093CCA1B8A72BE0088E94D /* Lock.swift in Sources */, C8093D441B8A72BE0088E94D /* Take.swift in Sources */, + C84CC5591BDCF51200E06A64 /* SynchronizedSubscribeType.swift in Sources */, C8093D321B8A72BE0088E94D /* Reduce.swift in Sources */, C84B38EA1BA43380001B7D88 /* ScheduledItem.swift in Sources */, C8640A041BA5B12A00D3C4E8 /* Repeat.swift in Sources */, @@ -2214,10 +2254,12 @@ C8093D401B8A72BE0088E94D /* SubscribeOn.swift in Sources */, CBEE77201BD649A000AD584C /* ToArray.swift in Sources */, C8093CFE1B8A72BE0088E94D /* Observable.swift in Sources */, + C84CC55E1BDD010800E06A64 /* SynchronizedUnsubscribeType.swift in Sources */, C8093CE21B8A72BE0088E94D /* NAryDisposable.swift in Sources */, C8093CEC1B8A72BE0088E94D /* SerialDisposable.swift in Sources */, C8C3DA0D1B93959F004D233E /* Never.swift in Sources */, C8093D7C1B8A72BE0088E94D /* ObserverType+Extensions.swift in Sources */, + C84CC5681BDD08A500E06A64 /* SubscriptionDisposable.swift in Sources */, C8093CF61B8A72BE0088E94D /* Event.swift in Sources */, C8093D521B8A72BE0088E94D /* Zip.swift in Sources */, ); @@ -2272,7 +2314,9 @@ C89CDB361BCB0DD7002063D9 /* ShareReplay1.swift in Sources */, C8093D291B8A72BE0088E94D /* ObserveOn.swift in Sources */, C8093D351B8A72BE0088E94D /* Sample.swift in Sources */, + C84CC54E1BDCF48200E06A64 /* LockOwnerType.swift in Sources */, D285BAC41BC0231000B3F602 /* SkipUntil.swift in Sources */, + C84CC5531BDCF49300E06A64 /* SynchronizedOnType.swift in Sources */, C8093CE91B8A72BE0088E94D /* ScopedDisposable.swift in Sources */, C8093D251B8A72BE0088E94D /* Multicast.swift in Sources */, C8C3DA0F1B939767004D233E /* CurrentThreadScheduler.swift in Sources */, @@ -2287,6 +2331,7 @@ C8093D231B8A72BE0088E94D /* Merge.swift in Sources */, C8093D8D1B8A72BE0088E94D /* SchedulerType.swift in Sources */, C8093DA71B8A72BE0088E94D /* Variable.swift in Sources */, + C84CC5621BDD037900E06A64 /* SynchronizedDisposeType.swift in Sources */, C8093D951B8A72BE0088E94D /* OperationQueueScheduler.swift in Sources */, C8093D911B8A72BE0088E94D /* DispatchQueueSchedulerPriority.swift in Sources */, C8093D071B8A72BE0088E94D /* CombineLatest+arity.swift in Sources */, @@ -2319,6 +2364,7 @@ C8093D0D1B8A72BE0088E94D /* Concat.swift in Sources */, C8093CC91B8A72BE0088E94D /* Lock.swift in Sources */, C8093D431B8A72BE0088E94D /* Take.swift in Sources */, + C84CC5581BDCF51200E06A64 /* SynchronizedSubscribeType.swift in Sources */, C8093D311B8A72BE0088E94D /* Reduce.swift in Sources */, C84B38E91BA43380001B7D88 /* ScheduledItem.swift in Sources */, C8640A031BA5B12A00D3C4E8 /* Repeat.swift in Sources */, @@ -2336,10 +2382,12 @@ C8093D3F1B8A72BE0088E94D /* SubscribeOn.swift in Sources */, CBEE771F1BD649A000AD584C /* ToArray.swift in Sources */, C8093CFD1B8A72BE0088E94D /* Observable.swift in Sources */, + C84CC55D1BDD010800E06A64 /* SynchronizedUnsubscribeType.swift in Sources */, C8093CE11B8A72BE0088E94D /* NAryDisposable.swift in Sources */, C8093CEB1B8A72BE0088E94D /* SerialDisposable.swift in Sources */, C8C3DA0C1B93959F004D233E /* Never.swift in Sources */, C8093D7B1B8A72BE0088E94D /* ObserverType+Extensions.swift in Sources */, + C84CC5671BDD08A500E06A64 /* SubscriptionDisposable.swift in Sources */, C8093CF51B8A72BE0088E94D /* Event.swift in Sources */, C8093D511B8A72BE0088E94D /* Zip.swift in Sources */, ); @@ -2394,7 +2442,9 @@ C89CDB391BCB0DD7002063D9 /* ShareReplay1.swift in Sources */, C8F0BFB71BBBFB8B001B112F /* ObserveOn.swift in Sources */, C8F0BFB81BBBFB8B001B112F /* Sample.swift in Sources */, + C84CC5511BDCF48200E06A64 /* LockOwnerType.swift in Sources */, D21C29311BC6A1C300448E70 /* SkipUntil.swift in Sources */, + C84CC5561BDCF49300E06A64 /* SynchronizedOnType.swift in Sources */, C8F0BFB91BBBFB8B001B112F /* ScopedDisposable.swift in Sources */, C8F0BFBA1BBBFB8B001B112F /* Multicast.swift in Sources */, C8F0BFBB1BBBFB8B001B112F /* CurrentThreadScheduler.swift in Sources */, @@ -2409,6 +2459,7 @@ C8F0BFC31BBBFB8B001B112F /* Merge.swift in Sources */, C8F0BFC41BBBFB8B001B112F /* SchedulerType.swift in Sources */, C8F0BFC51BBBFB8B001B112F /* Variable.swift in Sources */, + C84CC5651BDD037900E06A64 /* SynchronizedDisposeType.swift in Sources */, C8F0BFC61BBBFB8B001B112F /* OperationQueueScheduler.swift in Sources */, C8F0BFC71BBBFB8B001B112F /* DispatchQueueSchedulerPriority.swift in Sources */, C8F0BFC81BBBFB8B001B112F /* CombineLatest+arity.swift in Sources */, @@ -2441,6 +2492,7 @@ C8F0BFE21BBBFB8B001B112F /* Concat.swift in Sources */, C8F0BFE31BBBFB8B001B112F /* Lock.swift in Sources */, C8F0BFE41BBBFB8B001B112F /* Take.swift in Sources */, + C84CC55B1BDCF51200E06A64 /* SynchronizedSubscribeType.swift in Sources */, C8F0BFE51BBBFB8B001B112F /* Reduce.swift in Sources */, C8F0BFE61BBBFB8B001B112F /* ScheduledItem.swift in Sources */, C8F0BFE71BBBFB8B001B112F /* Repeat.swift in Sources */, @@ -2458,10 +2510,12 @@ C8F0BFF31BBBFB8B001B112F /* SubscribeOn.swift in Sources */, CBEE77221BD649A000AD584C /* ToArray.swift in Sources */, C8F0BFF41BBBFB8B001B112F /* Observable.swift in Sources */, + C84CC5601BDD010800E06A64 /* SynchronizedUnsubscribeType.swift in Sources */, C8F0BFF51BBBFB8B001B112F /* NAryDisposable.swift in Sources */, C8F0BFF61BBBFB8B001B112F /* SerialDisposable.swift in Sources */, C8F0BFF71BBBFB8B001B112F /* Never.swift in Sources */, C8F0BFF81BBBFB8B001B112F /* ObserverType+Extensions.swift in Sources */, + C84CC56A1BDD08A500E06A64 /* SubscriptionDisposable.swift in Sources */, C8F0BFF91BBBFB8B001B112F /* Event.swift in Sources */, C8F0BFFA1BBBFB8B001B112F /* Zip.swift in Sources */, ); @@ -2666,7 +2720,9 @@ D2EBEB2A1BB9B6C5003A27DC /* Zip+CollectionType.swift in Sources */, C89CDB381BCB0DD7002063D9 /* ShareReplay1.swift in Sources */, D2EBEB401BB9B6DE003A27DC /* BehaviorSubject.swift in Sources */, + C84CC5501BDCF48200E06A64 /* LockOwnerType.swift in Sources */, D2EBEB271BB9B6C1003A27DC /* Timer.swift in Sources */, + C84CC5551BDCF49300E06A64 /* SynchronizedOnType.swift in Sources */, D2752D631BC5551B0070C418 /* SkipUntil.swift in Sources */, D2EBEB351BB9B6D2003A27DC /* ObserverBase.swift in Sources */, D2EBEB0F1BB9B6C1003A27DC /* Generate.swift in Sources */, @@ -2681,6 +2737,7 @@ D2EBEAFB1BB9B6B2003A27DC /* StableCompositeDisposable.swift in Sources */, D2EBEB011BB9B6BA003A27DC /* CombineLatest.swift in Sources */, D2EBEB021BB9B6BA003A27DC /* CombineLatest+arity.swift in Sources */, + C84CC5641BDD037900E06A64 /* SynchronizedDisposeType.swift in Sources */, D2EBEB211BB9B6C1003A27DC /* SubscribeOn.swift in Sources */, D2EBEB251BB9B6C1003A27DC /* TakeWhile.swift in Sources */, D2EBEB221BB9B6C1003A27DC /* Switch.swift in Sources */, @@ -2713,6 +2770,7 @@ D2EBEB2B1BB9B6CA003A27DC /* Observable+Aggregate.swift in Sources */, D2EBEB291BB9B6C1003A27DC /* Zip+arity.swift in Sources */, D2EBEB241BB9B6C1003A27DC /* TakeUntil.swift in Sources */, + C84CC55A1BDCF51200E06A64 /* SynchronizedSubscribeType.swift in Sources */, D2EBEB3B1BB9B6D8003A27DC /* OperationQueueScheduler.swift in Sources */, D2EBEAE51BB9B697003A27DC /* AnyObserver.swift in Sources */, D2EBEB3D1BB9B6D8003A27DC /* SchedulerServices+Emulation.swift in Sources */, @@ -2730,10 +2788,12 @@ D2EBEAF41BB9B6AE003A27DC /* DisposeBase.swift in Sources */, CBEE77211BD649A000AD584C /* ToArray.swift in Sources */, D2EBEB3F1BB9B6D8003A27DC /* CurrentThreadScheduler.swift in Sources */, + C84CC55F1BDD010800E06A64 /* SynchronizedUnsubscribeType.swift in Sources */, D2EBEAF21BB9B6AE003A27DC /* CompositeDisposable.swift in Sources */, D2EBEB0E1BB9B6C1003A27DC /* FlatMap.swift in Sources */, D2EBEB171BB9B6C1003A27DC /* Producer.swift in Sources */, D2EBEAF91BB9B6B2003A27DC /* SerialDisposable.swift in Sources */, + C84CC5691BDD08A500E06A64 /* SubscriptionDisposable.swift in Sources */, D2EBEB0A1BB9B6C1003A27DC /* Do.swift in Sources */, D2EBEB2E1BB9B6CA003A27DC /* Observable+Creation.swift in Sources */, ); diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index 580da957..02b11de6 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -113,6 +113,13 @@ C84B913C1B8A282000C9CCCF /* RxCollectionViewSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C859B9A71B45C83700D012D7 /* RxCollectionViewSectionedDataSource.swift */; }; C84B913D1B8A282000C9CCCF /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C859B9A91B45CB0900D012D7 /* RxCollectionViewSectionedAnimatedDataSource.swift */; }; C84CC52E1BDC344100E06A64 /* ElementAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC52D1BDC344100E06A64 /* ElementAt.swift */; }; + C84CC58B1BDD486300E06A64 /* LockOwnerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC56B1BDD08F500E06A64 /* LockOwnerType.swift */; }; + C84CC58C1BDD486300E06A64 /* SynchronizedDisposeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC56C1BDD08F500E06A64 /* SynchronizedDisposeType.swift */; }; + C84CC58D1BDD486300E06A64 /* SynchronizedOnType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC56D1BDD08F500E06A64 /* SynchronizedOnType.swift */; }; + C84CC58E1BDD486300E06A64 /* SynchronizedSubscribeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC56E1BDD08F500E06A64 /* SynchronizedSubscribeType.swift */; }; + C84CC58F1BDD486300E06A64 /* SynchronizedUnsubscribeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC56F1BDD08F500E06A64 /* SynchronizedUnsubscribeType.swift */; }; + C84CC5901BDD486300E06A64 /* AsyncLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894642A1BC6C2B00055219D /* AsyncLock.swift */; }; + C84CC5911BDD48B800E06A64 /* SubscriptionDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84CC5831BDD484400E06A64 /* SubscriptionDisposable.swift */; }; C859B9A41B45C5D900D012D7 /* PartialUpdatesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C859B9A31B45C5D900D012D7 /* PartialUpdatesViewController.swift */; }; C859B9AC1B45CF9100D012D7 /* NumberCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C859B9AB1B45CF9100D012D7 /* NumberCell.swift */; }; C859B9AE1B45CFAB00D012D7 /* NumberSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C859B9AD1B45CFAB00D012D7 /* NumberSectionView.swift */; }; @@ -140,7 +147,6 @@ C890A65A1AEBD28A00AFF7E6 /* GitHubAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C890A6591AEBD28A00AFF7E6 /* GitHubAPI.swift */; }; C890A65D1AEC084100AFF7E6 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C890A65C1AEC084100AFF7E6 /* ViewController.swift */; }; C894649E1BC6C2B00055219D /* Cancelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464281BC6C2B00055219D /* Cancelable.swift */; }; - C894649F1BC6C2B00055219D /* AsyncLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894642A1BC6C2B00055219D /* AsyncLock.swift */; }; C89464A01BC6C2B00055219D /* Lock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894642B1BC6C2B00055219D /* Lock.swift */; }; C89464A11BC6C2B00055219D /* ConnectableObservableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894642C1BC6C2B00055219D /* ConnectableObservableType.swift */; }; C89464A21BC6C2B00055219D /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894642E1BC6C2B00055219D /* Bag.swift */; }; @@ -501,6 +507,12 @@ C83367111AD029AE00C668A7 /* HtmlParsing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HtmlParsing.swift; sourceTree = ""; }; C83367121AD029AE00C668A7 /* ImageService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageService.swift; sourceTree = ""; }; C84CC52D1BDC344100E06A64 /* ElementAt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElementAt.swift; sourceTree = ""; }; + C84CC56B1BDD08F500E06A64 /* LockOwnerType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockOwnerType.swift; sourceTree = ""; }; + C84CC56C1BDD08F500E06A64 /* SynchronizedDisposeType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizedDisposeType.swift; sourceTree = ""; }; + C84CC56D1BDD08F500E06A64 /* SynchronizedOnType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizedOnType.swift; sourceTree = ""; }; + C84CC56E1BDD08F500E06A64 /* SynchronizedSubscribeType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizedSubscribeType.swift; sourceTree = ""; }; + C84CC56F1BDD08F500E06A64 /* SynchronizedUnsubscribeType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizedUnsubscribeType.swift; sourceTree = ""; }; + C84CC5831BDD484400E06A64 /* SubscriptionDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionDisposable.swift; sourceTree = ""; }; C859B9A31B45C5D900D012D7 /* PartialUpdatesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PartialUpdatesViewController.swift; sourceTree = ""; }; C859B9A51B45C80700D012D7 /* RxCollectionViewSectionedReloadDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewSectionedReloadDataSource.swift; sourceTree = ""; }; C859B9A71B45C83700D012D7 /* RxCollectionViewSectionedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewSectionedDataSource.swift; sourceTree = ""; }; @@ -1066,6 +1078,11 @@ C89464291BC6C2B00055219D /* Concurrency */ = { isa = PBXGroup; children = ( + C84CC56B1BDD08F500E06A64 /* LockOwnerType.swift */, + C84CC56C1BDD08F500E06A64 /* SynchronizedDisposeType.swift */, + C84CC56D1BDD08F500E06A64 /* SynchronizedOnType.swift */, + C84CC56E1BDD08F500E06A64 /* SynchronizedSubscribeType.swift */, + C84CC56F1BDD08F500E06A64 /* SynchronizedUnsubscribeType.swift */, C894642A1BC6C2B00055219D /* AsyncLock.swift */, C894642B1BC6C2B00055219D /* Lock.swift */, ); @@ -1085,6 +1102,7 @@ C89464321BC6C2B00055219D /* Disposables */ = { isa = PBXGroup; children = ( + C84CC5831BDD484400E06A64 /* SubscriptionDisposable.swift */, C89464331BC6C2B00055219D /* AnonymousDisposable.swift */, C89464341BC6C2B00055219D /* BinaryDisposable.swift */, C89464351BC6C2B00055219D /* CompositeDisposable.swift */, @@ -1644,6 +1662,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C84CC58B1BDD486300E06A64 /* LockOwnerType.swift in Sources */, C89465971BC6C2BC0055219D /* UIScrollView+Rx.swift in Sources */, C8297E2F1B6CF905000589EA /* RxTableViewSectionedAnimatedDataSource.swift in Sources */, C89464FF1BC6C2B00055219D /* DispatchQueueSchedulerPriority.swift in Sources */, @@ -1661,6 +1680,7 @@ C89465731BC6C2BC0055219D /* Deallocating.swift in Sources */, C89464A51BC6C2B00055219D /* Disposable.swift in Sources */, C89464F91BC6C2B00055219D /* ObserverType+Extensions.swift in Sources */, + C84CC58D1BDD486300E06A64 /* SynchronizedOnType.swift in Sources */, C89464DC1BC6C2B00055219D /* Scan.swift in Sources */, C89464B21BC6C2B00055219D /* StableCompositeDisposable.swift in Sources */, C89464AE1BC6C2B00055219D /* ScheduledDisposable.swift in Sources */, @@ -1681,6 +1701,7 @@ C89465961BC6C2BC0055219D /* UILabel+Rx.swift in Sources */, C894659C1BC6C2BC0055219D /* UISwitch+Rx.swift in Sources */, C89464F81BC6C2B00055219D /* TailRecursiveSink.swift in Sources */, + C84CC58C1BDD486300E06A64 /* SynchronizedDisposeType.swift in Sources */, C89464BF1BC6C2B00055219D /* CombineLatest+arity.swift in Sources */, C89465751BC6C2BC0055219D /* KVOObservable.swift in Sources */, C89464CB1BC6C2B00055219D /* FailWith.swift in Sources */, @@ -1761,10 +1782,12 @@ C8297E3F1B6CF905000589EA /* SectionModelType.swift in Sources */, C8297E401B6CF905000589EA /* ImageService.swift in Sources */, C89464AD1BC6C2B00055219D /* NopDisposable.swift in Sources */, + C84CC5901BDD486300E06A64 /* AsyncLock.swift in Sources */, CBEE77541BD8C7B700AD584C /* ToArray.swift in Sources */, C89465771BC6C2BC0055219D /* NSNotificationCenter+Rx.swift in Sources */, C89465091BC6C2B00055219D /* ReplaySubject.swift in Sources */, C8297E411B6CF905000589EA /* RxCollectionViewSectionedReloadDataSource.swift in Sources */, + C84CC58E1BDD486300E06A64 /* SynchronizedSubscribeType.swift in Sources */, C89464A81BC6C2B00055219D /* CompositeDisposable.swift in Sources */, C89464D21BC6C2B00055219D /* Multicast.swift in Sources */, C89465821BC6C2BC0055219D /* RxCollectionViewDataSourceType.swift in Sources */, @@ -1808,6 +1831,7 @@ C89464E31BC6C2B00055219D /* TakeUntil.swift in Sources */, C89464FB1BC6C2B00055219D /* Rx.swift in Sources */, C89464FD1BC6C2B00055219D /* ConcurrentDispatchQueueScheduler.swift in Sources */, + C84CC5911BDD48B800E06A64 /* SubscriptionDisposable.swift in Sources */, C89464C71BC6C2B00055219D /* DelaySubscription.swift in Sources */, C8297E481B6CF905000589EA /* Differentiator.swift in Sources */, C8297E491B6CF905000589EA /* WikipediaSearchCell.swift in Sources */, @@ -1820,6 +1844,7 @@ C89464D31BC6C2B00055219D /* Never.swift in Sources */, C8297E4D1B6CF905000589EA /* RxTableViewSectionedReloadDataSource.swift in Sources */, C89465931BC6C2BC0055219D /* UIDatePicker+Rx.swift in Sources */, + C84CC58F1BDD486300E06A64 /* SynchronizedUnsubscribeType.swift in Sources */, C8297E4E1B6CF905000589EA /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */, C89CDB711BCC45E5002063D9 /* ShareReplay1.swift in Sources */, C89464BD1BC6C2B00055219D /* Buffer.swift in Sources */, @@ -1838,7 +1863,6 @@ D2AF91981BD3D95900A008C1 /* Using.swift in Sources */, C8297E501B6CF905000589EA /* TableViewController.swift in Sources */, C8297E511B6CF905000589EA /* PartialUpdatesViewController.swift in Sources */, - C894649F1BC6C2B00055219D /* AsyncLock.swift in Sources */, C8B145141BD2E4D000267DCE /* ConcurrentMainScheduler.swift in Sources */, C8297E521B6CF905000589EA /* Dependencies.swift in Sources */, C80DDED91BCE9046006A1832 /* ObservableConvertibleType+Driver.swift in Sources */, diff --git a/RxSwift/Concurrency/Lock.swift b/RxSwift/Concurrency/Lock.swift index 5ec4a06d..df4d6f40 100644 --- a/RxSwift/Concurrency/Lock.swift +++ b/RxSwift/Concurrency/Lock.swift @@ -8,6 +8,11 @@ import Foundation +protocol Lock { + func lock() + func unlock() +} + /** Simple wrapper for spin lock. */ @@ -49,7 +54,7 @@ struct SpinLock { } } -extension NSRecursiveLock { +extension NSRecursiveLock : Lock { func performLocked(@noescape action: () -> Void) { self.lock() action() diff --git a/RxSwift/Concurrency/LockOwnerType.swift b/RxSwift/Concurrency/LockOwnerType.swift new file mode 100644 index 00000000..b11fcaa8 --- /dev/null +++ b/RxSwift/Concurrency/LockOwnerType.swift @@ -0,0 +1,23 @@ +// +// LockOwnerType.swift +// Rx +// +// Created by Krunoslav Zaher on 10/25/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +protocol LockOwnerType : class, Lock { + var _lock: NSRecursiveLock { get } +} + +extension LockOwnerType { + func lock() { + _lock.lock() + } + + func unlock() { + _lock.unlock() + } +} \ No newline at end of file diff --git a/RxSwift/Concurrency/SynchronizedDisposeType.swift b/RxSwift/Concurrency/SynchronizedDisposeType.swift new file mode 100644 index 00000000..5764575e --- /dev/null +++ b/RxSwift/Concurrency/SynchronizedDisposeType.swift @@ -0,0 +1,20 @@ +// +// SynchronizedDisposeType.swift +// Rx +// +// Created by Krunoslav Zaher on 10/25/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +protocol SynchronizedDisposeType : class, Disposable, Lock { + func _synchronized_dispose() +} + +extension SynchronizedDisposeType { + func synchronizedDispose() { + lock(); defer { unlock() } + _synchronized_dispose() + } +} \ No newline at end of file diff --git a/RxSwift/Concurrency/SynchronizedOnType.swift b/RxSwift/Concurrency/SynchronizedOnType.swift new file mode 100644 index 00000000..84a3df5d --- /dev/null +++ b/RxSwift/Concurrency/SynchronizedOnType.swift @@ -0,0 +1,20 @@ +// +// SynchronizedOnType.swift +// Rx +// +// Created by Krunoslav Zaher on 10/25/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +protocol SynchronizedOnType : class, ObserverType, Lock { + func _synchronized_on(event: Event) +} + +extension SynchronizedOnType { + func synchronizedOn(event: Event) { + lock(); defer { unlock() } + _synchronized_on(event) + } +} \ No newline at end of file diff --git a/RxSwift/Concurrency/SynchronizedSubscribeType.swift b/RxSwift/Concurrency/SynchronizedSubscribeType.swift new file mode 100644 index 00000000..a4903157 --- /dev/null +++ b/RxSwift/Concurrency/SynchronizedSubscribeType.swift @@ -0,0 +1,20 @@ +// +// SynchronizedSubscribeType.swift +// Rx +// +// Created by Krunoslav Zaher on 10/25/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +protocol SynchronizedSubscribeType : class, ObservableType, Lock { + func _synchronized_subscribe(observer: O) -> Disposable +} + +extension SynchronizedSubscribeType { + func synchronizedSubscribe(observer: O) -> Disposable { + lock(); defer { unlock() } + return _synchronized_subscribe(observer) + } +} \ No newline at end of file diff --git a/RxSwift/Concurrency/SynchronizedUnsubscribeType.swift b/RxSwift/Concurrency/SynchronizedUnsubscribeType.swift new file mode 100644 index 00000000..a73f8fca --- /dev/null +++ b/RxSwift/Concurrency/SynchronizedUnsubscribeType.swift @@ -0,0 +1,22 @@ +// +// SynchronizedUnsubscribeType.swift +// Rx +// +// Created by Krunoslav Zaher on 10/25/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +protocol SynchronizedUnsubscribeType : class, Lock { + typealias DisposeKey + + func _synchronized_unsubscribe(disposeKey: DisposeKey) +} + +extension SynchronizedUnsubscribeType { + func synchronizedUnsubscribe(disposeKey: DisposeKey) { + lock(); defer { unlock() } + _synchronized_unsubscribe(disposeKey) + } +} \ No newline at end of file diff --git a/RxSwift/DataStructures/Bag.swift b/RxSwift/DataStructures/Bag.swift index e9023b69..72968fe4 100644 --- a/RxSwift/DataStructures/Bag.swift +++ b/RxSwift/DataStructures/Bag.swift @@ -53,7 +53,7 @@ public struct Bag : CustomStringConvertible { private var _uniqueIdentity: Identity? private var _nextKey: ScopeUniqueTokenType = 0 - private var _pairs = [Entry]() + var _pairs = ContiguousArray() /** Creates new empty `Bag`. diff --git a/RxSwift/Disposables/SubscriptionDisposable.swift b/RxSwift/Disposables/SubscriptionDisposable.swift new file mode 100644 index 00000000..7a3e1e97 --- /dev/null +++ b/RxSwift/Disposables/SubscriptionDisposable.swift @@ -0,0 +1,23 @@ +// +// SubscriptionDisposable.swift +// Rx +// +// Created by Krunoslav Zaher on 10/25/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +struct SubscriptionDisposable : Disposable { + private let _key: T.DisposeKey + private weak var _owner: T? + + init(owner: T, key: T.DisposeKey) { + _owner = owner + _key = key + } + + func dispose() { + _owner?.synchronizedUnsubscribe(_key) + } +} \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/CombineLatest.swift b/RxSwift/Observables/Implementations/CombineLatest.swift index 40926eff..300cf2db 100644 --- a/RxSwift/Observables/Implementations/CombineLatest.swift +++ b/RxSwift/Observables/Implementations/CombineLatest.swift @@ -90,13 +90,16 @@ class CombineLatestSink : Sink, CombineLatestProtocol { } } -class CombineLatestObserver : ObserverType { +class CombineLatestObserver + : ObserverType + , LockOwnerType + , SynchronizedOnType { typealias Element = ElementType typealias ValueSetter = (Element) -> Void private let _parent: CombineLatestProtocol - private let _lock: NSRecursiveLock + let _lock: NSRecursiveLock private let _index: Int private let _this: Disposable private let _setLatestValue: ValueSetter @@ -110,18 +113,20 @@ class CombineLatestObserver : ObserverType { } func on(event: Event) { - _lock.performLocked { - switch event { - case .Next(let value): - _setLatestValue(value) - _parent.next(_index) - case .Error(let error): - _this.dispose() - _parent.fail(error) - case .Completed: - _this.dispose() - _parent.done(_index) - } + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { + switch event { + case .Next(let value): + _setLatestValue(value) + _parent.next(_index) + case .Error(let error): + _this.dispose() + _parent.fail(error) + case .Completed: + _this.dispose() + _parent.done(_index) } } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/ShareReplay1.swift b/RxSwift/Observables/Implementations/ShareReplay1.swift index 9ae0accd..ba478eea 100644 --- a/RxSwift/Observables/Implementations/ShareReplay1.swift +++ b/RxSwift/Observables/Implementations/ShareReplay1.swift @@ -9,10 +9,19 @@ import Foundation // optimized version of share replay for most common case -class ShareReplay1 : Observable, ObserverType { +class ShareReplay1 + : Observable + , ObserverType + , LockOwnerType + , SynchronizedOnType + , SynchronizedSubscribeType + , SynchronizedUnsubscribeType { + + typealias DisposeKey = Bag>.KeyType + private let _source: Observable - private let _lock = NSRecursiveLock() + let _lock = NSRecursiveLock() private var _subscription: Disposable? private var _element: Element? @@ -23,55 +32,61 @@ class ShareReplay1 : Observable, ObserverType { self._source = source } + override func subscribe(observer: O) -> Disposable { - return _lock.calculateLocked { - if let element = self._element { - observer.on(.Next(element)) - } + return synchronizedSubscribe(observer) + } - if let stopEvent = self._stopEvent { - observer.on(stopEvent) - return NopDisposable.instance - } + func _synchronized_subscribe(observer: O) -> Disposable { + if let element = self._element { + observer.on(.Next(element)) + } - let initialCount = self._observers.count + if let stopEvent = self._stopEvent { + observer.on(stopEvent) + return NopDisposable.instance + } - let observerKey = self._observers.insert(AnyObserver(observer)) + let initialCount = self._observers.count - if initialCount == 0 { - self._subscription = self._source.subscribe(self) - } + let disposeKey = self._observers.insert(AnyObserver(observer)) - return AnonymousDisposable { - self._lock.performLocked { - self._observers.removeKey(observerKey) + if initialCount == 0 { + self._subscription = self._source.subscribe(self) + } - if self._observers.count == 0 { - self._subscription?.dispose() - self._subscription = nil - } - } - } + return SubscriptionDisposable(owner: self, key: disposeKey) + } + + func _synchronized_unsubscribe(disposeKey: DisposeKey) { + // if already unsubscribed, just return + if self._observers.removeKey(disposeKey) == nil { + return + } + + if self._observers.count == 0 { + self._subscription?.dispose() + self._subscription = nil } } func on(event: Event) { - _lock.performLocked { - if self._stopEvent != nil { - return - } + synchronizedOn(event) + } - if case .Next(let element) = event { - self._element = element - } - - if event.isStopEvent { - self._stopEvent = event - } - - _observers.forEach { o in - o.on(event) - } + func _synchronized_on(event: Event) { + if self._stopEvent != nil { + return } + + if case .Next(let element) = event { + self._element = element + } + + if event.isStopEvent { + self._stopEvent = event + } + + _observers.on(event) } } \ No newline at end of file diff --git a/RxSwift/Schedulers/CurrentThreadScheduler.swift b/RxSwift/Schedulers/CurrentThreadScheduler.swift index 89779860..b839a4a8 100644 --- a/RxSwift/Schedulers/CurrentThreadScheduler.swift +++ b/RxSwift/Schedulers/CurrentThreadScheduler.swift @@ -9,6 +9,7 @@ import Foundation let CurrentThreadSchedulerKeyInstance = CurrentThreadSchedulerKey() +let CurrentThreadSchedulerQueueKeyInstance = CurrentThreadSchedulerQueueKey() class CurrentThreadSchedulerKey : NSObject, NSCopying { override func isEqual(object: AnyObject?) -> Bool { @@ -26,6 +27,22 @@ class CurrentThreadSchedulerKey : NSObject, NSCopying { } } +class CurrentThreadSchedulerQueueKey : NSObject, NSCopying { + override func isEqual(object: AnyObject?) -> Bool { + return object === CurrentThreadSchedulerQueueKeyInstance + } + + override var hashValue: Int { return -904739207 } + + override func copy() -> AnyObject { + return CurrentThreadSchedulerQueueKeyInstance + } + + func copyWithZone(zone: NSZone) -> AnyObject { + return CurrentThreadSchedulerQueueKeyInstance + } +} + /** Represents an object that schedules units of work on the current thread. @@ -43,15 +60,15 @@ public class CurrentThreadScheduler : ImmediateSchedulerType { static var queue : ScheduleQueue? { get { - return NSThread.currentThread().threadDictionary[CurrentThreadSchedulerKeyInstance] as? ScheduleQueue + return NSThread.currentThread().threadDictionary[CurrentThreadSchedulerQueueKeyInstance] as? ScheduleQueue } set { let threadDictionary = NSThread.currentThread().threadDictionary if let newValue = newValue { - threadDictionary[CurrentThreadSchedulerKeyInstance] = newValue + threadDictionary[CurrentThreadSchedulerQueueKeyInstance] = newValue } else { - threadDictionary.removeObjectForKey(CurrentThreadSchedulerKeyInstance) + threadDictionary.removeObjectForKey(CurrentThreadSchedulerQueueKeyInstance) } } } @@ -59,8 +76,18 @@ public class CurrentThreadScheduler : ImmediateSchedulerType { /** Gets a value that indicates whether the caller must call a `schedule` method. */ - public static var isScheduleRequired: Bool { - return NSThread.currentThread().threadDictionary[CurrentThreadSchedulerKeyInstance] == nil + public static private(set) var isScheduleRequired: Bool { + get { + return NSThread.currentThread().threadDictionary[CurrentThreadSchedulerKeyInstance] == nil + } + set(value) { + if value { + NSThread.currentThread().threadDictionary.removeObjectForKey(CurrentThreadSchedulerKeyInstance) + } + else { + NSThread.currentThread().threadDictionary[CurrentThreadSchedulerKeyInstance] = CurrentThreadSchedulerKeyInstance + } + } } /** @@ -74,28 +101,43 @@ public class CurrentThreadScheduler : ImmediateSchedulerType { - returns: The disposable object used to cancel the scheduled action (best effort). */ public func schedule(state: StateType, action: (StateType) -> Disposable) -> Disposable { - let queue = CurrentThreadScheduler.queue - - if let queue = queue { - let scheduledItem = ScheduledItem(action: action, state: state, time: 0) - queue.value.enqueue(scheduledItem) - return scheduledItem - } - - let newQueue = RxMutableBox(Queue(capacity: 0)) - CurrentThreadScheduler.queue = newQueue - - action(state) - - while let latest = newQueue.value.tryDequeue() { - if latest.disposed { - continue + if CurrentThreadScheduler.isScheduleRequired { + CurrentThreadScheduler.isScheduleRequired = false + + action(state) + + defer { + CurrentThreadScheduler.isScheduleRequired = true + CurrentThreadScheduler.queue = nil } - latest.invoke() + + guard let queue = CurrentThreadScheduler.queue else { + return NopDisposable.instance + } + + while let latest = queue.value.tryDequeue() { + if latest.disposed { + continue + } + latest.invoke() + } + + return NopDisposable.instance } - - CurrentThreadScheduler.queue = nil - - return NopDisposable.instance + + let existingQueue = CurrentThreadScheduler.queue + + let queue: RxMutableBox> + if let existingQueue = existingQueue { + queue = existingQueue + } + else { + queue = RxMutableBox(Queue(capacity: 1)) + CurrentThreadScheduler.queue = queue + } + + let scheduledItem = ScheduledItem(action: action, state: state, time: 0) + queue.value.enqueue(scheduledItem) + return scheduledItem } } \ No newline at end of file diff --git a/RxSwift/Subjects/BehaviorSubject.swift b/RxSwift/Subjects/BehaviorSubject.swift index 74ffb6e6..7623332d 100644 --- a/RxSwift/Subjects/BehaviorSubject.swift +++ b/RxSwift/Subjects/BehaviorSubject.swift @@ -103,7 +103,7 @@ public final class BehaviorSubject : Observable, SubjectType, _stoppedEvent = event } - _observers.forEach { $0.on(event) } + _observers.on(event) } } diff --git a/RxSwift/Subjects/PublishSubject.swift b/RxSwift/Subjects/PublishSubject.swift index 52909ed5..fd1d5712 100644 --- a/RxSwift/Subjects/PublishSubject.swift +++ b/RxSwift/Subjects/PublishSubject.swift @@ -8,46 +8,26 @@ import Foundation -class Subscription : Disposable { - typealias KeyType = Bag>.KeyType - - private var _lock = SpinLock() - - // state - private var _subject: PublishSubject? - private var _key: KeyType? - - init(subject: PublishSubject, key: KeyType) { - _key = key - _subject = subject - } - - func dispose() { - _lock.performLocked { - guard let subject = _subject, - let key = _key else { - return - } - - _subject = nil - _key = nil - - subject.unsubscribe(key) - } - } -} - /** Represents an object that is both an observable sequence as well as an observer. Each notification is broadcasted to all subscribed observers. */ -public class PublishSubject : Observable, SubjectType, Cancelable, ObserverType { +public class PublishSubject + : Observable + , SubjectType + , Cancelable + , ObserverType + , LockOwnerType + , SynchronizedOnType + , SynchronizedSubscribeType + , SynchronizedUnsubscribeType + , SynchronizedDisposeType { public typealias SubjectObserverType = PublishSubject typealias DisposeKey = Bag>.KeyType - private let _lock = NSRecursiveLock() + let _lock = NSRecursiveLock() // state private var _disposed = false @@ -59,9 +39,7 @@ public class PublishSubject : Observable, SubjectType, Cancela */ public var disposed: Bool { get { - return _lock.calculateLocked { - return _disposed - } + return _disposed } } @@ -78,20 +56,22 @@ public class PublishSubject : Observable, SubjectType, Cancela - parameter event: Event to send to the observers. */ public func on(event: Event) { - _lock.performLocked { - switch event { - case .Next(_): - if disposed || _stoppedEvent != nil { - return - } - - _observers.forEach { $0.on(event) } - case .Completed, .Error: - if _stoppedEvent == nil { - _stoppedEvent = event - _observers.forEach { $0.on(event) } - _observers.removeAll() - } + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { + switch event { + case .Next(_): + if _disposed || _stoppedEvent != nil { + return + } + + _observers.on(event) + case .Completed, .Error: + if _stoppedEvent == nil { + _stoppedEvent = event + _observers.on(event) + _observers.removeAll() } } } @@ -103,26 +83,27 @@ public class PublishSubject : Observable, SubjectType, Cancela - returns: Disposable object that can be used to unsubscribe the observer from the subject. */ public override func subscribe(observer: O) -> Disposable { - return _lock.calculateLocked { - if let stoppedEvent = _stoppedEvent { - observer.on(stoppedEvent) - return NopDisposable.instance - } - - if disposed { - observer.on(.Error(RxError.DisposedError)) - return NopDisposable.instance - } - - let key = _observers.insert(observer.asObserver()) - return Subscription(subject: self, key: key) - } + return synchronizedSubscribe(observer) } - func unsubscribe(key: DisposeKey) { - _lock.performLocked { - _ = _observers.removeKey(key) + func _synchronized_subscribe(observer: O) -> Disposable { + if let stoppedEvent = _stoppedEvent { + observer.on(stoppedEvent) + return NopDisposable.instance } + + if _disposed { + observer.on(.Error(RxError.DisposedError)) + return NopDisposable.instance + } + + let key = _observers.insert(observer.asObserver()) + return SubscriptionDisposable(owner: self, key: key) + } + + + func _synchronized_unsubscribe(disposeKey: DisposeKey) { + _ = _observers.removeKey(disposeKey) } /** @@ -136,10 +117,12 @@ public class PublishSubject : Observable, SubjectType, Cancela Unsubscribe all observers and release resources. */ public func dispose() { - _lock.performLocked { - _disposed = true - _observers.removeAll() - _stoppedEvent = nil - } + synchronizedDispose() + } + + func _synchronized_dispose() { + _disposed = true + _observers.removeAll() + _stoppedEvent = nil } } \ No newline at end of file diff --git a/RxSwift/Subjects/ReplaySubject.swift b/RxSwift/Subjects/ReplaySubject.swift index d868367b..97332e4c 100644 --- a/RxSwift/Subjects/ReplaySubject.swift +++ b/RxSwift/Subjects/ReplaySubject.swift @@ -99,11 +99,11 @@ class ReplayBufferBase : ReplaySubject { case .Next(let value): addValueToBuffer(value) trim() - _observers.forEach { $0.on(event) } + _observers.on(event) case .Error, .Completed: _stoppedEvent = event trim() - _observers.forEach { $0.on(event) } + _observers.on(event) _observers.removeAll() } diff --git a/RxSwift/Subjects/Variable.swift b/RxSwift/Subjects/Variable.swift index aaeb6bd9..e9c04472 100644 --- a/RxSwift/Subjects/Variable.swift +++ b/RxSwift/Subjects/Variable.swift @@ -32,14 +32,14 @@ public class Variable : ObservableType { */ public var value: E { get { - return _lock.calculateLocked { - return _value - } + _lock.lock(); defer { _lock.unlock() } + return _value } set(newValue) { - _lock.performLocked { - _value = newValue - } + _lock.lock() + _value = newValue + _lock.unlock() + _subject.on(.Next(newValue)) } } diff --git a/RxTests/PerformanceTests/main.swift b/RxTests/PerformanceTests/main.swift index 8d5f179c..9209d000 100644 --- a/RxTests/PerformanceTests/main.swift +++ b/RxTests/PerformanceTests/main.swift @@ -12,82 +12,19 @@ import RxCocoa import AppKit import CoreLocation -let NumberOfIterations = 1000 - -func approxValuePerIteration(total: Int) -> UInt64 { - return UInt64(round(Double(total) / Double(NumberOfIterations))) -} - -func approxValuePerIteration(total: UInt64) -> UInt64 { - return UInt64(round(Double(total) / Double(NumberOfIterations))) -} - -func measureTime(@noescape work: () -> ()) -> UInt64 { - var timebaseInfo: mach_timebase_info = mach_timebase_info() - let res = mach_timebase_info(&timebaseInfo) - - assert(res == 0) - - let start = mach_absolute_time() - for _ in 0 ..< NumberOfIterations { - work() - } - let timeInNano = (mach_absolute_time() - start) * UInt64(timebaseInfo.numer) / UInt64(timebaseInfo.denom) - - return approxValuePerIteration(timeInNano) / UInt64(NumberOfIterations) -} - -func measureMemoryUsage(@noescape work: () -> ()) -> (bytesAllocated: UInt64, allocations: UInt64) { - let (bytes, allocations) = getMemoryInfo() - for _ in 0 ..< NumberOfIterations { - work() - } - let (bytesAfter, allocationsAfter) = getMemoryInfo() - - return (approxValuePerIteration(bytesAfter - bytes), approxValuePerIteration(allocationsAfter - allocations)) -} - let bechmarkTime = true -func compareTwoImplementations(@noescape first first: () -> (), @noescape second: () -> ()) { - // first warm up to keep it fair - first() - second() - - let time1: UInt64 - let time2: UInt64 - - if bechmarkTime { - time1 = measureTime(first) - time2 = measureTime(second) - } - else { - time1 = 0 - time2 = 0 - } - - registerMallocHooks() - - let memory1 = measureMemoryUsage(first) - let memory2 = measureMemoryUsage(second) - - // this is good enough - print(String(format: "#1 implementation %8d bytes %4d allocations %5d useconds", arguments: [ - memory1.bytesAllocated, - memory1.allocations, - time1 - ])) - print(String(format: "#2 implementation %8d bytes %4d allocations %5d useconds", arguments: [ - memory2.bytesAllocated, - memory2.allocations, - time2 - ])) +func allocation() { + } -compareTwoImplementations(first: { +compareTwoImplementations(benchmarkTime: true, first: { let publishSubject = PublishSubject() - publishSubject + let a = just(1) + + //combineLatest(a, + publishSubject .shareReplay(1) //.map { $0 } //.filter { _ in true }// ){ x, _ in x } diff --git a/RxTests/RxSwiftTests/TestImplementations/Mocks/HotObservable.swift b/RxTests/RxSwiftTests/TestImplementations/Mocks/HotObservable.swift index 45ecb62d..bbaea4d2 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Mocks/HotObservable.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Mocks/HotObservable.swift @@ -30,7 +30,7 @@ class HotObservable : ObservableType, ObservableConvertible for recordedEvent in recordedEvents { testScheduler.schedule((), time: recordedEvent.time) { t in - self.observers.forEach { $0.on(recordedEvent.event) } + self.observers.on(recordedEvent.event) return NopDisposable.instance } } diff --git a/RxTests/RxSwiftTests/TestImplementations/Mocks/PrimitiveHotObservable.swift b/RxTests/RxSwiftTests/TestImplementations/Mocks/PrimitiveHotObservable.swift index 14056f6a..a5d47e02 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Mocks/PrimitiveHotObservable.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Mocks/PrimitiveHotObservable.swift @@ -29,7 +29,7 @@ class PrimitiveHotObservable : ObservableType { } func on(event: Event) { - observers.forEach { $0.on(event) } + observers.on(event) } func subscribe(observer: O) -> Disposable { diff --git a/RxTests/RxTests.xcodeproj/project.pbxproj b/RxTests/RxTests.xcodeproj/project.pbxproj index 97e01155..5b5313c8 100644 --- a/RxTests/RxTests.xcodeproj/project.pbxproj +++ b/RxTests/RxTests.xcodeproj/project.pbxproj @@ -1026,6 +1026,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; OTHER_LDFLAGS = ""; + OTHER_SWIFT_FLAGS = "$(inherits) -D ALLOC_HOOK"; PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.PerformanceTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; @@ -1070,6 +1071,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; OTHER_LDFLAGS = ""; + OTHER_SWIFT_FLAGS = "$(inherits) -D ALLOC_HOOK"; PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.PerformanceTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; @@ -1113,6 +1115,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; OTHER_LDFLAGS = ""; + OTHER_SWIFT_FLAGS = "$(inherits) -D ALLOC_HOOK"; PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.PerformanceTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; diff --git a/RxTests/Tests/PerformanceTools.swift b/RxTests/Tests/PerformanceTools.swift index bc2422c9..3ccd1f0c 100644 --- a/RxTests/Tests/PerformanceTools.swift +++ b/RxTests/Tests/PerformanceTools.swift @@ -16,18 +16,27 @@ var bytesAllocated: Int64 = 0 func call0(p: UnsafeMutablePointer<_malloc_zone_t>, size: Int) -> UnsafeMutablePointer { OSAtomicIncrement64(&allocCalls) OSAtomicAdd64(Int64(size), &bytesAllocated) +#if ALLOC_HOOK + allocation() +#endif return mallocFunctions[0](p, size) } func call1(p: UnsafeMutablePointer<_malloc_zone_t>, size: Int) -> UnsafeMutablePointer { OSAtomicIncrement64(&allocCalls) OSAtomicAdd64(Int64(size), &bytesAllocated) +#if ALLOC_HOOK + allocation() +#endif return mallocFunctions[1](p, size) } func call2(p: UnsafeMutablePointer<_malloc_zone_t>, size: Int) -> UnsafeMutablePointer { OSAtomicIncrement64(&allocCalls) OSAtomicAdd64(Int64(size), &bytesAllocated) +#if ALLOC_HOOK + allocation() +#endif return mallocFunctions[2](p, size) } @@ -85,3 +94,108 @@ func registerMallocHooks() { } } +// MARK: Benchmark tools + +let NumberOfIterations = 10000 + +class A { + let _0 = 0 + let _1 = 0 + let _2 = 0 + let _3 = 0 + let _4 = 0 + let _5 = 0 + let _6 = 0 +} + +class B { + var a = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29] +} + +let numberOfObjects = 1000000 +let aliveAtTheEnd = numberOfObjects / 10 + +var objects: [AnyObject] = [] + +func fragmentMemory() { + objects = [AnyObject](count: aliveAtTheEnd, repeatedValue: A()) + for _ in 0 ..< numberOfObjects { + objects[Int(arc4random_uniform(UInt32(aliveAtTheEnd)))] = arc4random_uniform(2) == 0 ? A() : B() + } +} + +func approxValuePerIteration(total: Int) -> UInt64 { + return UInt64(round(Double(total) / Double(NumberOfIterations))) +} + +func approxValuePerIteration(total: UInt64) -> UInt64 { + return UInt64(round(Double(total) / Double(NumberOfIterations))) +} + +func measureTime(@noescape work: () -> ()) -> UInt64 { + var timebaseInfo: mach_timebase_info = mach_timebase_info() + let res = mach_timebase_info(&timebaseInfo) + + assert(res == 0) + + let start = mach_absolute_time() + for _ in 0 ..< NumberOfIterations { + work() + } + let timeInNano = (mach_absolute_time() - start) * UInt64(timebaseInfo.numer) / UInt64(timebaseInfo.denom) + + return approxValuePerIteration(timeInNano) / 1000 +} + +func measureMemoryUsage(@noescape work: () -> ()) -> (bytesAllocated: UInt64, allocations: UInt64) { + let (bytes, allocations) = getMemoryInfo() + for _ in 0 ..< NumberOfIterations { + work() + } + let (bytesAfter, allocationsAfter) = getMemoryInfo() + + return (approxValuePerIteration(bytesAfter - bytes), approxValuePerIteration(allocationsAfter - allocations)) +} + +func compareTwoImplementations(benchmarkTime benchmarkTime: Bool, @noescape first: () -> (), @noescape second: () -> ()) { + print("Fragmenting memory ...") + fragmentMemory() + print("Benchmarking ...") + + // first warm up to keep it fair + + let time1: UInt64 + let time2: UInt64 + + if benchmarkTime { + first() + second() + + time1 = measureTime(first) + time2 = measureTime(second) + } + else { + time1 = 0 + time2 = 0 + } + + registerMallocHooks() + + first() + second() + + let memory1 = measureMemoryUsage(first) + let memory2 = measureMemoryUsage(second) + + // this is good enough + print(String(format: "#1 implementation %8d bytes %4d allocations %5d useconds", arguments: [ + memory1.bytesAllocated, + memory1.allocations, + time1 + ])) + print(String(format: "#2 implementation %8d bytes %4d allocations %5d useconds", arguments: [ + memory2.bytesAllocated, + memory2.allocations, + time2 + ])) +} From 6fdb23e222590dc322d0518fbac22d26151a4f55 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 25 Oct 2015 21:46:27 +0100 Subject: [PATCH 136/210] Replaces `Array` with `ContiguousArray` for `Queue` buffer data structure. --- RxSwift/DataStructures/Queue.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/RxSwift/DataStructures/Queue.swift b/RxSwift/DataStructures/Queue.swift index 124f32c7..43db307c 100644 --- a/RxSwift/DataStructures/Queue.swift +++ b/RxSwift/DataStructures/Queue.swift @@ -24,7 +24,7 @@ public struct Queue: SequenceType { private let _resizeFactor = 2 - private var _storage: [T?] + private var _storage: ContiguousArray private var _count: Int private var _pushNextIndex: Int private var _initialCapacity: Int @@ -41,10 +41,10 @@ public struct Queue: SequenceType { _pushNextIndex = 0 if capacity > 0 { - _storage = [T?](count: capacity, repeatedValue: nil) + _storage = ContiguousArray(count: capacity, repeatedValue: nil) } else { - _storage = [] + _storage = ContiguousArray() } } @@ -83,7 +83,7 @@ public struct Queue: SequenceType { } mutating private func resizeTo(size: Int) { - var newStorage = [T?](count: size, repeatedValue: nil) + var newStorage = ContiguousArray(count: size, repeatedValue: nil) let count = _count From f86a5923d5880003982051595443d24783c7f90a Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 26 Oct 2015 12:12:01 +0100 Subject: [PATCH 137/210] Optimizes `Bag`. --- RxSwift/DataStructures/Bag.swift | 160 +++++++++++++- RxSwift/Disposables/CompositeDisposable.swift | 2 +- RxTests/RxSwiftTests/Tests/BagTest.swift | 206 ++++++++++++++---- 3 files changed, 307 insertions(+), 61 deletions(-) diff --git a/RxSwift/DataStructures/Bag.swift b/RxSwift/DataStructures/Bag.swift index 72968fe4..2f023b6c 100644 --- a/RxSwift/DataStructures/Bag.swift +++ b/RxSwift/DataStructures/Bag.swift @@ -7,6 +7,9 @@ // import Foundation +import Swift + +let conciseArrayMaxSize = 30 /** Class that enables using memory allocations as a means to uniquely identify objects. @@ -16,12 +19,31 @@ class Identity { var _forceAllocation: Int32 = 0 } +func hash(_x: Int) -> Int { + var x = _x + x = ((x >> 16) ^ x) &* 0x45d9f3b; + x = ((x >> 16) ^ x) &* 0x45d9f3b; + x = ((x >> 16) ^ x); + return x; +} + /** Unique identifier for object added to `Bag`. */ -public struct BagKey : Equatable { +public struct BagKey : Hashable { let uniqueIdentity: Identity? let key: Int + + public var hashValue: Int { + get { + if let uniqueIdentity = uniqueIdentity { + return hash(key) ^ (unsafeAddressOf(uniqueIdentity).hashValue) + } + else { + return hash(key) + } + } + } } /** @@ -52,8 +74,21 @@ public struct Bag : CustomStringConvertible { private var _uniqueIdentity: Identity? private var _nextKey: ScopeUniqueTokenType = 0 - - var _pairs = ContiguousArray() + + // data + + // first fill inline variables + private var _key0: BagKey? = nil + private var _value0: T? = nil + + private var _key1: BagKey? = nil + private var _value1: T? = nil + + // then fill "array dictionary" + private var _pairs = ContiguousArray() + + // last is sparse dictionary + private var _dictionary: [BagKey : T]? = nil /** Creates new empty `Bag`. @@ -69,7 +104,7 @@ public struct Bag : CustomStringConvertible { return "\(self.count) elements in Bag" } } - + /** Inserts `value` into bag. @@ -86,10 +121,36 @@ public struct Bag : CustomStringConvertible { if _nextKey == 0 { _uniqueIdentity = Identity() } - + let key = BagKey(uniqueIdentity: _uniqueIdentity, key: _nextKey) - - _pairs.append(key: key, value: element) + + if _key0 == nil { + _key0 = key + _value0 = element + return key + } + + if _key1 == nil { + _key1 = key + _value1 = element + return key + } + + if _dictionary != nil { + _dictionary![key] = element + return key + } + + if _pairs.count < conciseArrayMaxSize { + _pairs.append(key: key, value: element) + return key + } + + if _dictionary == nil { + _dictionary = [:] + } + + _dictionary![key] = element return key } @@ -98,14 +159,20 @@ public struct Bag : CustomStringConvertible { - returns: Number of elements in bag. */ public var count: Int { - return _pairs.count + return _pairs.count + (_value0 != nil ? 1 : 0) + (_value1 != nil ? 1 : 0) + (_dictionary?.count ?? 0) } /** Removes all elements from bag and clears capacity. */ public mutating func removeAll() { + _key0 = nil + _value0 = nil + _key1 = nil + _value1 = nil + _pairs.removeAll(keepCapacity: false) + _dictionary?.removeAll(keepCapacity: false) } /** @@ -115,6 +182,24 @@ public struct Bag : CustomStringConvertible { - returns: Element that bag contained, or nil in case element was already removed. */ public mutating func removeKey(key: BagKey) -> T? { + if _key0 == key { + _key0 = nil + let value = _value0! + _value0 = nil + return value + } + + if _key1 == key { + _key1 = nil + let value = _value1! + _value1 = nil + return value + } + + if let existingObject = _dictionary?.removeValueForKey(key) { + return existingObject + } + for i in 0 ..< _pairs.count { if _pairs[i].key == key { let value = _pairs[i].value @@ -122,7 +207,7 @@ public struct Bag : CustomStringConvertible { return value } } - + return nil } } @@ -136,11 +221,28 @@ extension Bag { - parameter action: Enumeration closure. */ public func forEach(@noescape action: (T) -> Void) { - let pairs = self._pairs - + let pairs = _pairs + let value0 = _value0 + let value1 = _value1 + let dictionary = _dictionary + + if let value0 = value0 { + action(value0) + } + + if let value1 = value1 { + action(value1) + } + for i in 0 ..< pairs.count { action(pairs[i].value) } + + if dictionary?.count ?? 0 > 0 { + for element in dictionary!.values { + action(element) + } + } } } @@ -152,20 +254,54 @@ extension Bag where T: ObserverType { */ public func on(event: Event) { let pairs = self._pairs + let value0 = _value0 + let value1 = _value1 + let dictionary = _dictionary + + if let value0 = value0 { + value0.on(event) + } + + if let value1 = value1 { + value1.on(event) + } for i in 0 ..< pairs.count { pairs[i].value.on(event) } + + if dictionary?.count ?? 0 > 0 { + for element in dictionary!.values { + element.on(event) + } + } } } /** Dispatches `dispose` to all disposables contained inside bag. */ -func disposeFromBag(bag: Bag) { +public func disposeAllIn(bag: Bag) { let pairs = bag._pairs + let value0 = bag._value0 + let value1 = bag._value1 + let dictionary = bag._dictionary + + if let value0 = value0 { + value0.dispose() + } + + if let value1 = value1 { + value1.dispose() + } for i in 0 ..< pairs.count { pairs[i].value.dispose() } + + if dictionary?.count ?? 0 > 0 { + for element in dictionary!.values { + element.dispose() + } + } } diff --git a/RxSwift/Disposables/CompositeDisposable.swift b/RxSwift/Disposables/CompositeDisposable.swift index b832f4c2..27212d8b 100644 --- a/RxSwift/Disposables/CompositeDisposable.swift +++ b/RxSwift/Disposables/CompositeDisposable.swift @@ -107,7 +107,7 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable { */ public func dispose() { if let disposables = _dispose() { - disposeFromBag(disposables) + disposeAllIn(disposables) } } diff --git a/RxTests/RxSwiftTests/Tests/BagTest.swift b/RxTests/RxSwiftTests/Tests/BagTest.swift index b3f9be65..9a5cb5aa 100644 --- a/RxTests/RxSwiftTests/Tests/BagTest.swift +++ b/RxTests/RxSwiftTests/Tests/BagTest.swift @@ -20,92 +20,202 @@ extension BagTest { typealias DoSomething = () -> Void typealias KeyType = Bag.KeyType - func numberOfActionsAfter(nInsertions: Int, deletionsFromStart: Int) { - var increment = 0 - - var bag = Bag() + func numberOfActionsAfter(nInsertions: Int, deletionsFromStart: Int, createNew: () -> T, bagAction: (RxMutableBox>) -> ()) { + let bag = RxMutableBox(Bag()) var keys = [KeyType]() for _ in 0 ..< nInsertions { - keys.append(bag.insert({ - increment++ - })) + keys.append(bag.value.insert(createNew())) } for i in 0 ..< deletionsFromStart { let key = keys[i] - bag.removeKey(key) + XCTAssertTrue(bag.value.removeKey(key) != nil) } - - bag.forEach { $0() } - - XCTAssertTrue(increment == nInsertions - deletionsFromStart) + + bagAction(bag) } func testBag_deletionsFromStart() { for i in 0 ..< 50 { for j in 0 ... i { - numberOfActionsAfter(i, deletionsFromStart: j) + var numberForEachActions = 0 + var numberObservers = 0 + var numberDisposables = 0 + + numberOfActionsAfter(i, + deletionsFromStart: j, + createNew: { () -> DoSomething in { () -> () in numberForEachActions++ } }, + bagAction: { (bag: RxMutableBox>) in bag.value.forEach { $0() }; XCTAssertTrue(bag.value.count == i - j) } + ) + numberOfActionsAfter(i, + deletionsFromStart: j, + createNew: { () -> AnyObserver in AnyObserver { _ in numberObservers++ } }, + bagAction: { (bag: RxMutableBox>>) in bag.value.on(.Next(1)); XCTAssertTrue(bag.value.count == i - j) } + ) + numberOfActionsAfter(i, + deletionsFromStart: j, + createNew: { () -> Disposable in AnonymousDisposable { numberDisposables++ } }, + bagAction: { (bag: RxMutableBox>) in disposeAllIn(bag.value); XCTAssertTrue(bag.value.count == i - j) } + ) + + XCTAssertTrue(numberForEachActions == i - j) + XCTAssertTrue(numberObservers == i - j) + XCTAssertTrue(numberDisposables == i - j) } } } - func numberOfActionsAfter(nInsertions: Int, deletionsFromEnd: Int) { - var increment = 0 - - var bag = Bag() + func numberOfActionsAfter(nInsertions: Int, deletionsFromEnd: Int, createNew: () -> T, bagAction: (RxMutableBox>) -> ()) { + let bag = RxMutableBox(Bag()) var keys = [KeyType]() for _ in 0 ..< nInsertions { - keys.append(bag.insert({ - increment++ - })) + keys.append(bag.value.insert(createNew())) } for i in 0 ..< deletionsFromEnd { let key = keys[keys.count - 1 - i] - bag.removeKey(key) + XCTAssertTrue(bag.value.removeKey(key) != nil) } - - bag.forEach { $0() } - - XCTAssertTrue(increment == nInsertions - deletionsFromEnd) + + bagAction(bag) } func testBag_deletionsFromEnd() { for i in 0 ..< 50 { for j in 0 ... i { - numberOfActionsAfter(i, deletionsFromEnd: j) + var numberForEachActions = 0 + var numberObservers = 0 + var numberDisposables = 0 + + numberOfActionsAfter(i, + deletionsFromStart: j, + createNew: { () -> DoSomething in { () -> () in numberForEachActions++ } }, + bagAction: { (bag: RxMutableBox>) in bag.value.forEach { $0() }; XCTAssertTrue(bag.value.count == i - j) } + ) + numberOfActionsAfter(i, + deletionsFromStart: j, + createNew: { () -> AnyObserver in AnyObserver { _ in numberObservers++ } }, + bagAction: { (bag: RxMutableBox>>) in bag.value.on(.Next(1)); XCTAssertTrue(bag.value.count == i - j) } + ) + numberOfActionsAfter(i, + deletionsFromStart: j, + createNew: { () -> Disposable in AnonymousDisposable { numberDisposables++ } }, + bagAction: { (bag: RxMutableBox>) in disposeAllIn(bag.value); XCTAssertTrue(bag.value.count == i - j) } + ) + + XCTAssertTrue(numberForEachActions == i - j) + XCTAssertTrue(numberObservers == i - j) + XCTAssertTrue(numberDisposables == i - j) } } } func testBag_immutableForeach() { - var increment = 0 - - var bag = Bag() - - var keys = [KeyType]() - - for _ in 0 ..< 10 { - keys.append(bag.insert({ - increment++ - })) - } - - for _ in 0 ..< 2 { - var j = 0 - bag.forEach { c in - j++ - if j == 5 { - bag.removeAll() - } - c() + for breakAt in 0 ..< 50 { + var increment1 = 0 + var increment2 = 0 + var increment3 = 0 + + let bag1 = RxMutableBox(Bag()) + let bag2 = RxMutableBox(Bag>()) + let bag3 = RxMutableBox(Bag()) + + for _ in 0 ..< 50 { + bag1.value.insert({ + if increment1 == breakAt { + bag1.value.removeAll() + } + increment1++ + }) + bag2.value.insert(AnyObserver { _ in + if increment2 == breakAt { + bag2.value.removeAll() + } + increment2++ + }) + bag3.value.insert(AnonymousDisposable { _ in + if increment3 == breakAt { + bag3.value.removeAll() + } + increment3++ + }) } + + for _ in 0 ..< 2 { + bag1.value.forEach { c in + c() + } + + bag2.value.on(.Next(1)) + + disposeAllIn(bag3.value) + } + + XCTAssertEqual(increment1, 50) + } + } + + func testBag_removeAll() { + var numberForEachActions = 0 + var numberObservers = 0 + var numberDisposables = 0 + + numberOfActionsAfter(100, + deletionsFromStart: 0, + createNew: { () -> DoSomething in { () -> () in numberForEachActions++ } }, + bagAction: { (bag: RxMutableBox>) in bag.value.removeAll(); bag.value.forEach { $0() } } + ) + numberOfActionsAfter(100, + deletionsFromStart: 0, + createNew: { () -> AnyObserver in AnyObserver { _ in numberObservers++ } }, + bagAction: { (bag: RxMutableBox>>) in bag.value.removeAll(); bag.value.on(.Next(1)); } + ) + numberOfActionsAfter(100, + deletionsFromStart: 0, + createNew: { () -> Disposable in AnonymousDisposable { numberDisposables++ } }, + bagAction: { (bag: RxMutableBox>) in bag.value.removeAll(); disposeAllIn(bag.value); } + ) + + XCTAssertTrue(numberForEachActions == 0) + XCTAssertTrue(numberObservers == 0) + XCTAssertTrue(numberDisposables == 0) + } + + func testBag_complexityTestFromFront() { + var bag = Bag() + + let limit = 100000 + + var increment = 0 + + var keys: [Bag.KeyType] = [] + for _ in 0..() + + let limit = 100000 + + var increment = 0 + + var keys: [Bag.KeyType] = [] + for _ in 0.. Date: Mon, 26 Oct 2015 12:19:36 +0100 Subject: [PATCH 138/210] Improves naming in `Bag.swift`. --- RxSwift/DataStructures/Bag.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RxSwift/DataStructures/Bag.swift b/RxSwift/DataStructures/Bag.swift index 2f023b6c..3bc66e69 100644 --- a/RxSwift/DataStructures/Bag.swift +++ b/RxSwift/DataStructures/Bag.swift @@ -9,7 +9,7 @@ import Foundation import Swift -let conciseArrayMaxSize = 30 +let arrayDictionaryMaxSize = 30 /** Class that enables using memory allocations as a means to uniquely identify objects. @@ -141,7 +141,7 @@ public struct Bag : CustomStringConvertible { return key } - if _pairs.count < conciseArrayMaxSize { + if _pairs.count < arrayDictionaryMaxSize { _pairs.append(key: key, value: element) return key } From aada6618891535ee4e95eb2f0b0620f55fd315a8 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sat, 31 Oct 2015 10:35:08 +0100 Subject: [PATCH 139/210] Low hanging fruit optimizations for `just`, `failWith`, `never`, `empty`, `flatMap`. --- .../Observables/Implementations/Empty.swift | 2 +- .../Implementations/FailWith.swift | 2 +- .../Observables/Implementations/FlatMap.swift | 43 ++++++++----------- .../Observables/Implementations/Just.swift | 2 +- .../Observables/Implementations/Never.swift | 2 +- RxTests/PerformanceTests/main.swift | 5 ++- 6 files changed, 25 insertions(+), 31 deletions(-) diff --git a/RxSwift/Observables/Implementations/Empty.swift b/RxSwift/Observables/Implementations/Empty.swift index 6b9fcde7..d2ebb188 100644 --- a/RxSwift/Observables/Implementations/Empty.swift +++ b/RxSwift/Observables/Implementations/Empty.swift @@ -9,7 +9,7 @@ import Foundation class Empty : Producer { - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + override func subscribe(observer: O) -> Disposable { observer.on(.Completed) return NopDisposable.instance } diff --git a/RxSwift/Observables/Implementations/FailWith.swift b/RxSwift/Observables/Implementations/FailWith.swift index 8350175b..3b771c5b 100644 --- a/RxSwift/Observables/Implementations/FailWith.swift +++ b/RxSwift/Observables/Implementations/FailWith.swift @@ -15,7 +15,7 @@ class FailWith : Producer { _error = error } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + override func subscribe(observer: O) -> Disposable { observer.on(.Error(_error)) return NopDisposable.instance } diff --git a/RxSwift/Observables/Implementations/FlatMap.swift b/RxSwift/Observables/Implementations/FlatMap.swift index c623ad44..11ebef1f 100644 --- a/RxSwift/Observables/Implementations/FlatMap.swift +++ b/RxSwift/Observables/Implementations/FlatMap.swift @@ -27,14 +27,14 @@ class FlatMapSinkIter) { switch event { case .Next(let value): - _parent._lock.performLocked { + _parent._lock.lock(); defer { _parent._lock.unlock() } // lock { _parent.observer?.on(.Next(value)) - } + // } case .Error(let error): - _parent._lock.performLocked { + _parent._lock.lock(); defer { _parent._lock.unlock() } // lock { _parent.observer?.on(.Error(error)) _parent.dispose() - } + // } case .Completed: _parent._group.removeDisposable(_disposeKey) // If this has returned true that means that `Completed` should be sent. @@ -43,10 +43,10 @@ class FlatMapSinkIter: Producer { setSink(sink) return sink.run() } - } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Just.swift b/RxSwift/Observables/Implementations/Just.swift index 1218bbb1..fecb935a 100644 --- a/RxSwift/Observables/Implementations/Just.swift +++ b/RxSwift/Observables/Implementations/Just.swift @@ -15,7 +15,7 @@ class Just : Producer { _element = element } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + override func subscribe(observer: O) -> Disposable { observer.on(.Next(_element)) observer.on(.Completed) return NopDisposable.instance diff --git a/RxSwift/Observables/Implementations/Never.swift b/RxSwift/Observables/Implementations/Never.swift index 3ea38ca0..1b20b277 100644 --- a/RxSwift/Observables/Implementations/Never.swift +++ b/RxSwift/Observables/Implementations/Never.swift @@ -9,7 +9,7 @@ import Foundation class Never : Producer { - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + override func subscribe(observer: O) -> Disposable { return NopDisposable.instance } } \ No newline at end of file diff --git a/RxTests/PerformanceTests/main.swift b/RxTests/PerformanceTests/main.swift index 9209d000..289abde2 100644 --- a/RxTests/PerformanceTests/main.swift +++ b/RxTests/PerformanceTests/main.swift @@ -21,14 +21,15 @@ func allocation() { compareTwoImplementations(benchmarkTime: true, first: { let publishSubject = PublishSubject() - let a = just(1) + //let a = just(1) //combineLatest(a, publishSubject - .shareReplay(1) + //.shareReplay(1) //.map { $0 } //.filter { _ in true }// ){ x, _ in x } //.map { $0 } + .flatMap { just($0) } .subscribeNext { _ in } From 28c97d94130a62a197cb905c2e5b867ec43a48cd Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sat, 31 Oct 2015 19:32:20 +0100 Subject: [PATCH 140/210] Enables testability. --- Rx.xcodeproj/project.pbxproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index e7949ad6..99c1d709 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -3096,6 +3096,7 @@ ENABLE_BITCODE = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_PREPROCESSOR_DEFINITIONS = "TRACE_RESOURCES=1"; From 22de82ba2bbc7c16387affa3998a083e5906933a Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sat, 31 Oct 2015 19:32:36 +0100 Subject: [PATCH 141/210] Removes disposable from `Sink`. --- RxSwift/Observables/Implementations/Sink.swift | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/RxSwift/Observables/Implementations/Sink.swift b/RxSwift/Observables/Implementations/Sink.swift index 7aeaee53..62851338 100644 --- a/RxSwift/Observables/Implementations/Sink.swift +++ b/RxSwift/Observables/Implementations/Sink.swift @@ -10,7 +10,7 @@ import Foundation class Sink : Disposable { private var _lock = SpinLock() - + // state private var _observer: O? private var _cancel: Disposable @@ -23,13 +23,6 @@ class Sink : Disposable { } } - var cancel: Disposable { - get { - _lock.lock(); defer { _lock.unlock() } - return _cancel - } - } - init(observer: O, cancel: Disposable) { #if TRACE_RESOURCES OSAtomicIncrement32(&resourceCount) @@ -38,7 +31,7 @@ class Sink : Disposable { _cancel = cancel } - func _disposeInternal() -> Disposable? { + private func _disposeInternal() -> Disposable? { _lock.lock(); defer { _lock.unlock() } if _disposed { From 13a87af009b1ef7841feb5bc3fb634cd94c0347b Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 1 Nov 2015 11:48:10 +0100 Subject: [PATCH 142/210] Moves disposables combo in `Producer.subscribe` to `Sink`. `SynchronizedOnType` overhauls. --- RxSwift/Disposables/AnonymousDisposable.swift | 2 + .../SingleAssignmentDisposable.swift | 3 + RxSwift/Observables/Implementations/Amb.swift | 12 +- .../Implementations/AnonymousObservable.swift | 12 +- .../Observables/Implementations/Buffer.swift | 56 +++-- .../Observables/Implementations/Catch.swift | 24 +- .../CombineLatest+CollectionType.swift | 95 ++++---- .../Implementations/CombineLatest+arity.swift | 154 ++++++------- .../Implementations/CombineLatest+arity.tt | 14 +- .../Implementations/CombineLatest.swift | 10 +- .../Observables/Implementations/Concat.swift | 14 +- .../Observables/Implementations/Debug.swift | 12 +- .../Implementations/Deferred.swift | 12 +- .../Implementations/DelaySubscription.swift | 17 +- .../DistinctUntilChanged.swift | 12 +- RxSwift/Observables/Implementations/Do.swift | 14 +- .../Implementations/ElementAt.swift | 12 +- .../Observables/Implementations/Filter.swift | 12 +- .../Observables/Implementations/FlatMap.swift | 28 +-- .../Implementations/Generate.swift | 12 +- RxSwift/Observables/Implementations/Map.swift | 26 +-- .../Observables/Implementations/Merge.swift | 213 ++++++++++-------- .../Implementations/Multicast.swift | 12 +- .../Implementations/ObserveOn.swift | 98 ++++---- .../ObserveOnSerialDispatchQueue.swift | 25 +- .../Implementations/Producer.swift | 21 +- .../Observables/Implementations/Range.swift | 12 +- .../Observables/Implementations/Reduce.swift | 12 +- .../Implementations/RefCount.swift | 26 ++- .../Observables/Implementations/Repeat.swift | 13 +- .../Observables/Implementations/Sample.swift | 105 +++++---- .../Observables/Implementations/Scan.swift | 12 +- .../Observables/Implementations/Sink.swift | 28 +-- .../Observables/Implementations/Skip.swift | 25 +- .../Implementations/SkipUntil.swift | 102 +++++---- .../Implementations/SkipWhile.swift | 22 +- .../Implementations/StartWith.swift | 2 +- .../Implementations/SubscribeOn.swift | 12 +- .../Observables/Implementations/Switch.swift | 118 +++++----- .../Observables/Implementations/Take.swift | 64 +++--- .../Implementations/TakeLast.swift | 12 +- .../Implementations/TakeUntil.swift | 82 +++---- .../Implementations/TakeWhile.swift | 30 +-- .../Implementations/Throttle.swift | 105 ++++----- .../Observables/Implementations/Timer.swift | 22 +- .../Observables/Implementations/ToArray.swift | 14 +- .../Observables/Implementations/Using.swift | 12 +- .../Implementations/WithLatestFrom.swift | 92 ++++---- .../Implementations/Zip+CollectionType.swift | 19 +- .../Implementations/Zip+arity.swift | 154 ++++++------- .../Observables/Implementations/Zip+arity.tt | 14 +- RxSwift/Observables/Implementations/Zip.swift | 48 ++-- RxSwift/Observers/TailRecursiveSink.swift | 4 +- .../Schedulers/CurrentThreadScheduler.swift | 6 +- RxTests/PerformanceTests/main.swift | 10 +- ...rvable+StandardSequenceOperatorsTest.swift | 6 +- RxTests/RxTest.swift | 2 +- .../xcschemes/RxTests-OSX.xcscheme | 2 +- .../xcschemes/RxTests-iOS.xcscheme | 2 +- .../xcschemes/RxTests-tvOS.xcscheme | 2 +- 60 files changed, 1090 insertions(+), 1023 deletions(-) diff --git a/RxSwift/Disposables/AnonymousDisposable.swift b/RxSwift/Disposables/AnonymousDisposable.swift index dffdd30d..b9aef47a 100644 --- a/RxSwift/Disposables/AnonymousDisposable.swift +++ b/RxSwift/Disposables/AnonymousDisposable.swift @@ -45,6 +45,8 @@ public final class AnonymousDisposable : DisposeBase, Cancelable { */ public func dispose() { if OSAtomicCompareAndSwap32(0, 1, &_disposed) { + assert(_disposed == 1) + if let action = _disposeAction { _disposeAction = nil action() diff --git a/RxSwift/Disposables/SingleAssignmentDisposable.swift b/RxSwift/Disposables/SingleAssignmentDisposable.swift index 8b2858bd..f74e22eb 100644 --- a/RxSwift/Disposables/SingleAssignmentDisposable.swift +++ b/RxSwift/Disposables/SingleAssignmentDisposable.swift @@ -72,6 +72,9 @@ public class SingleAssignmentDisposable : DisposeBase, Disposable, Cancelable { Disposes the underlying disposable. */ public func dispose() { + if _disposed { + return + } _dispose()?.dispose() } diff --git a/RxSwift/Observables/Implementations/Amb.swift b/RxSwift/Observables/Implementations/Amb.swift index 9c19d568..439cbd65 100644 --- a/RxSwift/Observables/Implementations/Amb.swift +++ b/RxSwift/Observables/Implementations/Amb.swift @@ -58,9 +58,9 @@ class AmbSink : Sink { // state private var _choice = AmbState.Neither - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func run() -> Disposable { @@ -114,9 +114,9 @@ class Amb: Producer { _right = right } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = AmbSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = AmbSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/AnonymousObservable.swift b/RxSwift/Observables/Implementations/AnonymousObservable.swift index 74379c2d..506f8b31 100644 --- a/RxSwift/Observables/Implementations/AnonymousObservable.swift +++ b/RxSwift/Observables/Implementations/AnonymousObservable.swift @@ -16,8 +16,8 @@ class AnonymousObservableSink : Sink, ObserverType { // state private var _isStopped: Int32 = 0 - override init(observer: O, cancel: Disposable) { - super.init(observer: observer, cancel: cancel) + override init(observer: O) { + super.init(observer: observer) } func on(event: Event) { @@ -49,9 +49,9 @@ public class AnonymousObservable : Producer { _subscribeHandler = subscribeHandler } - public override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = AnonymousObservableSink(observer: observer, cancel: cancel) - setSink(sink) - return sink.run(self) + public override func run(observer: O) -> Disposable { + let sink = AnonymousObservableSink(observer: observer) + sink.disposable = sink.run(self) + return sink } } diff --git a/RxSwift/Observables/Implementations/Buffer.swift b/RxSwift/Observables/Implementations/Buffer.swift index eb380461..1c932ed3 100644 --- a/RxSwift/Observables/Implementations/Buffer.swift +++ b/RxSwift/Observables/Implementations/Buffer.swift @@ -22,29 +22,33 @@ class BufferTimeCount : Producer<[Element]> { _scheduler = scheduler } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = BufferTimeCountSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = BufferTimeCountSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } -class BufferTimeCountSink : Sink, ObserverType { +class BufferTimeCountSink + : Sink + , LockOwnerType + , ObserverType + , SynchronizedOnType { typealias Parent = BufferTimeCount typealias E = Element private let _parent: Parent - private let _lock = NSRecursiveLock() + let _lock = NSRecursiveLock() // state private let _timerD = SerialDisposable() private var _buffer = [Element]() private var _windowID = 0 - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func run() -> Disposable { @@ -64,24 +68,26 @@ class BufferTimeCountSink) { - _lock.performLocked { - switch event { - case .Next(let element): - _buffer.append(element) - - if _buffer.count == _parent._count { - startNewWindowAndSendCurrentOne() - } - - case .Error(let error): - _buffer = [] - observer?.on(.Error(error)) - dispose() - case .Completed: - observer?.on(.Next(_buffer)) - observer?.on(.Completed) - dispose() + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { + switch event { + case .Next(let element): + _buffer.append(element) + + if _buffer.count == _parent._count { + startNewWindowAndSendCurrentOne() } + + case .Error(let error): + _buffer = [] + observer?.on(.Error(error)) + dispose() + case .Completed: + observer?.on(.Next(_buffer)) + observer?.on(.Completed) + dispose() } } diff --git a/RxSwift/Observables/Implementations/Catch.swift b/RxSwift/Observables/Implementations/Catch.swift index 3924948c..eaccfc8e 100644 --- a/RxSwift/Observables/Implementations/Catch.swift +++ b/RxSwift/Observables/Implementations/Catch.swift @@ -39,9 +39,9 @@ class CatchSink : Sink, ObserverType { private let _parent: Parent private let _subscription = SerialDisposable() - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func run() -> Disposable { @@ -86,10 +86,10 @@ class Catch : Producer { _handler = handler } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = CatchSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = CatchSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } @@ -101,8 +101,8 @@ class CatchSequenceSink) { @@ -148,9 +148,9 @@ class CatchSequence(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = CatchSequenceSink(observer: observer, cancel: cancel) - setSink(sink) - return sink.run(self.sources.generate()) + override func run(observer: O) -> Disposable { + let sink = CatchSequenceSink(observer: observer) + sink.disposable = sink.run(self.sources.generate()) + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift b/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift index e61c4390..e1e2bb82 100644 --- a/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift +++ b/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift @@ -8,48 +8,49 @@ import Foundation -class CombineLatestCollectionTypeSink : Sink { +class CombineLatestCollectionTypeSink + : Sink { typealias Parent = CombineLatestCollectionType typealias SourceElement = C.Generator.Element.E - let parent: Parent - - let lock = NSRecursiveLock() + let _parent: Parent + let _lock = NSRecursiveLock() + // state - var numberOfValues = 0 - var values: [SourceElement?] - var isDone: [Bool] - var numberOfDone = 0 - var subscriptions: [SingleAssignmentDisposable] + var _numberOfValues = 0 + var _values: [SourceElement?] + var _isDone: [Bool] + var _numberOfDone = 0 + var _subscriptions: [SingleAssignmentDisposable] - init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent - self.values = [SourceElement?](count: parent.count, repeatedValue: nil) - self.isDone = [Bool](count: parent.count, repeatedValue: false) - self.subscriptions = Array() - self.subscriptions.reserveCapacity(parent.count) + init(parent: Parent, observer: O) { + _parent = parent + _values = [SourceElement?](count: parent._count, repeatedValue: nil) + _isDone = [Bool](count: parent._count, repeatedValue: false) + _subscriptions = Array() + _subscriptions.reserveCapacity(parent._count) - for _ in 0 ..< parent.count { - self.subscriptions.append(SingleAssignmentDisposable()) + for _ in 0 ..< parent._count { + _subscriptions.append(SingleAssignmentDisposable()) } - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func on(event: Event, atIndex: Int) { - lock.performLocked { + _lock.lock(); defer { _lock.unlock() } // { switch event { case .Next(let element): - if values[atIndex] == nil { - numberOfValues++ + if _values[atIndex] == nil { + _numberOfValues++ } - values[atIndex] = element + _values[atIndex] = element - if numberOfValues < parent.count { - let numberOfOthersThatAreDone = self.numberOfDone - (isDone[atIndex] ? 1 : 0) - if numberOfOthersThatAreDone == self.parent.count - 1 { + if _numberOfValues < _parent._count { + let numberOfOthersThatAreDone = self._numberOfDone - (_isDone[atIndex] ? 1 : 0) + if numberOfOthersThatAreDone == self._parent._count - 1 { observer?.on(.Completed) dispose() } @@ -57,7 +58,7 @@ class CombineLatestCollectionTypeSink Disposable { var j = 0 - for i in parent.sources.startIndex ..< parent.sources.endIndex { + for i in _parent._sources.startIndex ..< _parent._sources.endIndex { let index = j - let source = self.parent.sources[i].asObservable() - self.subscriptions[j].disposable = source.subscribe(AnyObserver { event in + let source = _parent._sources[i].asObservable() + _subscriptions[j].disposable = source.subscribe(AnyObserver { event in self.on(event, atIndex: index) }) j++ } - return CompositeDisposable(disposables: self.subscriptions.map { $0 }) + return CompositeDisposable(disposables: _subscriptions.map { $0 }) } } class CombineLatestCollectionType : Producer { typealias ResultSelector = [C.Generator.Element.E] throws -> R - let sources: C - let resultSelector: ResultSelector - let count: Int - + let _sources: C + let _resultSelector: ResultSelector + let _count: Int + init(sources: C, resultSelector: ResultSelector) { - self.sources = sources - self.resultSelector = resultSelector - self.count = Int(self.sources.count.toIntMax()) + _sources = sources + _resultSelector = resultSelector + _count = Int(self._sources.count.toIntMax()) } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = CombineLatestCollectionTypeSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = CombineLatestCollectionTypeSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/CombineLatest+arity.swift b/RxSwift/Observables/Implementations/CombineLatest+arity.swift index da65b503..320e701e 100644 --- a/RxSwift/Observables/Implementations/CombineLatest+arity.swift +++ b/RxSwift/Observables/Implementations/CombineLatest+arity.swift @@ -39,17 +39,17 @@ class CombineLatestSink2_ : CombineLatestSink { var _latestElement1: E1! = nil var _latestElement2: E2! = nil - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(arity: 2, observer: observer, cancel: cancel) + super.init(arity: 2, observer: observer) } func run() -> Disposable { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() - let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) - let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) + let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) + let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) subscription1.disposable = _parent._source1.subscribe(observer1) subscription2.disposable = _parent._source2.subscribe(observer2) @@ -80,10 +80,10 @@ class CombineLatest2 : Producer { _resultSelector = resultSelector } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = CombineLatestSink2_(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = CombineLatestSink2_(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } @@ -117,9 +117,9 @@ class CombineLatestSink3_ : CombineLatestSink { var _latestElement2: E2! = nil var _latestElement3: E3! = nil - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(arity: 3, observer: observer, cancel: cancel) + super.init(arity: 3, observer: observer) } func run() -> Disposable { @@ -127,9 +127,9 @@ class CombineLatestSink3_ : CombineLatestSink { let subscription2 = SingleAssignmentDisposable() let subscription3 = SingleAssignmentDisposable() - let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) - let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) - let observer3 = CombineLatestObserver(lock: lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) + let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) + let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) + let observer3 = CombineLatestObserver(lock: _lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) subscription1.disposable = _parent._source1.subscribe(observer1) subscription2.disposable = _parent._source2.subscribe(observer2) @@ -164,10 +164,10 @@ class CombineLatest3 : Producer { _resultSelector = resultSelector } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = CombineLatestSink3_(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = CombineLatestSink3_(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } @@ -202,9 +202,9 @@ class CombineLatestSink4_ : CombineLatestSink Disposable { @@ -213,10 +213,10 @@ class CombineLatestSink4_ : CombineLatestSink Void in self._latestElement1 = e }, this: subscription1) - let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) - let observer3 = CombineLatestObserver(lock: lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) - let observer4 = CombineLatestObserver(lock: lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) + let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) + let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) + let observer3 = CombineLatestObserver(lock: _lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) + let observer4 = CombineLatestObserver(lock: _lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) subscription1.disposable = _parent._source1.subscribe(observer1) subscription2.disposable = _parent._source2.subscribe(observer2) @@ -255,10 +255,10 @@ class CombineLatest4 : Producer { _resultSelector = resultSelector } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = CombineLatestSink4_(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = CombineLatestSink4_(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } @@ -294,9 +294,9 @@ class CombineLatestSink5_ : CombineLatestSi var _latestElement4: E4! = nil var _latestElement5: E5! = nil - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(arity: 5, observer: observer, cancel: cancel) + super.init(arity: 5, observer: observer) } func run() -> Disposable { @@ -306,11 +306,11 @@ class CombineLatestSink5_ : CombineLatestSi let subscription4 = SingleAssignmentDisposable() let subscription5 = SingleAssignmentDisposable() - let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) - let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) - let observer3 = CombineLatestObserver(lock: lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) - let observer4 = CombineLatestObserver(lock: lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) - let observer5 = CombineLatestObserver(lock: lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5) + let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) + let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) + let observer3 = CombineLatestObserver(lock: _lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) + let observer4 = CombineLatestObserver(lock: _lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) + let observer5 = CombineLatestObserver(lock: _lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5) subscription1.disposable = _parent._source1.subscribe(observer1) subscription2.disposable = _parent._source2.subscribe(observer2) @@ -353,10 +353,10 @@ class CombineLatest5 : Producer { _resultSelector = resultSelector } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = CombineLatestSink5_(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = CombineLatestSink5_(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } @@ -393,9 +393,9 @@ class CombineLatestSink6_ : CombineLate var _latestElement5: E5! = nil var _latestElement6: E6! = nil - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(arity: 6, observer: observer, cancel: cancel) + super.init(arity: 6, observer: observer) } func run() -> Disposable { @@ -406,12 +406,12 @@ class CombineLatestSink6_ : CombineLate let subscription5 = SingleAssignmentDisposable() let subscription6 = SingleAssignmentDisposable() - let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) - let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) - let observer3 = CombineLatestObserver(lock: lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) - let observer4 = CombineLatestObserver(lock: lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) - let observer5 = CombineLatestObserver(lock: lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5) - let observer6 = CombineLatestObserver(lock: lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6) + let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) + let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) + let observer3 = CombineLatestObserver(lock: _lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) + let observer4 = CombineLatestObserver(lock: _lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) + let observer5 = CombineLatestObserver(lock: _lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5) + let observer6 = CombineLatestObserver(lock: _lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6) subscription1.disposable = _parent._source1.subscribe(observer1) subscription2.disposable = _parent._source2.subscribe(observer2) @@ -458,10 +458,10 @@ class CombineLatest6 : Producer { _resultSelector = resultSelector } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = CombineLatestSink6_(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = CombineLatestSink6_(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } @@ -499,9 +499,9 @@ class CombineLatestSink7_ : Combine var _latestElement6: E6! = nil var _latestElement7: E7! = nil - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(arity: 7, observer: observer, cancel: cancel) + super.init(arity: 7, observer: observer) } func run() -> Disposable { @@ -513,13 +513,13 @@ class CombineLatestSink7_ : Combine let subscription6 = SingleAssignmentDisposable() let subscription7 = SingleAssignmentDisposable() - let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) - let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) - let observer3 = CombineLatestObserver(lock: lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) - let observer4 = CombineLatestObserver(lock: lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) - let observer5 = CombineLatestObserver(lock: lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5) - let observer6 = CombineLatestObserver(lock: lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6) - let observer7 = CombineLatestObserver(lock: lock, parent: self, index: 6, setLatestValue: { (e: E7) -> Void in self._latestElement7 = e }, this: subscription7) + let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) + let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) + let observer3 = CombineLatestObserver(lock: _lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) + let observer4 = CombineLatestObserver(lock: _lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) + let observer5 = CombineLatestObserver(lock: _lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5) + let observer6 = CombineLatestObserver(lock: _lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6) + let observer7 = CombineLatestObserver(lock: _lock, parent: self, index: 6, setLatestValue: { (e: E7) -> Void in self._latestElement7 = e }, this: subscription7) subscription1.disposable = _parent._source1.subscribe(observer1) subscription2.disposable = _parent._source2.subscribe(observer2) @@ -570,10 +570,10 @@ class CombineLatest7 : Producer { _resultSelector = resultSelector } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = CombineLatestSink7_(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = CombineLatestSink7_(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } @@ -612,9 +612,9 @@ class CombineLatestSink8_ : Com var _latestElement7: E7! = nil var _latestElement8: E8! = nil - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(arity: 8, observer: observer, cancel: cancel) + super.init(arity: 8, observer: observer) } func run() -> Disposable { @@ -627,14 +627,14 @@ class CombineLatestSink8_ : Com let subscription7 = SingleAssignmentDisposable() let subscription8 = SingleAssignmentDisposable() - let observer1 = CombineLatestObserver(lock: lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) - let observer2 = CombineLatestObserver(lock: lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) - let observer3 = CombineLatestObserver(lock: lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) - let observer4 = CombineLatestObserver(lock: lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) - let observer5 = CombineLatestObserver(lock: lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5) - let observer6 = CombineLatestObserver(lock: lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6) - let observer7 = CombineLatestObserver(lock: lock, parent: self, index: 6, setLatestValue: { (e: E7) -> Void in self._latestElement7 = e }, this: subscription7) - let observer8 = CombineLatestObserver(lock: lock, parent: self, index: 7, setLatestValue: { (e: E8) -> Void in self._latestElement8 = e }, this: subscription8) + let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1) + let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2) + let observer3 = CombineLatestObserver(lock: _lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3) + let observer4 = CombineLatestObserver(lock: _lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4) + let observer5 = CombineLatestObserver(lock: _lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5) + let observer6 = CombineLatestObserver(lock: _lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6) + let observer7 = CombineLatestObserver(lock: _lock, parent: self, index: 6, setLatestValue: { (e: E7) -> Void in self._latestElement7 = e }, this: subscription7) + let observer8 = CombineLatestObserver(lock: _lock, parent: self, index: 7, setLatestValue: { (e: E8) -> Void in self._latestElement8 = e }, this: subscription8) subscription1.disposable = _parent._source1.subscribe(observer1) subscription2.disposable = _parent._source2.subscribe(observer2) @@ -689,10 +689,10 @@ class CombineLatest8 : Producer { _resultSelector = resultSelector } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = CombineLatestSink8_(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = CombineLatestSink8_(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } diff --git a/RxSwift/Observables/Implementations/CombineLatest+arity.tt b/RxSwift/Observables/Implementations/CombineLatest+arity.tt index 927f7e0d..89217773 100644 --- a/RxSwift/Observables/Implementations/CombineLatest+arity.tt +++ b/RxSwift/Observables/Implementations/CombineLatest+arity.tt @@ -38,9 +38,9 @@ class CombineLatestSink<%= i %>_<<%= (Array(1...i).map { "E\($0)" }).joinWithSep " var _latestElement\($0): E\($0)! = nil" }).joinWithSeparator("\n") %> - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(arity: <%= i %>, observer: observer, cancel: cancel) + super.init(arity: <%= i %>, observer: observer) } func run() -> Disposable { @@ -49,7 +49,7 @@ class CombineLatestSink<%= i %>_<<%= (Array(1...i).map { "E\($0)" }).joinWithSep }).joinWithSeparator("\n") %> <%= (Array(1...i).map { -" let observer\($0) = CombineLatestObserver(lock: lock, parent: self, index: \($0 - 1), setLatestValue: { (e: E\($0)) -> Void in self._latestElement\($0) = e }, this: subscription\($0))" +" let observer\($0) = CombineLatestObserver(lock: _lock, parent: self, index: \($0 - 1), setLatestValue: { (e: E\($0)) -> Void in self._latestElement\($0) = e }, this: subscription\($0))" }).joinWithSeparator("\n") %> <%= (Array(1...i).map { @@ -83,10 +83,10 @@ class CombineLatest<%= i %><<%= (Array(1...i).map { "E\($0)" }).joinWithSeparato _resultSelector = resultSelector } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = CombineLatestSink<%= i %>_(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = CombineLatestSink<%= i %>_(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } diff --git a/RxSwift/Observables/Implementations/CombineLatest.swift b/RxSwift/Observables/Implementations/CombineLatest.swift index 300cf2db..5807ebdc 100644 --- a/RxSwift/Observables/Implementations/CombineLatest.swift +++ b/RxSwift/Observables/Implementations/CombineLatest.swift @@ -14,10 +14,12 @@ protocol CombineLatestProtocol : class { func done(index: Int) } -class CombineLatestSink : Sink, CombineLatestProtocol { +class CombineLatestSink + : Sink + , CombineLatestProtocol { typealias Element = O.E - let lock = NSRecursiveLock() + let _lock = NSRecursiveLock() private let _arity: Int private var _numberOfValues = 0 @@ -25,12 +27,12 @@ class CombineLatestSink : Sink, CombineLatestProtocol { private var _hasValue: [Bool] private var _isDone: [Bool] - init(arity: Int, observer: O, cancel: Disposable) { + init(arity: Int, observer: O) { _arity = arity _hasValue = [Bool](count: arity, repeatedValue: false) _isDone = [Bool](count: arity, repeatedValue: false) - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func getResult() throws -> Element { diff --git a/RxSwift/Observables/Implementations/Concat.swift b/RxSwift/Observables/Implementations/Concat.swift index 69136938..c9aca4e8 100644 --- a/RxSwift/Observables/Implementations/Concat.swift +++ b/RxSwift/Observables/Implementations/Concat.swift @@ -12,8 +12,8 @@ import Foundation class ConcatSink : TailRecursiveSink { typealias Element = O.E - override init(observer: O, cancel: Disposable) { - super.init(observer: observer, cancel: cancel) + override init(observer: O) { + super.init(observer: observer) } override func on(event: Event){ @@ -47,11 +47,9 @@ class Concat - (observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = ConcatSink(observer: observer, cancel: cancel) - setSink(sink) - - return sink.run(_sources.generate()) + override func run(observer: O) -> Disposable { + let sink = ConcatSink(observer: observer) + sink.disposable = sink.run(_sources.generate()) + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Debug.swift b/RxSwift/Observables/Implementations/Debug.swift index f527c2f5..090849cd 100644 --- a/RxSwift/Observables/Implementations/Debug.swift +++ b/RxSwift/Observables/Implementations/Debug.swift @@ -14,9 +14,9 @@ class Debug_ : Sink, ObserverType { private let _parent: Parent - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func on(event: Event) { @@ -45,10 +45,10 @@ class Debug : Producer { _source = source } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + override func run(observer: O) -> Disposable { print("[\(_identifier)] subscribed") - let sink = Debug_(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return _source.subscribe(sink) + let sink = Debug_(parent: self, observer: observer) + sink.disposable = _source.subscribe(sink) + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Deferred.swift b/RxSwift/Observables/Implementations/Deferred.swift index affbd3d2..92b71788 100644 --- a/RxSwift/Observables/Implementations/Deferred.swift +++ b/RxSwift/Observables/Implementations/Deferred.swift @@ -14,9 +14,9 @@ class DeferredSink : Sink, ObserverType { private let _parent: Parent - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func run() -> Disposable { @@ -54,10 +54,10 @@ class Deferred : Producer { _observableFactory = observableFactory } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = DeferredSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = DeferredSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } func eval() throws -> Observable { diff --git a/RxSwift/Observables/Implementations/DelaySubscription.swift b/RxSwift/Observables/Implementations/DelaySubscription.swift index 0669bc10..147fd032 100644 --- a/RxSwift/Observables/Implementations/DelaySubscription.swift +++ b/RxSwift/Observables/Implementations/DelaySubscription.swift @@ -8,15 +8,17 @@ import Foundation -class DelaySubscriptionSink : Sink, ObserverType { +class DelaySubscriptionSink + : Sink + , ObserverType { typealias Parent = DelaySubscription typealias E = O.E private let _parent: Parent - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func on(event: Event) { @@ -41,11 +43,12 @@ class DelaySubscription: Producer { _scheduler = scheduler } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = DelaySubscriptionSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return _scheduler.scheduleRelative((), dueTime: _dueTime) { _ in + override func run(observer: O) -> Disposable { + let sink = DelaySubscriptionSink(parent: self, observer: observer) + sink.disposable = _scheduler.scheduleRelative((), dueTime: _dueTime) { _ in return self._source.subscribe(sink) } + + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/DistinctUntilChanged.swift b/RxSwift/Observables/Implementations/DistinctUntilChanged.swift index 56521765..d0ab70cf 100644 --- a/RxSwift/Observables/Implementations/DistinctUntilChanged.swift +++ b/RxSwift/Observables/Implementations/DistinctUntilChanged.swift @@ -14,9 +14,9 @@ class DistinctUntilChangedSink: Sink, ObserverType { private let _parent: DistinctUntilChanged private var _currentKey: Key? = nil - init(parent: DistinctUntilChanged, observer: O, cancel: Disposable) { + init(parent: DistinctUntilChanged, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func on(event: Event) { @@ -64,9 +64,9 @@ class DistinctUntilChanged: Producer { _comparer = comparer } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = DistinctUntilChangedSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return _source.subscribe(sink) + override func run(observer: O) -> Disposable { + let sink = DistinctUntilChangedSink(parent: self, observer: observer) + sink.disposable = _source.subscribe(sink) + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Do.swift b/RxSwift/Observables/Implementations/Do.swift index 1af070c6..5f163cf6 100644 --- a/RxSwift/Observables/Implementations/Do.swift +++ b/RxSwift/Observables/Implementations/Do.swift @@ -14,9 +14,9 @@ class DoSink : Sink, ObserverType { private let _parent: Parent - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func on(event: Event) { @@ -45,11 +45,9 @@ class Do : Producer { _eventHandler = eventHandler } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = DoSink(parent: self, observer: observer, cancel: cancel) - - setSink(sink) - - return _source.subscribe(sink) + override func run(observer: O) -> Disposable { + let sink = DoSink(parent: self, observer: observer) + sink.disposable = _source.subscribe(sink) + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/ElementAt.swift b/RxSwift/Observables/Implementations/ElementAt.swift index 29f635e2..19401b2c 100644 --- a/RxSwift/Observables/Implementations/ElementAt.swift +++ b/RxSwift/Observables/Implementations/ElementAt.swift @@ -15,11 +15,11 @@ class ElementAtSink : Sink< let _parent: Parent var _i: Int - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent _i = parent._index - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func on(event: Event) { @@ -71,9 +71,9 @@ class ElementAt : Producer { self._throwOnEmpty = throwOnEmpty } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = ElementAtSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return _source.subscribeSafe(sink) + override func run(observer: O) -> Disposable { + let sink = ElementAtSink(parent: self, observer: observer) + sink.disposable = _source.subscribeSafe(sink) + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Filter.swift b/RxSwift/Observables/Implementations/Filter.swift index ad47c6fb..8efda19b 100644 --- a/RxSwift/Observables/Implementations/Filter.swift +++ b/RxSwift/Observables/Implementations/Filter.swift @@ -15,9 +15,9 @@ class FilterSink: Sink, ObserverType { private let _parent: Parent - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func on(event: Event) { @@ -51,9 +51,9 @@ class Filter : Producer { _predicate = predicate } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = FilterSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return _source.subscribe(sink) + override func run(observer: O) -> Disposable { + let sink = FilterSink(parent: self, observer: observer) + sink.disposable = _source.subscribe(sink) + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/FlatMap.swift b/RxSwift/Observables/Implementations/FlatMap.swift index 11ebef1f..f9383df3 100644 --- a/RxSwift/Observables/Implementations/FlatMap.swift +++ b/RxSwift/Observables/Implementations/FlatMap.swift @@ -67,9 +67,9 @@ class FlatMapSink S { @@ -128,8 +128,8 @@ class FlatMapSink : FlatMapSink { - override init(parent: Parent, observer: O, cancel: Disposable) { - super.init(parent: parent, observer: observer, cancel: cancel) + override init(parent: Parent, observer: O) { + super.init(parent: parent, observer: observer) } override func performMap(element: SourceType) throws -> S { @@ -140,8 +140,8 @@ class FlatMapSink1 : FlatMapSink { private var _index = 0 - override init(parent: Parent, observer: O, cancel: Disposable) { - super.init(parent: parent, observer: observer, cancel: cancel) + override init(parent: Parent, observer: O) { + super.init(parent: parent, observer: observer) } override func performMap(element: SourceType) throws -> S { @@ -170,16 +170,18 @@ class FlatMap: Producer { _selector1 = nil } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + override func run(observer: O) -> Disposable { + let sink: FlatMapSink if let _ = _selector1 { - let sink = FlatMapSink1(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + sink = FlatMapSink1(parent: self, observer: observer) } else { - let sink = FlatMapSink2(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + sink = FlatMapSink2(parent: self, observer: observer) } + + let subscription = sink.run() + sink.disposable = subscription + + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Generate.swift b/RxSwift/Observables/Implementations/Generate.swift index 4e6a51c1..1c2f6ba2 100644 --- a/RxSwift/Observables/Implementations/Generate.swift +++ b/RxSwift/Observables/Implementations/Generate.swift @@ -15,10 +15,10 @@ class GenerateSink : Sink { private var _state: S - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent _state = parent._initialState - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func run() -> Disposable { @@ -63,9 +63,9 @@ class Generate : Producer { super.init() } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = GenerateSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = GenerateSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Map.swift b/RxSwift/Observables/Implementations/Map.swift index 5781ed60..e7817cbe 100644 --- a/RxSwift/Observables/Implementations/Map.swift +++ b/RxSwift/Observables/Implementations/Map.swift @@ -15,9 +15,9 @@ class MapSink : Sink, ObserverType { private let _parent: Parent - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func performMap(element: SourceType) throws -> ResultType { @@ -50,8 +50,8 @@ class MapSink : Sink, ObserverType { class MapSink1 : MapSink { typealias ResultType = O.E - override init(parent: Map, observer: O, cancel: Disposable) { - super.init(parent: parent, observer: observer, cancel: cancel) + override init(parent: Map, observer: O) { + super.init(parent: parent, observer: observer) } override func performMap(element: SourceType) throws -> ResultType { @@ -64,8 +64,8 @@ class MapSink2 : MapSink { private var _index = 0 - override init(parent: Map, observer: O, cancel: Disposable) { - super.init(parent: parent, observer: observer, cancel: cancel) + override init(parent: Map, observer: O) { + super.init(parent: parent, observer: observer) } override func performMap(element: SourceType) throws -> ResultType { return try _parent._selector2!(element, try incrementChecked(&_index)) @@ -93,16 +93,16 @@ class Map: Producer { _selector1 = nil } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + override func run(observer: O) -> Disposable { if let _ = _selector1 { - let sink = MapSink1(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return _source.subscribe(sink) + let sink = MapSink1(parent: self, observer: observer) + sink.disposable = _source.subscribe(sink) + return sink } else { - let sink = MapSink2(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return _source.subscribe(sink) + let sink = MapSink2(parent: self, observer: observer) + sink.disposable = _source.subscribe(sink) + return sink } } diff --git a/RxSwift/Observables/Implementations/Merge.swift b/RxSwift/Observables/Implementations/Merge.swift index 65761898..a0b996ba 100644 --- a/RxSwift/Observables/Implementations/Merge.swift +++ b/RxSwift/Observables/Implementations/Merge.swift @@ -10,46 +10,59 @@ import Foundation // sequential -class MergeSinkIter : ObserverType { +class MergeSinkIter + : ObserverType + , LockOwnerType + , SynchronizedOnType { typealias E = O.E typealias DisposeKey = Bag.KeyType typealias Parent = MergeSink private let _parent: Parent private let _disposeKey: DisposeKey - + + var _lock: NSRecursiveLock { + return _parent._lock + } + init(parent: Parent, disposeKey: DisposeKey) { _parent = parent _disposeKey = disposeKey } func on(event: Event) { - _parent._lock.performLocked { - switch event { - case .Next: - _parent.observer?.on(event) - case .Error: - _parent.observer?.on(event) + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { + switch event { + case .Next: + _parent.observer?.on(event) + case .Error: + _parent.observer?.on(event) + _parent.dispose() + case .Completed: + _parent._group.removeDisposable(_disposeKey) + + if _parent._stopped && _parent._group.count == 1 { + _parent.observer?.on(.Completed) _parent.dispose() - case .Completed: - _parent._group.removeDisposable(_disposeKey) - - if _parent._stopped && _parent._group.count == 1 { - _parent.observer?.on(.Completed) - _parent.dispose() - } } } } } -class MergeSink : Sink, ObserverType { +class MergeSink + : Sink + , ObserverType + , LockOwnerType + , SynchronizedOnType { typealias E = S typealias Parent = Merge private let _parent: Parent - private let _lock = NSRecursiveLock() + let _lock = NSRecursiveLock() // state private var _stopped = false @@ -57,10 +70,10 @@ class MergeSink private let _group = CompositeDisposable() private let _sourceSubscription = SingleAssignmentDisposable() - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func run() -> Disposable { @@ -73,8 +86,7 @@ class MergeSink } func on(event: Event) { - switch event { - case .Next(let value): + if case .Next(let value) = event { let innerSubscription = SingleAssignmentDisposable() let maybeKey = _group.addDisposable(innerSubscription) @@ -83,22 +95,29 @@ class MergeSink let disposable = value.asObservable().subscribe(observer) innerSubscription.disposable = disposable } + + return + } + + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { + switch event { + case .Next: + rxFatalError("Next should have been handled") case .Error(let error): - _lock.performLocked { - observer?.on(.Error(error)) + observer?.on(.Error(error)) + dispose() + case .Completed: + _stopped = true + + if _group.count == 1 { + observer?.on(.Completed) dispose() } - case .Completed: - _lock.performLocked { - _stopped = true - - if _group.count == 1 { - observer?.on(.Completed) - dispose() - } - else { - _sourceSubscription.dispose() - } + else { + _sourceSubscription.dispose() } } } @@ -106,13 +125,20 @@ class MergeSink // concurrent -class MergeConcurrentSinkIter : ObserverType { +class MergeConcurrentSinkIter + : ObserverType + , LockOwnerType + , SynchronizedOnType { typealias E = O.E typealias DisposeKey = Bag.KeyType typealias Parent = MergeConcurrentSink private let _parent: Parent private let _disposeKey: DisposeKey + + var _lock: NSRecursiveLock { + return _parent._lock + } init(parent: Parent, disposeKey: DisposeKey) { _parent = parent @@ -120,42 +146,48 @@ class MergeConcurrentSinkIter) { - _parent._lock.performLocked { - switch event { - case .Next: - _parent.observer?.on(event) - case .Error: - _parent.observer?.on(event) - _parent.dispose() - case .Completed: - _parent._group.removeDisposable(_disposeKey) - let queue = _parent._queue - if queue.value.count > 0 { - let s = queue.value.dequeue() - _parent.subscribe(s, group: _parent._group) - } - else { - _parent._activeCount = _parent._activeCount - 1 - - if _parent._stopped && _parent._activeCount == 0 { - _parent.observer?.on(.Completed) - _parent.dispose() - } + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { + switch event { + case .Next: + _parent.observer?.on(event) + case .Error: + _parent.observer?.on(event) + _parent.dispose() + case .Completed: + _parent._group.removeDisposable(_disposeKey) + let queue = _parent._queue + if queue.value.count > 0 { + let s = queue.value.dequeue() + _parent.subscribe(s, group: _parent._group) + } + else { + _parent._activeCount = _parent._activeCount - 1 + + if _parent._stopped && _parent._activeCount == 0 { + _parent.observer?.on(.Completed) + _parent.dispose() } } } } } -class MergeConcurrentSink : Sink, ObserverType { +class MergeConcurrentSink + : Sink + , ObserverType + , LockOwnerType + , SynchronizedOnType { typealias E = S typealias Parent = Merge typealias QueueType = Queue private let _parent: Parent - private let _lock = NSRecursiveLock() - + let _lock = NSRecursiveLock() + // state private var _stopped = false private var _activeCount = 0 @@ -164,11 +196,11 @@ class MergeConcurrentSink Disposable { @@ -193,39 +225,38 @@ class MergeConcurrentSink) { + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { switch event { case .Next(let value): - let subscribe = _lock.calculateLocked { () -> Bool in - if _activeCount < _parent._maxConcurrent { - _activeCount += 1 - return true - } - else { - _queue.value.enqueue(value) - return false - } + let subscribe: Bool + if _activeCount < _parent._maxConcurrent { + _activeCount += 1 + subscribe = true } - + else { + _queue.value.enqueue(value) + subscribe = false + } + if subscribe { self.subscribe(value, group: _group) } case .Error(let error): - _lock.performLocked { - observer?.on(.Error(error)) + observer?.on(.Error(error)) + dispose() + case .Completed: + if _activeCount == 0 { + observer?.on(.Completed) dispose() } - case .Completed: - _lock.performLocked { - if _activeCount == 0 { - observer?.on(.Completed) - dispose() - } - else { - _sourceSubscription.dispose() - } - - _stopped = true + else { + _sourceSubscription.dispose() } + + _stopped = true } } } @@ -239,16 +270,16 @@ class Merge : Producer { _maxConcurrent = maxConcurrent } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + override func run(observer: O) -> Disposable { if _maxConcurrent > 0 { - let sink = MergeConcurrentSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + let sink = MergeConcurrentSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } else { - let sink = MergeSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + let sink = MergeSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Multicast.swift b/RxSwift/Observables/Implementations/Multicast.swift index f63d14b5..770d5e8d 100644 --- a/RxSwift/Observables/Implementations/Multicast.swift +++ b/RxSwift/Observables/Implementations/Multicast.swift @@ -15,9 +15,9 @@ class MulticastSink: Sink, ObserverType { private let _parent: MutlicastType - init(parent: MutlicastType, observer: O, cancel: Disposable) { + init(parent: MutlicastType, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func run() -> Disposable { @@ -63,9 +63,9 @@ class Multicast: Producer { _selector = selector } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = MulticastSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = MulticastSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/ObserveOn.swift b/RxSwift/Observables/Implementations/ObserveOn.swift index ec2d12f1..5eb1298d 100644 --- a/RxSwift/Observables/Implementations/ObserveOn.swift +++ b/RxSwift/Observables/Implementations/ObserveOn.swift @@ -21,10 +21,10 @@ class ObserveOn : Producer { #endif } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = ObserveOnSink(scheduler: scheduler, observer: observer, cancel: cancel) - setSink(sink) - return source.subscribe(sink) + override func run(observer: O) -> Disposable { + let sink = ObserveOnSink(scheduler: scheduler, observer: observer) + sink._subscription.disposable = source.subscribe(sink) + return sink } #if TRACE_RESOURCES @@ -44,31 +44,30 @@ enum ObserveOnState : Int32 { class ObserveOnSink : ObserverBase { typealias E = O.E - var cancel: Disposable - - var lock = SpinLock() - - let scheduler: ImmediateSchedulerType - var observer: O? - - var state = ObserveOnState.Stopped - - var queue = Queue>(capacity: 10) - let scheduleDisposable = SerialDisposable() - - init(scheduler: ImmediateSchedulerType, observer: O, cancel: Disposable) { - self.cancel = cancel - self.scheduler = scheduler - self.observer = observer + let _scheduler: ImmediateSchedulerType + + var _lock = SpinLock() + + // state + var _state = ObserveOnState.Stopped + var _observer: O? + var _queue = Queue>(capacity: 10) + + let _scheduleDisposable = SerialDisposable() + let _subscription = SingleAssignmentDisposable() + + init(scheduler: ImmediateSchedulerType, observer: O) { + _scheduler = scheduler + _observer = observer } override func onCore(event: Event) { - let shouldStart = lock.calculateLocked { () -> Bool in - self.queue.enqueue(event) + let shouldStart = _lock.calculateLocked { () -> Bool in + self._queue.enqueue(event) - switch self.state { + switch self._state { case .Stopped: - self.state = .Running + self._state = .Running return true case .Running: return false @@ -76,18 +75,18 @@ class ObserveOnSink : ObserverBase { } if shouldStart { - scheduleDisposable.disposable = self.scheduler.scheduleRecursive((), action: self.run) + _scheduleDisposable.disposable = self._scheduler.scheduleRecursive((), action: self.run) } } func run(state: Void, recurse: Void -> Void) { - let (nextEvent, observer) = self.lock.calculateLocked { () -> (Event?, O?) in - if self.queue.count > 0 { - return (self.queue.dequeue(), self.observer) + let (nextEvent, observer) = self._lock.calculateLocked { () -> (Event?, O?) in + if self._queue.count > 0 { + return (self._queue.dequeue(), self._observer) } else { - self.state = .Stopped - return (nil, self.observer) + self._state = .Stopped + return (nil, self._observer) } } @@ -101,32 +100,33 @@ class ObserveOnSink : ObserverBase { return } - let shouldContinue = self.lock.calculateLocked { () -> Bool in - if self.queue.count > 0 { - return true - } - else { - self.state = .Stopped - return false - } - } + let shouldContinue = _shouldContinue_synchronized() if shouldContinue { recurse() } } + + func _shouldContinue_synchronized() -> Bool { + _lock.lock(); defer { _lock.unlock() } // { + if self._queue.count > 0 { + return true + } + else { + self._state = .Stopped + return false + } + // } + } override func dispose() { super.dispose() - - let toDispose = lock.calculateLocked { () -> Disposable in - let originalCancel = self.cancel - self.cancel = NopDisposable.instance - self.scheduleDisposable.dispose() - self.observer = nil - return originalCancel - } - - toDispose.dispose() + + _subscription.dispose() + _scheduleDisposable.dispose() + + _lock.lock(); defer { _lock.unlock() } // { + _observer = nil + // } } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/ObserveOnSerialDispatchQueue.swift b/RxSwift/Observables/Implementations/ObserveOnSerialDispatchQueue.swift index 3fa66efd..7d3f5675 100644 --- a/RxSwift/Observables/Implementations/ObserveOnSerialDispatchQueue.swift +++ b/RxSwift/Observables/Implementations/ObserveOnSerialDispatchQueue.swift @@ -22,12 +22,9 @@ class ObserveOnSerialDispatchQueueSink : ObserverBase { let scheduler: SerialDispatchQueueScheduler let observer: O - var disposeLock = SpinLock() + let subscription = SingleAssignmentDisposable() - var cancel: Disposable - - init(scheduler: SerialDispatchQueueScheduler, observer: O, cancel: Disposable) { - self.cancel = cancel + init(scheduler: SerialDispatchQueueScheduler, observer: O) { self.scheduler = scheduler self.observer = observer super.init() @@ -47,14 +44,8 @@ class ObserveOnSerialDispatchQueueSink : ObserverBase { override func dispose() { super.dispose() - - let toDispose = disposeLock.calculateLocked { () -> Disposable in - let originalCancel = self.cancel - self.cancel = NopDisposable.instance - return originalCancel - } - - toDispose.dispose() + + subscription.dispose() } } @@ -72,10 +63,10 @@ class ObserveOnSerialDispatchQueue : Producer { #endif } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = ObserveOnSerialDispatchQueueSink(scheduler: scheduler, observer: observer, cancel: cancel) - setSink(sink) - return source.subscribe(sink) + override func run(observer: O) -> Disposable { + let sink = ObserveOnSerialDispatchQueueSink(scheduler: scheduler, observer: observer) + sink.subscription.disposable = source.subscribe(sink) + return sink } #if TRACE_RESOURCES diff --git a/RxSwift/Observables/Implementations/Producer.swift b/RxSwift/Observables/Implementations/Producer.swift index 63c38c76..8b8098a8 100644 --- a/RxSwift/Observables/Implementations/Producer.swift +++ b/RxSwift/Observables/Implementations/Producer.swift @@ -14,30 +14,17 @@ class Producer : Observable { } override func subscribe(observer: O) -> Disposable { - let sink = SingleAssignmentDisposable() - let subscription = SingleAssignmentDisposable() - - let d = BinaryDisposable(sink, subscription) - - let setSink: (Disposable) -> Void = { d in sink.disposable = d } - if !CurrentThreadScheduler.isScheduleRequired { - let disposable = run(observer, cancel: subscription, setSink: setSink) - - subscription.disposable = disposable + return run(observer) } else { - CurrentThreadScheduler.instance.schedule(sink) { sink in - let disposable = self.run(observer, cancel: subscription, setSink: setSink) - subscription.disposable = disposable - return NopDisposable.instance + return CurrentThreadScheduler.instance.schedule(()) { _ in + return self.run(observer) } } - - return d } - func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + func run(observer: O) -> Disposable { abstractMethod() } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Range.swift b/RxSwift/Observables/Implementations/Range.swift index ded21856..8093416a 100644 --- a/RxSwift/Observables/Implementations/Range.swift +++ b/RxSwift/Observables/Implementations/Range.swift @@ -27,10 +27,10 @@ class RangeProducer<_CompilerWorkaround> : Producer { _scheduler = scheduler } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = RangeSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = RangeSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } @@ -39,9 +39,9 @@ class RangeSink<_CompilerWorkaround, O: ObserverType where O.E == Int> : Sink private let _parent: Parent - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func run() -> Disposable { diff --git a/RxSwift/Observables/Implementations/Reduce.swift b/RxSwift/Observables/Implementations/Reduce.swift index e8653d85..3276b326 100644 --- a/RxSwift/Observables/Implementations/Reduce.swift +++ b/RxSwift/Observables/Implementations/Reduce.swift @@ -15,11 +15,11 @@ class ReduceSink : Sink, Observe private let _parent: Parent private var _accumulation: AccumulateType - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent _accumulation = parent._seed - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func on(event: Event) { @@ -66,9 +66,9 @@ class Reduce : Producer { _mapResult = mapResult } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = ReduceSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return _source.subscribe(sink) + override func run(observer: O) -> Disposable { + let sink = ReduceSink(parent: self, observer: observer) + sink.disposable = _source.subscribe(sink) + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/RefCount.swift b/RxSwift/Observables/Implementations/RefCount.swift index eacd4ac0..4216aefa 100644 --- a/RxSwift/Observables/Implementations/RefCount.swift +++ b/RxSwift/Observables/Implementations/RefCount.swift @@ -8,21 +8,23 @@ import Foundation -class RefCountSink : Sink, ObserverType { +class RefCountSink + : Sink + , ObserverType { typealias Element = O.E typealias Parent = RefCount private let _parent: Parent - - init(parent: Parent, observer: O, cancel: Disposable) { + + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func run() -> Disposable { let subscription = _parent._source.subscribeSafe(self) - _parent._lock.performLocked { + _parent._lock.lock(); defer { _parent._lock.unlock() } // { if _parent._count == 0 { _parent._count = 1 _parent._connectableSubscription = _parent._source.connect() @@ -30,11 +32,11 @@ class RefCountSink: Producer { _source = source } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = RefCountSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = RefCountSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Repeat.swift b/RxSwift/Observables/Implementations/Repeat.swift index 33621d68..8655d929 100644 --- a/RxSwift/Observables/Implementations/Repeat.swift +++ b/RxSwift/Observables/Implementations/Repeat.swift @@ -17,10 +17,11 @@ class RepeatElement : Producer { _scheduler = scheduler } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = RepeatElementSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = RepeatElementSink(parent: self, observer: observer) + sink.disposable = sink.run() + + return sink } } @@ -29,9 +30,9 @@ class RepeatElementSink : Sink { private let _parent: Parent - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func run() -> Disposable { diff --git a/RxSwift/Observables/Implementations/Sample.swift b/RxSwift/Observables/Implementations/Sample.swift index 459a3484..703b86e1 100644 --- a/RxSwift/Observables/Implementations/Sample.swift +++ b/RxSwift/Observables/Implementations/Sample.swift @@ -8,57 +8,70 @@ import Foundation -class SamplerSink : ObserverType { +class SamplerSink + : ObserverType + , LockOwnerType + , SynchronizedOnType { typealias E = SampleType typealias Parent = SampleSequenceSink private let _parent: Parent + + var _lock: NSRecursiveLock { + return _parent._lock + } init(parent: Parent) { _parent = parent } func on(event: Event) { - _parent._lock.performLocked { - switch event { - case .Next: - if let element = _parent._element { - if _parent._parent._onlyNew { - _parent._element = nil - } - - _parent.observer?.on(.Next(element)) - } + synchronizedOn(event) + } - if _parent._atEnd { - _parent.observer?.on(.Completed) - _parent.dispose() - } - case .Error(let e): - _parent.observer?.on(.Error(e)) - _parent.dispose() - case .Completed: - if let element = _parent._element { + func _synchronized_on(event: Event) { + switch event { + case .Next: + if let element = _parent._element { + if _parent._parent._onlyNew { _parent._element = nil - _parent.observer?.on(.Next(element)) - } - if _parent._atEnd { - _parent.observer?.on(.Completed) - _parent.dispose() } + + _parent.observer?.on(.Next(element)) + } + + if _parent._atEnd { + _parent.observer?.on(.Completed) + _parent.dispose() + } + case .Error(let e): + _parent.observer?.on(.Error(e)) + _parent.dispose() + case .Completed: + if let element = _parent._element { + _parent._element = nil + _parent.observer?.on(.Next(element)) + } + if _parent._atEnd { + _parent.observer?.on(.Completed) + _parent.dispose() } } } } -class SampleSequenceSink : Sink, ObserverType { +class SampleSequenceSink + : Sink + , ObserverType + , LockOwnerType + , SynchronizedOnType { typealias Element = O.E typealias Parent = Sample private let _parent: Parent - private let _lock = NSRecursiveLock() + let _lock = NSRecursiveLock() // state private var _element = nil as Element? @@ -66,30 +79,32 @@ class SampleSequenceSink : Sink, ObserverType { private let _sourceSubscription = SingleAssignmentDisposable() - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func run() -> Disposable { _sourceSubscription.disposable = _parent._source.subscribe(self) let samplerSubscription = _parent._sampler.subscribe(SamplerSink(parent: self)) - return CompositeDisposable(_sourceSubscription, samplerSubscription) + return StableCompositeDisposable.create(_sourceSubscription, samplerSubscription) } func on(event: Event) { - _lock.performLocked { - switch event { - case .Next(let element): - _element = element - case .Error: - observer?.on(event) - dispose() - case .Completed: - _atEnd = true - _sourceSubscription.dispose() - } + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { + switch event { + case .Next(let element): + _element = element + case .Error: + observer?.on(event) + dispose() + case .Completed: + _atEnd = true + _sourceSubscription.dispose() } } @@ -106,9 +121,9 @@ class Sample : Producer { _onlyNew = onlyNew } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = SampleSequenceSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = SampleSequenceSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Scan.swift b/RxSwift/Observables/Implementations/Scan.swift index 58a7d720..9f0f5feb 100644 --- a/RxSwift/Observables/Implementations/Scan.swift +++ b/RxSwift/Observables/Implementations/Scan.swift @@ -15,10 +15,10 @@ class ScanSink private let _parent: Parent private var _accumulate: Accumulate - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent _accumulate = parent._seed - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func on(event: Event) { @@ -56,9 +56,9 @@ class Scan: Producer { _accumulator = accumulator } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = ScanSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return _source.subscribe(sink) + override func run(observer: O) -> Disposable { + let sink = ScanSink(parent: self, observer: observer) + sink.disposable = _source.subscribe(sink) + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Sink.swift b/RxSwift/Observables/Implementations/Sink.swift index 62851338..e6ce7278 100644 --- a/RxSwift/Observables/Implementations/Sink.swift +++ b/RxSwift/Observables/Implementations/Sink.swift @@ -8,14 +8,12 @@ import Foundation -class Sink : Disposable { +class Sink : SingleAssignmentDisposable { private var _lock = SpinLock() // state private var _observer: O? - private var _cancel: Disposable - private var _disposed: Bool = false - + var observer: O? { get { _lock.lock(); defer { _lock.unlock() } @@ -23,32 +21,24 @@ class Sink : Disposable { } } - init(observer: O, cancel: Disposable) { + init(observer: O) { #if TRACE_RESOURCES OSAtomicIncrement32(&resourceCount) #endif _observer = observer - _cancel = cancel } - private func _disposeInternal() -> Disposable? { + private func _disposeObserver() { _lock.lock(); defer { _lock.unlock() } - if _disposed { - return nil - } - - let cancel = _cancel - - _disposed = true _observer = nil - _cancel = NopDisposable.instance - - return cancel } - func dispose() { - _disposeInternal()?.dispose() + override func dispose() { + if !disposed { + _disposeObserver() + } + super.dispose() } deinit { diff --git a/RxSwift/Observables/Implementations/Skip.swift b/RxSwift/Observables/Implementations/Skip.swift index 65df2212..8d4ddc53 100644 --- a/RxSwift/Observables/Implementations/Skip.swift +++ b/RxSwift/Observables/Implementations/Skip.swift @@ -18,10 +18,10 @@ class SkipCountSink : Sin var remaining: Int - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { self.parent = parent self.remaining = parent.count - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func on(event: Event) { @@ -54,10 +54,11 @@ class SkipCount: Producer { self.count = count } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = SkipCountSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return source.subscribe(sink) + override func run(observer: O) -> Disposable { + let sink = SkipCountSink(parent: self, observer: observer) + sink.disposable = source.subscribe(sink) + + return sink } } @@ -72,9 +73,9 @@ class SkipTimeSink) { @@ -121,9 +122,9 @@ class SkipTime: Producer { self.duration = duration } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = SkipTimeSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = SkipTimeSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/SkipUntil.swift b/RxSwift/Observables/Implementations/SkipUntil.swift index 05882d29..66448416 100644 --- a/RxSwift/Observables/Implementations/SkipUntil.swift +++ b/RxSwift/Observables/Implementations/SkipUntil.swift @@ -8,20 +8,27 @@ import Foundation -class SkipUntilSinkOther : ObserverType { +class SkipUntilSinkOther + : ObserverType + , LockOwnerType + , SynchronizedOnType { typealias Parent = SkipUntilSink typealias E = Other private let _parent: Parent + + var _lock: NSRecursiveLock { + return _parent._lock + } - private let _singleAssignmentDisposable = SingleAssignmentDisposable() - - var disposable: Disposable { + private let _subscription = SingleAssignmentDisposable() + + var subscription: Disposable { get { abstractMethod() } set { - _singleAssignmentDisposable.disposable = newValue + _subscription.disposable = newValue } } @@ -33,19 +40,19 @@ class SkipUntilSinkOther) { + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { switch event { case .Next: - _parent._lock.performLocked { - _parent._forwardElements = true - _singleAssignmentDisposable.dispose() - } + _parent._forwardElements = true + _subscription.dispose() case .Error(let e): - _parent._lock.performLocked { - _parent.observer?.onError(e) - _parent.dispose() - } + _parent.observer?.onError(e) + _parent.dispose() case .Completed: - _singleAssignmentDisposable.dispose() + _subscription.dispose() } } @@ -58,46 +65,43 @@ class SkipUntilSinkOther : Sink, ObserverType { +class SkipUntilSink + : Sink + , ObserverType + , LockOwnerType + , SynchronizedOnType { typealias E = ElementType typealias Parent = SkipUntil - private let _lock = NSRecursiveLock() + let _lock = NSRecursiveLock() private let _parent: Parent private var _forwardElements = false - private let _singleAssignmentDisposable = SingleAssignmentDisposable() - - var disposable: Disposable { - get { - abstractMethod() - } - set { - _singleAssignmentDisposable.disposable = newValue - } - } - - init(parent: Parent, observer: O, cancel: Disposable) { + private let _sourceSubscription = SingleAssignmentDisposable() + + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func on(event: Event) { - _lock.performLocked { - switch event { - case .Next: - if _forwardElements { - observer?.on(event) - } - case .Error: + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { + switch event { + case .Next: + if _forwardElements { observer?.on(event) - dispose() - case .Completed: - if _forwardElements { - observer?.on(event) - } - _singleAssignmentDisposable.dispose() } + case .Error: + observer?.on(event) + dispose() + case .Completed: + if _forwardElements { + observer?.on(event) + } + _sourceSubscription.dispose() } } @@ -105,10 +109,10 @@ class SkipUntilSink: Producer { _other = other } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = SkipUntilSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = SkipUntilSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } diff --git a/RxSwift/Observables/Implementations/SkipWhile.swift b/RxSwift/Observables/Implementations/SkipWhile.swift index 8261f0eb..65af9476 100644 --- a/RxSwift/Observables/Implementations/SkipWhile.swift +++ b/RxSwift/Observables/Implementations/SkipWhile.swift @@ -14,9 +14,9 @@ class SkipWhileSink : Sin private let _parent: Parent private var _running = false - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func on(event: Event) { @@ -51,9 +51,9 @@ class SkipWhileSinkWithIndex) { @@ -100,16 +100,16 @@ class SkipWhile: Producer { _predicateWithIndex = predicate } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + override func run(observer: O) -> Disposable { if let _ = _predicate { - let sink = SkipWhileSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return _source.subscribe(sink) + let sink = SkipWhileSink(parent: self, observer: observer) + sink.disposable = _source.subscribe(sink) + return sink } else { - let sink = SkipWhileSinkWithIndex(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return _source.subscribe(sink) + let sink = SkipWhileSinkWithIndex(parent: self, observer: observer) + sink.disposable = _source.subscribe(sink) + return sink } } } diff --git a/RxSwift/Observables/Implementations/StartWith.swift b/RxSwift/Observables/Implementations/StartWith.swift index 450c1189..f2848409 100644 --- a/RxSwift/Observables/Implementations/StartWith.swift +++ b/RxSwift/Observables/Implementations/StartWith.swift @@ -18,7 +18,7 @@ class StartWith: Producer { super.init() } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + override func run(observer: O) -> Disposable { for e in elements { observer.on(.Next(e)) } diff --git a/RxSwift/Observables/Implementations/SubscribeOn.swift b/RxSwift/Observables/Implementations/SubscribeOn.swift index e1419d40..a919af00 100644 --- a/RxSwift/Observables/Implementations/SubscribeOn.swift +++ b/RxSwift/Observables/Implementations/SubscribeOn.swift @@ -14,9 +14,9 @@ class SubscribeOnSink : S let parent: Parent - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { self.parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func on(event: Event) { @@ -52,9 +52,9 @@ class SubscribeOn : Producer { self.scheduler = scheduler } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = SubscribeOnSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = SubscribeOnSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Switch.swift b/RxSwift/Observables/Implementations/Switch.swift index 1f48d33c..db5b7662 100644 --- a/RxSwift/Observables/Implementations/Switch.swift +++ b/RxSwift/Observables/Implementations/Switch.swift @@ -8,7 +8,11 @@ import Foundation -class SwitchSink : Sink, ObserverType { +class SwitchSink + : Sink + , ObserverType + , LockOwnerType + , SynchronizedOnType { typealias E = S typealias Parent = Switch @@ -16,34 +20,36 @@ class SwitchSink private let _innerSubscription: SerialDisposable = SerialDisposable() private let _parent: Parent - private let _lock = NSRecursiveLock() + let _lock = NSRecursiveLock() // state private var _stopped = false private var _latest = 0 private var _hasLatest = false - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func run() -> Disposable { let subscription = _parent._sources.subscribe(self) _subscriptions.disposable = subscription - return CompositeDisposable(_subscriptions, _innerSubscription) + return StableCompositeDisposable.create(_subscriptions, _innerSubscription) } func on(event: Event) { + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { switch event { case .Next(let observable): - let latest: Int = _lock.calculateLocked { - _hasLatest = true - _latest = _latest &+ 1 - return _latest - } - + _hasLatest = true + _latest = _latest &+ 1 + let latest = _latest + let d = SingleAssignmentDisposable() _innerSubscription.disposable = d @@ -51,33 +57,36 @@ class SwitchSink let disposable = observable.asObservable().subscribe(observer) d.disposable = disposable case .Error(let error): - _lock.performLocked { - observer?.on(.Error(error)) - dispose() - } + observer?.on(.Error(error)) + dispose() case .Completed: - _lock.performLocked { - _stopped = true - - _subscriptions.dispose() - - if !_hasLatest { - observer?.on(.Completed) - dispose() - } + _stopped = true + + _subscriptions.dispose() + + if !_hasLatest { + observer?.on(.Completed) + dispose() } } } } -class SwitchSinkIter : ObserverType { +class SwitchSinkIter + : ObserverType + , LockOwnerType + , SynchronizedOnType { typealias E = O.E typealias Parent = SwitchSink private let _parent: Parent private let _id: Int private let _self: Disposable - + + var _lock: NSRecursiveLock { + return _parent._lock + } + init(parent: Parent, id: Int, _self: Disposable) { _parent = parent _id = id @@ -85,32 +94,33 @@ class SwitchSinkIter) { - return _parent._lock.calculateLocked { - - switch event { - case .Next: break - case .Error, .Completed: - _self.dispose() - } - - if _parent._latest != _id { - return - } - - let observer = _parent.observer - - switch event { - case .Next: - observer?.on(event) - case .Error: + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { + switch event { + case .Next: break + case .Error, .Completed: + _self.dispose() + } + + if _parent._latest != _id { + return + } + + let observer = _parent.observer + + switch event { + case .Next: + observer?.on(event) + case .Error: + observer?.on(event) + _parent.dispose() + case .Completed: + _parent._hasLatest = false + if _parent._stopped { observer?.on(event) _parent.dispose() - case .Completed: - _parent._hasLatest = false - if _parent._stopped { - observer?.on(event) - _parent.dispose() - } } } } @@ -123,9 +133,9 @@ class Switch : Producer { _sources = sources } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = SwitchSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = SwitchSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Take.swift b/RxSwift/Observables/Implementations/Take.swift index 9208e618..24ef5231 100644 --- a/RxSwift/Observables/Implementations/Take.swift +++ b/RxSwift/Observables/Implementations/Take.swift @@ -18,10 +18,10 @@ class TakeCountSink : Sin private var _remaining: Int - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent _remaining = parent._count - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func on(event: Event) { @@ -61,48 +61,54 @@ class TakeCount: Producer { _count = count } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = TakeCountSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return _source.subscribe(sink) + override func run(observer: O) -> Disposable { + let sink = TakeCountSink(parent: self, observer: observer) + sink.disposable = _source.subscribe(sink) + return sink } } // time version -class TakeTimeSink : Sink, ObserverType { +class TakeTimeSink + : Sink + , LockOwnerType + , ObserverType + , SynchronizedOnType { typealias Parent = TakeTime typealias E = ElementType private let _parent: Parent - private let _lock = NSRecursiveLock() + let _lock = NSRecursiveLock() - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func on(event: Event) { - _lock.performLocked { - switch event { - case .Next(let value): - observer?.on(.Next(value)) - case .Error: - observer?.on(event) - dispose() - case .Completed: - observer?.on(event) - dispose() - } + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { + switch event { + case .Next(let value): + observer?.on(.Next(value)) + case .Error: + observer?.on(event) + dispose() + case .Completed: + observer?.on(event) + dispose() } } func tick() { - _lock.performLocked { - observer?.on(.Completed) - dispose() - } + _lock.lock(); defer { _lock.unlock() } + + observer?.on(.Completed) + dispose() } func run() -> Disposable { @@ -130,9 +136,9 @@ class TakeTime: Producer { _duration = duration } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = TakeTimeSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = TakeTimeSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/TakeLast.swift b/RxSwift/Observables/Implementations/TakeLast.swift index 34edb8a2..1a14e2e6 100644 --- a/RxSwift/Observables/Implementations/TakeLast.swift +++ b/RxSwift/Observables/Implementations/TakeLast.swift @@ -17,10 +17,10 @@ class TakeLastSink : Sink private var _elements: Queue - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent _elements = Queue(capacity: parent._count + 1) - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func on(event: Event) { @@ -55,9 +55,9 @@ class TakeLast: Producer { _count = count } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = TakeLastSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return _source.subscribe(sink) + override func run(observer: O) -> Disposable { + let sink = TakeLastSink(parent: self, observer: observer) + sink.disposable = _source.subscribe(sink) + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/TakeUntil.swift b/RxSwift/Observables/Implementations/TakeUntil.swift index b15020b9..21f492d1 100644 --- a/RxSwift/Observables/Implementations/TakeUntil.swift +++ b/RxSwift/Observables/Implementations/TakeUntil.swift @@ -8,11 +8,18 @@ import Foundation -class TakeUntilSinkOther : ObserverType { +class TakeUntilSinkOther + : ObserverType + , LockOwnerType + , SynchronizedOnType { typealias Parent = TakeUntilSink typealias E = Other private let _parent: Parent + + var _lock: NSRecursiveLock { + return _parent._lock + } private let _singleAssignmentDisposable = SingleAssignmentDisposable() @@ -33,18 +40,20 @@ class TakeUntilSinkOther) { - _parent._lock.performLocked { - switch event { - case .Next: - _parent.observer?.on(.Completed) - _parent.dispose() - case .Error(let e): - _parent.observer?.on(.Error(e)) - _parent.dispose() - case .Completed: - _parent._open = true - _singleAssignmentDisposable.dispose() - } + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { + switch event { + case .Next: + _parent.observer?.on(.Completed) + _parent.dispose() + case .Error(let e): + _parent.observer?.on(.Error(e)) + _parent.dispose() + case .Completed: + _parent._open = true + _singleAssignmentDisposable.dispose() } } @@ -55,43 +64,40 @@ class TakeUntilSinkOther : Sink, ObserverType { +class TakeUntilSink + : Sink + , LockOwnerType + , ObserverType + , SynchronizedOnType { typealias E = ElementType typealias Parent = TakeUntil private let _parent: Parent - private let _lock = NSRecursiveLock() + let _lock = NSRecursiveLock() // state private var _open = false - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func on(event: Event) { + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { switch event { case .Next: - if _open { - observer?.on(event) - } - else { - _lock.performLocked { - observer?.on(event) - } - } + observer?.on(event) case .Error: - _lock.performLocked { - observer?.on(event) - dispose() - } + observer?.on(event) + dispose() case .Completed: - _lock.performLocked { - observer?.on(event) - dispose() - } + observer?.on(event) + dispose() } } @@ -101,7 +107,7 @@ class TakeUntilSink: Producer { _other = other } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = TakeUntilSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = TakeUntilSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/TakeWhile.swift b/RxSwift/Observables/Implementations/TakeWhile.swift index 22c5f99a..80dadf94 100644 --- a/RxSwift/Observables/Implementations/TakeWhile.swift +++ b/RxSwift/Observables/Implementations/TakeWhile.swift @@ -8,7 +8,9 @@ import Foundation -class TakeWhileSink : Sink, ObserverType { +class TakeWhileSink + : Sink + , ObserverType { typealias Parent = TakeWhile typealias Element = ElementType @@ -16,9 +18,9 @@ class TakeWhileSink : Sin private var _running = true - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func on(event: Event) { @@ -50,7 +52,9 @@ class TakeWhileSink : Sin } -class TakeWhileSinkWithIndex : Sink, ObserverType { +class TakeWhileSinkWithIndex + : Sink + , ObserverType { typealias Parent = TakeWhile typealias Element = ElementType @@ -59,9 +63,9 @@ class TakeWhileSinkWithIndex) { @@ -114,15 +118,15 @@ class TakeWhile: Producer { _predicateWithIndex = predicate } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + override func run(observer: O) -> Disposable { if let _ = _predicate { - let sink = TakeWhileSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return _source.subscribe(sink) + let sink = TakeWhileSink(parent: self, observer: observer) + sink.disposable = _source.subscribe(sink) + return sink } else { - let sink = TakeWhileSinkWithIndex(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return _source.subscribe(sink) + let sink = TakeWhileSinkWithIndex(parent: self, observer: observer) + sink.disposable = _source.subscribe(sink) + return sink } } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Throttle.swift b/RxSwift/Observables/Implementations/Throttle.swift index 5ca0642f..e89cf908 100644 --- a/RxSwift/Observables/Implementations/Throttle.swift +++ b/RxSwift/Observables/Implementations/Throttle.swift @@ -8,13 +8,17 @@ import Foundation -class ThrottleSink : Sink, ObserverType { +class ThrottleSink + : Sink + , ObserverType + , LockOwnerType + , SynchronizedOnType { typealias Element = O.E typealias ParentType = Throttle private let _parent: ParentType - private let _lock = NSRecursiveLock() + let _lock = NSRecursiveLock() // state private var _id = 0 as UInt64 @@ -22,81 +26,60 @@ class ThrottleSink : Sink, Observe let cancellable = SerialDisposable() - init(parent: ParentType, observer: O, cancel: Disposable) { + init(parent: ParentType, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func run() -> Disposable { let subscription = _parent._source.subscribe(self) - return CompositeDisposable(subscription, cancellable) + return StableCompositeDisposable.create(subscription, cancellable) } func on(event: Event) { + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { switch event { - case .Next: - break - case .Error, .Completed: - cancellable.dispose() - } - - let latestId = _lock.calculateLocked { () -> UInt64 in - let observer = self.observer - - let oldValue = _value - + case .Next(let element): _id = _id &+ 1 - - switch event { - case .Next(let element): - _value = element - case .Error: - _value = nil - observer?.on(event) - dispose() - case .Completed: - _value = nil - if let value = oldValue { - observer?.on(.Next(value)) - } - observer?.on(.Completed) - dispose() - } - - return _id - } - - - switch event { - case .Next: - let d = SingleAssignmentDisposable() - self.cancellable.disposable = d + let currentId = _id + _value = element + let scheduler = _parent._scheduler let dueTime = _parent._dueTime - - let disposeTimer = scheduler.scheduleRelative(latestId, dueTime: dueTime) { (id) in - self.propagate() - return NopDisposable.instance + + let d = SingleAssignmentDisposable() + self.cancellable.disposable = d + d.disposable = scheduler.scheduleRelative(currentId, dueTime: dueTime, action: self.propagate) + case .Error: + _value = nil + observer?.on(event) + dispose() + case .Completed: + if let value = _value { + _value = nil + observer?.on(.Next(value)) } - - d.disposable = disposeTimer - default: break + observer?.on(.Completed) + dispose() } } - func propagate() { - let originalValue: Element? = _lock.calculateLocked { + func propagate(currentId: UInt64) -> Disposable { + _lock.lock(); defer { _lock.unlock() } // { let originalValue = _value - _value = nil - return originalValue - } - - if let value = originalValue { - observer?.on(.Next(value)) - } + + if let value = originalValue where _id == currentId { + _value = nil + observer?.on(.Next(value)) + } + // } + return NopDisposable.instance } } @@ -112,10 +95,10 @@ class Throttle : Producer { _scheduler = scheduler } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = ThrottleSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = ThrottleSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Timer.swift b/RxSwift/Observables/Implementations/Timer.swift index 35f2839f..f6b7d724 100644 --- a/RxSwift/Observables/Implementations/Timer.swift +++ b/RxSwift/Observables/Implementations/Timer.swift @@ -13,9 +13,9 @@ class TimerSink : Sink private let _parent: Parent - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func run() -> Disposable { @@ -31,9 +31,9 @@ class TimerOneOffSink : Si private let _parent: Parent - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func run() -> Disposable { @@ -59,16 +59,16 @@ class Timer: Producer { _period = period } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + override func run(observer: O) -> Disposable { if let _ = _period { - let sink = TimerSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + let sink = TimerSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } else { - let sink = TimerOneOffSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + let sink = TimerOneOffSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/ToArray.swift b/RxSwift/Observables/Implementations/ToArray.swift index c52cf710..1b514d26 100644 --- a/RxSwift/Observables/Implementations/ToArray.swift +++ b/RxSwift/Observables/Implementations/ToArray.swift @@ -14,10 +14,10 @@ class ToArraySink : Sink< let _parent: Parent var _list = Array() - init(parent: Parent, observer: O, cancel: Disposable) { - self._parent = parent + init(parent: Parent, observer: O) { + _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func on(event: Event) { @@ -42,9 +42,9 @@ class ToArray : Producer<[SourceType]> { _source = source } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = ToArraySink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return _source.subscribe(sink) + override func run(observer: O) -> Disposable { + let sink = ToArraySink(parent: self, observer: observer) + sink.disposable = _source.subscribe(sink) + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Using.swift b/RxSwift/Observables/Implementations/Using.swift index 6c852634..ed4a891a 100644 --- a/RxSwift/Observables/Implementations/Using.swift +++ b/RxSwift/Observables/Implementations/Using.swift @@ -15,9 +15,9 @@ class UsingSink Disposable { @@ -70,9 +70,9 @@ class Using: Producer { _observableFactory = observableFactory } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = UsingSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = UsingSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/WithLatestFrom.swift b/RxSwift/Observables/Implementations/WithLatestFrom.swift index b2cf3233..685c56d7 100644 --- a/RxSwift/Observables/Implementations/WithLatestFrom.swift +++ b/RxSwift/Observables/Implementations/WithLatestFrom.swift @@ -8,21 +8,24 @@ import Foundation -class WithLatestFromSink : Sink, ObserverType { - +class WithLatestFromSink + : Sink + , ObserverType + , LockOwnerType + , SynchronizedOnType { + typealias Parent = WithLatestFrom typealias E = FirstType private let _parent: Parent - private var _lock = NSRecursiveLock() + var _lock = NSRecursiveLock() private var _latest: SecondType? - - - init(parent: Parent, observer: O, cancel: Disposable) { + + init(parent: Parent, observer: O) { _parent = parent - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func run() -> Disposable { @@ -34,60 +37,70 @@ class WithLatestFromSink) { - _lock.performLocked { - switch event { - case let .Next(value): - guard let latest = _latest else { return } - do { - let res = try _parent._resultSelector(value, latest) - - observer?.onNext(res) - } catch let e { - observer?.onError(e) - dispose() - } - case .Completed: - observer?.onComplete() - dispose() - case let .Error(error): - observer?.onError(error) + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { + switch event { + case let .Next(value): + guard let latest = _latest else { return } + do { + let res = try _parent._resultSelector(value, latest) + + observer?.onNext(res) + } catch let e { + observer?.onError(e) dispose() } + case .Completed: + observer?.onComplete() + dispose() + case let .Error(error): + observer?.onError(error) + dispose() } } } -class WithLatestFromSecond: ObserverType { +class WithLatestFromSecond + : ObserverType + , LockOwnerType + , SynchronizedOnType { typealias Parent = WithLatestFromSink typealias E = SecondType private let _parent: Parent private let _disposable: Disposable - + + var _lock: NSRecursiveLock { + get { + return _parent._lock + } + } + init(parent: Parent, disposable: Disposable) { _parent = parent _disposable = disposable } func on(event: Event) { + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { switch event { case let .Next(value): - _parent._lock.performLocked { - _parent._latest = value - } + _parent._latest = value case .Completed: _disposable.dispose() case let .Error(error): - _parent._lock.performLocked { - _parent.observer?.onError(error) - _parent.dispose() - } + _parent.observer?.onError(error) + _parent.dispose() } } - } class WithLatestFrom: Producer { @@ -103,10 +116,9 @@ class WithLatestFrom: Producer { _resultSelector = resultSelector } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - - let sink = WithLatestFromSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = WithLatestFromSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Zip+CollectionType.swift b/RxSwift/Observables/Implementations/Zip+CollectionType.swift index a2339599..f2b188a8 100644 --- a/RxSwift/Observables/Implementations/Zip+CollectionType.swift +++ b/RxSwift/Observables/Implementations/Zip+CollectionType.swift @@ -8,7 +8,8 @@ import Foundation -class ZipCollectionTypeSink : Sink { +class ZipCollectionTypeSink + : Sink { typealias Parent = ZipCollectionType typealias SourceElement = C.Generator.Element.E @@ -23,7 +24,7 @@ class ZipCollectionTypeSink](count: parent.count, repeatedValue: Queue(capacity: 4)) _isDone = [Bool](count: parent.count, repeatedValue: false) @@ -34,11 +35,11 @@ class ZipCollectionTypeSink, atIndex: Int) { - _lock.performLocked { + _lock.lock(); defer { _lock.unlock() } // { switch event { case .Next(let element): _values[atIndex].enqueue(element) @@ -97,7 +98,7 @@ class ZipCollectionTypeSink Disposable { @@ -128,9 +129,9 @@ class ZipCollectionType(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = ZipCollectionTypeSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = ZipCollectionTypeSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } diff --git a/RxSwift/Observables/Implementations/Zip+arity.swift b/RxSwift/Observables/Implementations/Zip+arity.swift index 196cd76c..7d70640a 100644 --- a/RxSwift/Observables/Implementations/Zip+arity.swift +++ b/RxSwift/Observables/Implementations/Zip+arity.swift @@ -39,9 +39,9 @@ class ZipSink2_ : ZipSink { var _values1: Queue = Queue(capacity: 2) var _values2: Queue = Queue(capacity: 2) - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(arity: 2, observer: observer, cancel: cancel) + super.init(arity: 2, observer: observer) } override func hasElements(index: Int) -> Bool { @@ -60,8 +60,8 @@ class ZipSink2_ : ZipSink { let subscription1 = SingleAssignmentDisposable() let subscription2 = SingleAssignmentDisposable() - let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) - let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) + let observer1 = ZipObserver(lock: _lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) + let observer2 = ZipObserver(lock: _lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) subscription1.disposable = _parent.source1.subscribe(observer1) subscription2.disposable = _parent.source2.subscribe(observer2) @@ -92,10 +92,10 @@ class Zip2 : Producer { _resultSelector = resultSelector } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = ZipSink2_(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = ZipSink2_(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } @@ -129,9 +129,9 @@ class ZipSink3_ : ZipSink { var _values2: Queue = Queue(capacity: 2) var _values3: Queue = Queue(capacity: 2) - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(arity: 3, observer: observer, cancel: cancel) + super.init(arity: 3, observer: observer) } override func hasElements(index: Int) -> Bool { @@ -152,9 +152,9 @@ class ZipSink3_ : ZipSink { let subscription2 = SingleAssignmentDisposable() let subscription3 = SingleAssignmentDisposable() - let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) - let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) - let observer3 = ZipObserver(lock: lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) + let observer1 = ZipObserver(lock: _lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) + let observer2 = ZipObserver(lock: _lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) + let observer3 = ZipObserver(lock: _lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) subscription1.disposable = _parent.source1.subscribe(observer1) subscription2.disposable = _parent.source2.subscribe(observer2) @@ -189,10 +189,10 @@ class Zip3 : Producer { _resultSelector = resultSelector } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = ZipSink3_(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = ZipSink3_(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } @@ -227,9 +227,9 @@ class ZipSink4_ : ZipSink { var _values3: Queue = Queue(capacity: 2) var _values4: Queue = Queue(capacity: 2) - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(arity: 4, observer: observer, cancel: cancel) + super.init(arity: 4, observer: observer) } override func hasElements(index: Int) -> Bool { @@ -252,10 +252,10 @@ class ZipSink4_ : ZipSink { let subscription3 = SingleAssignmentDisposable() let subscription4 = SingleAssignmentDisposable() - let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) - let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) - let observer3 = ZipObserver(lock: lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) - let observer4 = ZipObserver(lock: lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) + let observer1 = ZipObserver(lock: _lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) + let observer2 = ZipObserver(lock: _lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) + let observer3 = ZipObserver(lock: _lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) + let observer4 = ZipObserver(lock: _lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) subscription1.disposable = _parent.source1.subscribe(observer1) subscription2.disposable = _parent.source2.subscribe(observer2) @@ -294,10 +294,10 @@ class Zip4 : Producer { _resultSelector = resultSelector } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = ZipSink4_(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = ZipSink4_(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } @@ -333,9 +333,9 @@ class ZipSink5_ : ZipSink { var _values4: Queue = Queue(capacity: 2) var _values5: Queue = Queue(capacity: 2) - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(arity: 5, observer: observer, cancel: cancel) + super.init(arity: 5, observer: observer) } override func hasElements(index: Int) -> Bool { @@ -360,11 +360,11 @@ class ZipSink5_ : ZipSink { let subscription4 = SingleAssignmentDisposable() let subscription5 = SingleAssignmentDisposable() - let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) - let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) - let observer3 = ZipObserver(lock: lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) - let observer4 = ZipObserver(lock: lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) - let observer5 = ZipObserver(lock: lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5) + let observer1 = ZipObserver(lock: _lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) + let observer2 = ZipObserver(lock: _lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) + let observer3 = ZipObserver(lock: _lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) + let observer4 = ZipObserver(lock: _lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) + let observer5 = ZipObserver(lock: _lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5) subscription1.disposable = _parent.source1.subscribe(observer1) subscription2.disposable = _parent.source2.subscribe(observer2) @@ -407,10 +407,10 @@ class Zip5 : Producer { _resultSelector = resultSelector } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = ZipSink5_(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = ZipSink5_(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } @@ -447,9 +447,9 @@ class ZipSink6_ : ZipSink { var _values5: Queue = Queue(capacity: 2) var _values6: Queue = Queue(capacity: 2) - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(arity: 6, observer: observer, cancel: cancel) + super.init(arity: 6, observer: observer) } override func hasElements(index: Int) -> Bool { @@ -476,12 +476,12 @@ class ZipSink6_ : ZipSink { let subscription5 = SingleAssignmentDisposable() let subscription6 = SingleAssignmentDisposable() - let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) - let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) - let observer3 = ZipObserver(lock: lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) - let observer4 = ZipObserver(lock: lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) - let observer5 = ZipObserver(lock: lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5) - let observer6 = ZipObserver(lock: lock, parent: self, index: 5, setNextValue: { self._values6.enqueue($0) }, this: subscription6) + let observer1 = ZipObserver(lock: _lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) + let observer2 = ZipObserver(lock: _lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) + let observer3 = ZipObserver(lock: _lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) + let observer4 = ZipObserver(lock: _lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) + let observer5 = ZipObserver(lock: _lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5) + let observer6 = ZipObserver(lock: _lock, parent: self, index: 5, setNextValue: { self._values6.enqueue($0) }, this: subscription6) subscription1.disposable = _parent.source1.subscribe(observer1) subscription2.disposable = _parent.source2.subscribe(observer2) @@ -528,10 +528,10 @@ class Zip6 : Producer { _resultSelector = resultSelector } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = ZipSink6_(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = ZipSink6_(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } @@ -569,9 +569,9 @@ class ZipSink7_ : ZipSink { var _values6: Queue = Queue(capacity: 2) var _values7: Queue = Queue(capacity: 2) - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(arity: 7, observer: observer, cancel: cancel) + super.init(arity: 7, observer: observer) } override func hasElements(index: Int) -> Bool { @@ -600,13 +600,13 @@ class ZipSink7_ : ZipSink { let subscription6 = SingleAssignmentDisposable() let subscription7 = SingleAssignmentDisposable() - let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) - let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) - let observer3 = ZipObserver(lock: lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) - let observer4 = ZipObserver(lock: lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) - let observer5 = ZipObserver(lock: lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5) - let observer6 = ZipObserver(lock: lock, parent: self, index: 5, setNextValue: { self._values6.enqueue($0) }, this: subscription6) - let observer7 = ZipObserver(lock: lock, parent: self, index: 6, setNextValue: { self._values7.enqueue($0) }, this: subscription7) + let observer1 = ZipObserver(lock: _lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) + let observer2 = ZipObserver(lock: _lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) + let observer3 = ZipObserver(lock: _lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) + let observer4 = ZipObserver(lock: _lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) + let observer5 = ZipObserver(lock: _lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5) + let observer6 = ZipObserver(lock: _lock, parent: self, index: 5, setNextValue: { self._values6.enqueue($0) }, this: subscription6) + let observer7 = ZipObserver(lock: _lock, parent: self, index: 6, setNextValue: { self._values7.enqueue($0) }, this: subscription7) subscription1.disposable = _parent.source1.subscribe(observer1) subscription2.disposable = _parent.source2.subscribe(observer2) @@ -657,10 +657,10 @@ class Zip7 : Producer { _resultSelector = resultSelector } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = ZipSink7_(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = ZipSink7_(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } @@ -699,9 +699,9 @@ class ZipSink8_ : ZipSink { var _values7: Queue = Queue(capacity: 2) var _values8: Queue = Queue(capacity: 2) - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(arity: 8, observer: observer, cancel: cancel) + super.init(arity: 8, observer: observer) } override func hasElements(index: Int) -> Bool { @@ -732,14 +732,14 @@ class ZipSink8_ : ZipSink { let subscription7 = SingleAssignmentDisposable() let subscription8 = SingleAssignmentDisposable() - let observer1 = ZipObserver(lock: lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) - let observer2 = ZipObserver(lock: lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) - let observer3 = ZipObserver(lock: lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) - let observer4 = ZipObserver(lock: lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) - let observer5 = ZipObserver(lock: lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5) - let observer6 = ZipObserver(lock: lock, parent: self, index: 5, setNextValue: { self._values6.enqueue($0) }, this: subscription6) - let observer7 = ZipObserver(lock: lock, parent: self, index: 6, setNextValue: { self._values7.enqueue($0) }, this: subscription7) - let observer8 = ZipObserver(lock: lock, parent: self, index: 7, setNextValue: { self._values8.enqueue($0) }, this: subscription8) + let observer1 = ZipObserver(lock: _lock, parent: self, index: 0, setNextValue: { self._values1.enqueue($0) }, this: subscription1) + let observer2 = ZipObserver(lock: _lock, parent: self, index: 1, setNextValue: { self._values2.enqueue($0) }, this: subscription2) + let observer3 = ZipObserver(lock: _lock, parent: self, index: 2, setNextValue: { self._values3.enqueue($0) }, this: subscription3) + let observer4 = ZipObserver(lock: _lock, parent: self, index: 3, setNextValue: { self._values4.enqueue($0) }, this: subscription4) + let observer5 = ZipObserver(lock: _lock, parent: self, index: 4, setNextValue: { self._values5.enqueue($0) }, this: subscription5) + let observer6 = ZipObserver(lock: _lock, parent: self, index: 5, setNextValue: { self._values6.enqueue($0) }, this: subscription6) + let observer7 = ZipObserver(lock: _lock, parent: self, index: 6, setNextValue: { self._values7.enqueue($0) }, this: subscription7) + let observer8 = ZipObserver(lock: _lock, parent: self, index: 7, setNextValue: { self._values8.enqueue($0) }, this: subscription8) subscription1.disposable = _parent.source1.subscribe(observer1) subscription2.disposable = _parent.source2.subscribe(observer2) @@ -794,10 +794,10 @@ class Zip8 : Producer { _resultSelector = resultSelector } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = ZipSink8_(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = ZipSink8_(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } diff --git a/RxSwift/Observables/Implementations/Zip+arity.tt b/RxSwift/Observables/Implementations/Zip+arity.tt index 6b9a0460..c16b8418 100644 --- a/RxSwift/Observables/Implementations/Zip+arity.tt +++ b/RxSwift/Observables/Implementations/Zip+arity.tt @@ -38,9 +38,9 @@ class ZipSink<%= i %>_<<%= (Array(1...i).map { "E\($0)" }).joinWithSeparator(", " var _values\($0): Queue = Queue(capacity: 2)" }).joinWithSeparator("\n") %> - init(parent: Parent, observer: O, cancel: Disposable) { + init(parent: Parent, observer: O) { _parent = parent - super.init(arity: <%= i %>, observer: observer, cancel: cancel) + super.init(arity: <%= i %>, observer: observer) } override func hasElements(index: Int) -> Bool { @@ -61,7 +61,7 @@ class ZipSink<%= i %>_<<%= (Array(1...i).map { "E\($0)" }).joinWithSeparator(", }).joinWithSeparator("\n") %> <%= (Array(1...i).map { -" let observer\($0) = ZipObserver(lock: lock, parent: self, index: \($0 - 1), setNextValue: { self._values\($0).enqueue($0) }, this: subscription\($0))" +" let observer\($0) = ZipObserver(lock: _lock, parent: self, index: \($0 - 1), setNextValue: { self._values\($0).enqueue($0) }, this: subscription\($0))" }).joinWithSeparator("\n") %> <%= (Array(1...i).map { @@ -93,10 +93,10 @@ class Zip<%= i %><<%= (Array(1...i).map { "E\($0)" }).joinWithSeparator(", ") %> _resultSelector = resultSelector } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = ZipSink<%= i %>_(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run(observer: O) -> Disposable { + let sink = ZipSink<%= i %>_(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } diff --git a/RxSwift/Observables/Implementations/Zip.swift b/RxSwift/Observables/Implementations/Zip.swift index 71927516..273f80ff 100644 --- a/RxSwift/Observables/Implementations/Zip.swift +++ b/RxSwift/Observables/Implementations/Zip.swift @@ -18,18 +18,18 @@ protocol ZipSinkProtocol : class class ZipSink : Sink, ZipSinkProtocol { typealias Element = O.E - let arity: Int - - let lock = NSRecursiveLock() - + let _arity: Int + + let _lock = NSRecursiveLock() + // state private var _isDone: [Bool] - init(arity: Int, observer: O, cancel: Disposable) { + init(arity: Int, observer: O) { _isDone = [Bool](count: arity, repeatedValue: false) - self.arity = arity + _arity = arity - super.init(observer: observer, cancel: cancel) + super.init(observer: observer) } func getResult() throws -> Element { @@ -43,7 +43,7 @@ class ZipSink : Sink, ZipSinkProtocol { func next(index: Int) { var hasValueAll = true - for i in 0 ..< arity { + for i in 0 ..< _arity { if !hasElements(i) { hasValueAll = false; break; @@ -102,13 +102,16 @@ class ZipSink : Sink, ZipSinkProtocol { } } -class ZipObserver : ObserverType { +class ZipObserver + : ObserverType + , LockOwnerType + , SynchronizedOnType { typealias E = ElementType typealias ValueSetter = (ElementType) -> () private var _parent: ZipSinkProtocol? - private let _lock: NSRecursiveLock + let _lock: NSRecursiveLock // state private let _index: Int @@ -124,7 +127,10 @@ class ZipObserver : ObserverType { } func on(event: Event) { - + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { if let _ = _parent { switch event { case .Next(_): @@ -136,17 +142,15 @@ class ZipObserver : ObserverType { } } - _lock.performLocked { - if let parent = _parent { - switch event { - case .Next(let value): - _setNextValue(value) - parent.next(_index) - case .Error(let error): - parent.fail(error) - case .Completed: - parent.done(_index) - } + if let parent = _parent { + switch event { + case .Next(let value): + _setNextValue(value) + parent.next(_index) + case .Error(let error): + parent.fail(error) + case .Completed: + parent.done(_index) } } } diff --git a/RxSwift/Observers/TailRecursiveSink.swift b/RxSwift/Observers/TailRecursiveSink.swift index 93bee506..c4021064 100644 --- a/RxSwift/Observers/TailRecursiveSink.swift +++ b/RxSwift/Observers/TailRecursiveSink.swift @@ -19,8 +19,8 @@ class TailRecursiveSink Disposable { diff --git a/RxSwift/Schedulers/CurrentThreadScheduler.swift b/RxSwift/Schedulers/CurrentThreadScheduler.swift index b839a4a8..ada3b894 100644 --- a/RxSwift/Schedulers/CurrentThreadScheduler.swift +++ b/RxSwift/Schedulers/CurrentThreadScheduler.swift @@ -104,7 +104,7 @@ public class CurrentThreadScheduler : ImmediateSchedulerType { if CurrentThreadScheduler.isScheduleRequired { CurrentThreadScheduler.isScheduleRequired = false - action(state) + let disposable = action(state) defer { CurrentThreadScheduler.isScheduleRequired = true @@ -112,7 +112,7 @@ public class CurrentThreadScheduler : ImmediateSchedulerType { } guard let queue = CurrentThreadScheduler.queue else { - return NopDisposable.instance + return disposable } while let latest = queue.value.tryDequeue() { @@ -122,7 +122,7 @@ public class CurrentThreadScheduler : ImmediateSchedulerType { latest.invoke() } - return NopDisposable.instance + return disposable } let existingQueue = CurrentThreadScheduler.queue diff --git a/RxTests/PerformanceTests/main.swift b/RxTests/PerformanceTests/main.swift index 289abde2..414168da 100644 --- a/RxTests/PerformanceTests/main.swift +++ b/RxTests/PerformanceTests/main.swift @@ -25,17 +25,17 @@ compareTwoImplementations(benchmarkTime: true, first: { //combineLatest(a, publishSubject - //.shareReplay(1) + .shareReplay(1) + .map { $0 } + .filter { _ in true }// ){ x, _ in x } //.map { $0 } - //.filter { _ in true }// ){ x, _ in x } - //.map { $0 } - .flatMap { just($0) } + //.flatMap { just($0) } .subscribeNext { _ in } - for i in 0..<100 { + for i in 0..<1000 { publishSubject.on(.Next(i)) } diff --git a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift index 3440ee91..0d77a9e6 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift @@ -3057,12 +3057,16 @@ extension ObservableStandardSequenceOperatorsTest { func testTakeLast_DecrementCountsFirst() { let k = BehaviorSubject(value: false) - + + var elements = [Bool]() _ = k.takeLast(1).subscribeNext { n in + elements.append(n) k.on(.Next(!n)) } k.on(.Completed) + + XCTAssertEqual(elements, [false]) } } diff --git a/RxTests/RxTest.swift b/RxTests/RxTest.swift index 2f661520..a15f9629 100644 --- a/RxTests/RxTest.swift +++ b/RxTests/RxTest.swift @@ -107,7 +107,7 @@ class RxTest: XCTestCase { #if TRACE_RESOURCES // give 5 sec to clean up resources - for var i = 0; i < 100; ++i { + for var i = 0; i < 10; ++i { if self.startResourceCount < resourceCount { // main schedulers need to finish work NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate(timeIntervalSinceNow: 0.05)) diff --git a/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-OSX.xcscheme b/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-OSX.xcscheme index 4f6e4922..a9d93f0f 100644 --- a/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-OSX.xcscheme +++ b/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-OSX.xcscheme @@ -52,7 +52,7 @@ Date: Sun, 1 Nov 2015 12:21:14 +0100 Subject: [PATCH 143/210] Removes optimizations on `PerformanceTests` because it generates wrong assembly, kudos Swift compiler team. --- RxTests/PerformanceTests/main.swift | 2 +- RxTests/RxTests.xcodeproj/project.pbxproj | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/RxTests/PerformanceTests/main.swift b/RxTests/PerformanceTests/main.swift index 414168da..1d80a525 100644 --- a/RxTests/PerformanceTests/main.swift +++ b/RxTests/PerformanceTests/main.swift @@ -35,7 +35,7 @@ compareTwoImplementations(benchmarkTime: true, first: { } - for i in 0..<1000 { + for i in 0..<100 { publishSubject.on(.Next(i)) } diff --git a/RxTests/RxTests.xcodeproj/project.pbxproj b/RxTests/RxTests.xcodeproj/project.pbxproj index 5b5313c8..1224c280 100644 --- a/RxTests/RxTests.xcodeproj/project.pbxproj +++ b/RxTests/RxTests.xcodeproj/project.pbxproj @@ -1075,6 +1075,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.PerformanceTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Release; }; @@ -1119,6 +1120,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "Krunoslav-Zaher.PerformanceTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = "Release-Tests"; }; From d4cd9cf69b9ef13a75a89804e7515a9fa0fab093 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 1 Nov 2015 12:47:06 +0100 Subject: [PATCH 144/210] Refactors `ReplaySubject` and `BehaviorSubject` to be consistent with `ReplaySubject`. --- RxSwift/Subjects/BehaviorSubject.swift | 111 ++++++++-------- RxSwift/Subjects/ReplaySubject.swift | 170 +++++++++++-------------- 2 files changed, 128 insertions(+), 153 deletions(-) diff --git a/RxSwift/Subjects/BehaviorSubject.swift b/RxSwift/Subjects/BehaviorSubject.swift index 7623332d..4059ce87 100644 --- a/RxSwift/Subjects/BehaviorSubject.swift +++ b/RxSwift/Subjects/BehaviorSubject.swift @@ -8,37 +8,24 @@ import Foundation -private class BehaviorSubjectSubscription : Disposable { - typealias Parent = BehaviorSubject - typealias DisposeKey = Bag>.KeyType - - private let _parent: Parent - private var _disposeKey: DisposeKey? - - init(parent: BehaviorSubject, disposeKey: DisposeKey) { - _parent = parent - _disposeKey = disposeKey - } - - func dispose() { - _parent._lock.performLocked { - if let disposeKey = _disposeKey { - _parent._observers.removeKey(disposeKey) - _disposeKey = nil - } - } - } -} - /** Represents a value that changes over time. Observers can subscribe to the subject to receive the last (or initial) value and all subsequent notifications. */ -public final class BehaviorSubject : Observable, SubjectType, ObserverType, Disposable { +public final class BehaviorSubject + : Observable + , SubjectType + , ObserverType + , LockOwnerType + , SynchronizedOnType + , SynchronizedSubscribeType + , SynchronizedUnsubscribeType + , Disposable { public typealias SubjectObserverType = BehaviorSubject + typealias DisposeKey = Bag>.KeyType - private let _lock = NSRecursiveLock() + let _lock = NSRecursiveLock() // state private var _disposed = false @@ -50,9 +37,7 @@ public final class BehaviorSubject : Observable, SubjectType, Indicates whether the subject has been disposed. */ public var disposed: Bool { - return _lock.calculateLocked { - return _disposed - } + return _disposed } /** @@ -70,7 +55,7 @@ public final class BehaviorSubject : Observable, SubjectType, - returns: Latest value. */ public func value() throws -> Element { - return try _lock.calculateLockedOrFail { + _lock.lock(); defer { _lock.unlock() } // { if _disposed { throw RxError.DisposedError } @@ -82,7 +67,7 @@ public final class BehaviorSubject : Observable, SubjectType, else { return _value } - } + //} } /** @@ -91,20 +76,22 @@ public final class BehaviorSubject : Observable, SubjectType, - parameter event: Event to send to the observers. */ public func on(event: Event) { - _lock.performLocked { - if _stoppedEvent != nil || _disposed { - return - } - - switch event { - case .Next(let value): - _value = value - case .Error, .Completed: - _stoppedEvent = event - } - - _observers.on(event) + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { + if _stoppedEvent != nil || _disposed { + return } + + switch event { + case .Next(let value): + _value = value + case .Error, .Completed: + _stoppedEvent = event + } + + _observers.on(event) } /** @@ -114,22 +101,32 @@ public final class BehaviorSubject : Observable, SubjectType, - returns: Disposable object that can be used to unsubscribe the observer from the subject. */ public override func subscribe(observer: O) -> Disposable { - return _lock.calculateLocked { - if _disposed { - observer.on(.Error(RxError.DisposedError)) - return NopDisposable.instance - } - - if let stoppedEvent = _stoppedEvent { - observer.on(stoppedEvent) - return NopDisposable.instance - } - - let key = _observers.insert(observer.asObserver()) - observer.on(.Next(_value)) - - return BehaviorSubjectSubscription(parent: self, disposeKey: key) + return synchronizedSubscribe(observer) + } + + func _synchronized_subscribe(observer: O) -> Disposable { + if _disposed { + observer.on(.Error(RxError.DisposedError)) + return NopDisposable.instance } + + if let stoppedEvent = _stoppedEvent { + observer.on(stoppedEvent) + return NopDisposable.instance + } + + let key = _observers.insert(observer.asObserver()) + observer.on(.Next(_value)) + + return SubscriptionDisposable(owner: self, key: key) + } + + func _synchronized_unsubscribe(disposeKey: DisposeKey) { + if _disposed { + return + } + + _ = _observers.removeKey(disposeKey) } /** diff --git a/RxSwift/Subjects/ReplaySubject.swift b/RxSwift/Subjects/ReplaySubject.swift index 97332e4c..3ad58d4f 100644 --- a/RxSwift/Subjects/ReplaySubject.swift +++ b/RxSwift/Subjects/ReplaySubject.swift @@ -13,7 +13,11 @@ Represents an object that is both an observable sequence as well as an observer. Each notification is broadcasted to all subscribed and future observers, subject to buffer trimming policies. */ -public class ReplaySubject : Observable, SubjectType, ObserverType, Disposable { +public class ReplaySubject + : Observable + , SubjectType + , ObserverType + , Disposable { public typealias SubjectObserverType = ReplaySubject typealias DisposeKey = Bag>.KeyType @@ -60,9 +64,15 @@ public class ReplaySubject : Observable, SubjectType, Observer } } -class ReplayBufferBase : ReplaySubject { +class ReplayBufferBase + : ReplaySubject + , LockOwnerType + , SynchronizedOnType + , SynchronizedSubscribeType + , SynchronizedUnsubscribeType + , SynchronizedDisposeType { - private let _lock = NSRecursiveLock() + let _lock = NSRecursiveLock() // state private var _disposed = false @@ -86,73 +96,72 @@ class ReplayBufferBase : ReplaySubject { } override func on(event: Event) { - _lock.performLocked { - if _disposed { - return - } - - if _stoppedEvent != nil { - return - } - - switch event { - case .Next(let value): - addValueToBuffer(value) - trim() - _observers.on(event) - case .Error, .Completed: - _stoppedEvent = event - trim() - _observers.on(event) - _observers.removeAll() - } - + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { + if _disposed { + return + } + + if _stoppedEvent != nil { + return + } + + switch event { + case .Next(let value): + addValueToBuffer(value) + trim() + _observers.on(event) + case .Error, .Completed: + _stoppedEvent = event + trim() + _observers.on(event) + _observers.removeAll() } } override func subscribe(observer: O) -> Disposable { - return _lock.calculateLocked { - if _disposed { - observer.on(.Error(RxError.DisposedError)) - return NopDisposable.instance - } - - let AnyObserver = observer.asObserver() - - replayBuffer(AnyObserver) - if let stoppedEvent = _stoppedEvent { - observer.on(stoppedEvent) - return NopDisposable.instance - } - else { - let key = _observers.insert(AnyObserver) - return ReplaySubscription(subject: self, disposeKey: key) - } - } + return synchronizedSubscribe(observer) } - - override func unsubscribe(key: DisposeKey) { - _lock.performLocked { - if _disposed { - return - } - - _ = _observers.removeKey(key) + + func _synchronized_subscribe(observer: O) -> Disposable { + if _disposed { + observer.on(.Error(RxError.DisposedError)) + return NopDisposable.instance + } + + let AnyObserver = observer.asObserver() + + replayBuffer(AnyObserver) + if let stoppedEvent = _stoppedEvent { + observer.on(stoppedEvent) + return NopDisposable.instance + } + else { + let key = _observers.insert(AnyObserver) + return SubscriptionDisposable(owner: self, key: key) } } - func lockedDispose() { - _disposed = true - _stoppedEvent = nil - _observers.removeAll() + func _synchronized_unsubscribe(disposeKey: DisposeKey) { + if _disposed { + return + } + + _ = _observers.removeKey(disposeKey) } override func dispose() { super.dispose() - - _lock.performLocked { - lockedDispose() - } + + synchronizedDispose() + } + + func _synchronized_dispose() { + _disposed = true + _stoppedEvent = nil + _observers.removeAll() } } @@ -176,10 +185,9 @@ class ReplayOne : ReplayBufferBase { observer.on(.Next(value)) } } - - override func lockedDispose() { - super.lockedDispose() - + + override func _synchronized_dispose() { + super._synchronized_dispose() _value = nil } } @@ -200,9 +208,9 @@ class ReplayManyBase : ReplayBufferBase { observer.on(.Next(item)) } } - - override func lockedDispose() { - super.lockedDispose() + + override func _synchronized_dispose() { + super._synchronized_dispose() _queue = Queue(capacity: 0) } } @@ -231,34 +239,4 @@ class ReplayAll : ReplayManyBase { override func trim() { } -} - -class ReplaySubscription : Disposable { - typealias Subject = ReplaySubject - typealias DisposeKey = ReplayBufferBase.DisposeKey - - private var _lock = SpinLock() - - // state - private var _subject: Subject? - private var _disposeKey: DisposeKey? - - init(subject: Subject, disposeKey: DisposeKey) { - _subject = subject - _disposeKey = disposeKey - } - - func dispose() { - let oldState = _lock.calculateLocked { () -> (Subject?, DisposeKey?) in - let state = (self._subject, self._disposeKey) - self._subject = nil - self._disposeKey = nil - - return state - } - - if let subject = oldState.0, let disposeKey = oldState.1 { - subject.unsubscribe(disposeKey) - } - } -} +} \ No newline at end of file From 99a75f586e4664810602dbe82ced05c4a5f1c63f Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 1 Nov 2015 20:56:34 +0100 Subject: [PATCH 145/210] Remove `AsObservable` implementation. --- .../Implementations/AsObservable.swift | 42 ------------------- 1 file changed, 42 deletions(-) delete mode 100644 RxSwift/Observables/Implementations/AsObservable.swift diff --git a/RxSwift/Observables/Implementations/AsObservable.swift b/RxSwift/Observables/Implementations/AsObservable.swift deleted file mode 100644 index 134a252c..00000000 --- a/RxSwift/Observables/Implementations/AsObservable.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// AsObservable.swift -// Rx -// -// Created by Krunoslav Zaher on 2/27/15. -// Copyright (c) 2015 Krunoslav Zaher. All rights reserved. -// - -import Foundation - -class AsObservableSink : Sink, ObserverType { - typealias Element = O.E - - override init(observer: O, cancel: Disposable) { - super.init(observer: observer, cancel: cancel) - } - - func on(event: Event) { - observer?.on(event) - - switch event { - case .Error, .Completed: - dispose() - default: break - } - } -} - -class AsObservable : Producer { - - private let _source: Observable - - init(source: Observable) { - _source = source - } - - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = AsObservableSink(observer: observer, cancel: cancel) - setSink(sink) - return _source.subscribeSafe(sink) - } -} From 71ee2eeb347ee6144ca5f32c11f0c165ed6acd84 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 1 Nov 2015 21:42:25 +0100 Subject: [PATCH 146/210] Extends `ObservableType` instead of `ObservableConvertibleType`. --- RxSwift/Observables/Observable+Aggregate.swift | 2 +- RxSwift/Observables/Observable+Binding.swift | 10 +++++----- .../Observables/Observable+Concurrency.swift | 2 +- RxSwift/Observables/Observable+Debug.swift | 2 +- RxSwift/Observables/Observable+Multiple.swift | 18 +++++++++--------- RxSwift/Observables/Observable+Single.swift | 12 ++++++------ .../Observable+StandardSequenceOperators.swift | 18 +++++++++--------- RxSwift/Observables/Observable+Time.swift | 12 ++++++------ 8 files changed, 38 insertions(+), 38 deletions(-) diff --git a/RxSwift/Observables/Observable+Aggregate.swift b/RxSwift/Observables/Observable+Aggregate.swift index c2a93053..4f14ad5a 100644 --- a/RxSwift/Observables/Observable+Aggregate.swift +++ b/RxSwift/Observables/Observable+Aggregate.swift @@ -10,7 +10,7 @@ import Foundation // MARK: reduce -extension ObservableConvertibleType { +extension ObservableType { /** Applies an `accumulator` function over an observable sequence, returning the result of the aggregation as a single element in the result sequence. The specified `seed` value is used as the initial accumulator value. diff --git a/RxSwift/Observables/Observable+Binding.swift b/RxSwift/Observables/Observable+Binding.swift index 9ccad52e..c0912cd2 100644 --- a/RxSwift/Observables/Observable+Binding.swift +++ b/RxSwift/Observables/Observable+Binding.swift @@ -10,7 +10,7 @@ import Foundation // MARK: multicast -extension ObservableConvertibleType { +extension ObservableType { /** Multicasts the source sequence notifications through the specified subject to the resulting connectable observable. @@ -52,7 +52,7 @@ extension ObservableConvertibleType { // MARK: publish -extension ObservableConvertibleType { +extension ObservableType { /** Returns a connectable observable sequence that shares a single subscription to the underlying sequence. @@ -69,7 +69,7 @@ extension ObservableConvertibleType { // MARK: replay -extension ObservableConvertibleType { +extension ObservableType { /** Returns a connectable observable sequence that shares a single subscription to the underlying sequence replaying bufferSize elements. @@ -103,7 +103,7 @@ extension ConnectableObservableType { // MARK: share -extension ObservableConvertibleType { +extension ObservableType { /** Returns an observable sequence that shares a single subscription to the underlying sequence. @@ -120,7 +120,7 @@ extension ObservableConvertibleType { // MARK: shareReplay -extension ObservableConvertibleType { +extension ObservableType { /** Returns an observable sequence that shares a single subscription to the underlying sequence replaying notifications subject to a maximum time length for the replay buffer. diff --git a/RxSwift/Observables/Observable+Concurrency.swift b/RxSwift/Observables/Observable+Concurrency.swift index 1f5de591..2bf4de71 100644 --- a/RxSwift/Observables/Observable+Concurrency.swift +++ b/RxSwift/Observables/Observable+Concurrency.swift @@ -10,7 +10,7 @@ import Foundation // MARK: observeOn -extension ObservableConvertibleType { +extension ObservableType { /** Wraps the source sequence in order to run its observer callbacks on the specified scheduler. diff --git a/RxSwift/Observables/Observable+Debug.swift b/RxSwift/Observables/Observable+Debug.swift index 4090c993..41df23f8 100644 --- a/RxSwift/Observables/Observable+Debug.swift +++ b/RxSwift/Observables/Observable+Debug.swift @@ -10,7 +10,7 @@ import Foundation // MARK: debug -extension ObservableConvertibleType { +extension ObservableType { /** Prints received events for all observers on standard output. diff --git a/RxSwift/Observables/Observable+Multiple.swift b/RxSwift/Observables/Observable+Multiple.swift index bba42c2a..d6b4dd6c 100644 --- a/RxSwift/Observables/Observable+Multiple.swift +++ b/RxSwift/Observables/Observable+Multiple.swift @@ -42,7 +42,7 @@ extension CollectionType where Generator.Element : ObservableConvertibleType { // MARK: switch -extension ObservableConvertibleType where E : ObservableConvertibleType { +extension ObservableType where E : ObservableConvertibleType { /** Transforms an observable sequence of observable sequences into an observable sequence @@ -61,7 +61,7 @@ extension ObservableConvertibleType where E : ObservableConvertibleType { // MARK: concat -extension ObservableConvertibleType { +extension ObservableType { /** Concatenates the second observable sequence to `self` upon successful termination of `self`. @@ -89,7 +89,7 @@ extension SequenceType where Generator.Element : ObservableConvertibleType { } } -extension ObservableConvertibleType where E : ObservableConvertibleType { +extension ObservableType where E : ObservableConvertibleType { /** Concatenates all inner observable sequences, as long as the previous observable sequence terminated successfully. @@ -104,7 +104,7 @@ extension ObservableConvertibleType where E : ObservableConvertibleType { // MARK: merge -extension ObservableConvertibleType where E : ObservableConvertibleType { +extension ObservableType where E : ObservableConvertibleType { /** Merges elements from all observable sequences in the given enumerable sequence into a single observable sequence. @@ -131,7 +131,7 @@ extension ObservableConvertibleType where E : ObservableConvertibleType { // MARK: catch -extension ObservableConvertibleType { +extension ObservableType { /** Continues an observable sequence that is terminated by an error with the observable sequence produced by the handler. @@ -174,7 +174,7 @@ extension SequenceType where Generator.Element : ObservableConvertibleType { // MARK: takeUntil -extension ObservableConvertibleType { +extension ObservableType { /** Returns the elements from the source observable sequence until the other observable sequence produces an element. @@ -191,7 +191,7 @@ extension ObservableConvertibleType { // MARK: skipUntil -extension ObservableConvertibleType { +extension ObservableType { /** Returns the elements from the source observable sequence until the other observable sequence produces an element. @@ -208,7 +208,7 @@ extension ObservableConvertibleType { // MARK: amb -extension ObservableConvertibleType { +extension ObservableType { /** Propagates the observable sequence that reacts first. @@ -242,7 +242,7 @@ extension SequenceType where Generator.Element : ObservableConvertibleType { // withLatestFrom -extension ObservableConvertibleType { +extension ObservableType { /** Merges two observable sequences into one observable sequence by combining each element from self with the latest element from the second source, if any. diff --git a/RxSwift/Observables/Observable+Single.swift b/RxSwift/Observables/Observable+Single.swift index b1434a2b..4cf01d78 100644 --- a/RxSwift/Observables/Observable+Single.swift +++ b/RxSwift/Observables/Observable+Single.swift @@ -10,7 +10,7 @@ import Foundation // MARK: distinct until changed -extension ObservableConvertibleType where E: Equatable { +extension ObservableType where E: Equatable { /** Returns an observable sequence that contains only distinct contiguous elements according to equality operator. @@ -24,7 +24,7 @@ extension ObservableConvertibleType where E: Equatable { } } -extension ObservableConvertibleType { +extension ObservableType { /** Returns an observable sequence that contains only distinct contiguous elements according to the `keySelector`. @@ -65,7 +65,7 @@ extension ObservableConvertibleType { // MARK: do -extension ObservableConvertibleType { +extension ObservableType { /** Invokes an action for each event in the observable sequence, and propagates all observer messages through the result sequence. @@ -105,7 +105,7 @@ extension ObservableConvertibleType { // MARK: startWith -extension ObservableConvertibleType { +extension ObservableType { /** Prepends a sequence of values to an observable sequence. @@ -122,7 +122,7 @@ extension ObservableConvertibleType { // MARK: retry -extension ObservableConvertibleType { +extension ObservableType { /** Repeats the source observable sequence until it successfully terminates. @@ -153,7 +153,7 @@ extension ObservableConvertibleType { // MARK: scan -extension ObservableConvertibleType { +extension ObservableType { /** Applies an accumulator function over an observable sequence and returns each intermediate result. The specified seed value is used as the initial accumulator value. diff --git a/RxSwift/Observables/Observable+StandardSequenceOperators.swift b/RxSwift/Observables/Observable+StandardSequenceOperators.swift index 6ff01147..5278c3de 100644 --- a/RxSwift/Observables/Observable+StandardSequenceOperators.swift +++ b/RxSwift/Observables/Observable+StandardSequenceOperators.swift @@ -10,7 +10,7 @@ import Foundation // MARK: filter aka where -extension ObservableConvertibleType { +extension ObservableType { /** Filters the elements of an observable sequence based on a predicate. @@ -27,7 +27,7 @@ extension ObservableConvertibleType { // MARK: takeWhile -extension ObservableConvertibleType { +extension ObservableType { /** Returns elements from an observable sequence as long as a specified condition is true. @@ -58,7 +58,7 @@ extension ObservableConvertibleType { // MARK: take -extension ObservableConvertibleType { +extension ObservableType { /** Returns a specified number of contiguous elements from the start of an observable sequence. @@ -80,7 +80,7 @@ extension ObservableConvertibleType { // MARK: takeLast -extension ObservableConvertibleType { +extension ObservableType { /** Returns a specified number of contiguous elements from the end of an observable sequence. @@ -100,7 +100,7 @@ extension ObservableConvertibleType { // MARK: skip -extension ObservableConvertibleType { +extension ObservableType { /** Bypasses a specified number of elements in an observable sequence and then returns the remaining elements. @@ -117,7 +117,7 @@ extension ObservableConvertibleType { // MARK: SkipWhile -extension ObservableConvertibleType { +extension ObservableType { /** Bypasses elements in an observable sequence as long as a specified condition is true and then returns the remaining elements. @@ -145,7 +145,7 @@ extension ObservableConvertibleType { // MARK: map aka select -extension ObservableConvertibleType { +extension ObservableType { /** Projects each element of an observable sequence into a new form. @@ -174,7 +174,7 @@ extension ObservableConvertibleType { // MARK: flatMap -extension ObservableConvertibleType { +extension ObservableType { /** Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence. @@ -203,7 +203,7 @@ extension ObservableConvertibleType { // elementAt -extension ObservableConvertibleType { +extension ObservableType { /** Returns a sequence emitting only item _n_ emitted by an Observable diff --git a/RxSwift/Observables/Observable+Time.swift b/RxSwift/Observables/Observable+Time.swift index 262df3bf..6917b79a 100644 --- a/RxSwift/Observables/Observable+Time.swift +++ b/RxSwift/Observables/Observable+Time.swift @@ -9,7 +9,7 @@ import Foundation // MARK: throttle -extension ObservableConvertibleType { +extension ObservableType { /** Ignores elements from an observable sequence which are followed by another element within a specified relative time duration, using the specified scheduler to run throttling timers. @@ -44,7 +44,7 @@ extension ObservableConvertibleType { // MARK: sample -extension ObservableConvertibleType { +extension ObservableType { /** Samples the source observable sequence using a samper observable sequence producing sampling ticks. @@ -136,7 +136,7 @@ public func timer(dueTime: S.TimeInterval, _ scheduler: S) // MARK: take -extension ObservableConvertibleType { +extension ObservableType { /** Takes elements for the specified duration from the start of the observable source sequence, using the specified scheduler to run timers. @@ -154,7 +154,7 @@ extension ObservableConvertibleType { // MARK: skip -extension ObservableConvertibleType { +extension ObservableType { /** Skips elements for the specified duration from the start of the observable source sequence, using the specified scheduler to run timers. @@ -173,7 +173,7 @@ extension ObservableConvertibleType { // MARK: delaySubscription -extension ObservableConvertibleType { +extension ObservableType { /** Time shifts the observable sequence by delaying the subscription with the specified relative time duration, using the specified scheduler to run timers. @@ -191,7 +191,7 @@ extension ObservableConvertibleType { // MARK: buffer -extension ObservableConvertibleType { +extension ObservableType { /** Projects each element of an observable sequence into a buffer that's sent out when either it's full or a given amount of time has elapsed, using the specified scheduler to run timers. From dcfe2c5f0cdcf86543584027053a8e3796be5095 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 1 Nov 2015 21:58:57 +0100 Subject: [PATCH 147/210] Optimizations for `observeOn`. --- RxSwift/Disposables/ScheduledDisposable.swift | 10 +++++--- .../Implementations/ObserveOn.swift | 1 + .../ObserveOnSerialDispatchQueue.swift | 25 +++++++++++-------- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/RxSwift/Disposables/ScheduledDisposable.swift b/RxSwift/Disposables/ScheduledDisposable.swift index 8ad765e3..dea13edf 100644 --- a/RxSwift/Disposables/ScheduledDisposable.swift +++ b/RxSwift/Disposables/ScheduledDisposable.swift @@ -8,6 +8,11 @@ import Foundation +private let disposeScheduledDisposable: ScheduledDisposable -> Disposable = { sd in + sd.disposeInner() + return NopDisposable.instance +} + /** Represents a disposable resource whose disposal invocation will be scheduled on the specified scheduler. */ @@ -43,10 +48,7 @@ public class ScheduledDisposable : Cancelable { Disposes the wrapped disposable on the provided scheduler. */ public func dispose() { - scheduler.schedule(()) { - self.disposeInner() - return NopDisposable.instance - } + scheduler.schedule(self, action: disposeScheduledDisposable) } func disposeInner() { diff --git a/RxSwift/Observables/Implementations/ObserveOn.swift b/RxSwift/Observables/Implementations/ObserveOn.swift index 5eb1298d..074dded5 100644 --- a/RxSwift/Observables/Implementations/ObserveOn.swift +++ b/RxSwift/Observables/Implementations/ObserveOn.swift @@ -127,6 +127,7 @@ class ObserveOnSink : ObserverBase { _lock.lock(); defer { _lock.unlock() } // { _observer = nil + // } } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/ObserveOnSerialDispatchQueue.swift b/RxSwift/Observables/Implementations/ObserveOnSerialDispatchQueue.swift index 7d3f5675..4a6a4328 100644 --- a/RxSwift/Observables/Implementations/ObserveOnSerialDispatchQueue.swift +++ b/RxSwift/Observables/Implementations/ObserveOnSerialDispatchQueue.swift @@ -18,28 +18,31 @@ public var numberOfSerialDispatchQueueObservables: Int32 = 0 #endif class ObserveOnSerialDispatchQueueSink : ObserverBase { - let scheduler: SerialDispatchQueueScheduler let observer: O let subscription = SingleAssignmentDisposable() - + + var cachedScheduleLambda: ((ObserveOnSerialDispatchQueueSink, Event) -> Disposable)! + init(scheduler: SerialDispatchQueueScheduler, observer: O) { self.scheduler = scheduler self.observer = observer super.init() + + cachedScheduleLambda = { sink, event in + sink.observer.on(event) + + if event.isStopEvent { + sink.dispose() + } + + return NopDisposable.instance + } } override func onCore(event: Event) { - self.scheduler.schedule(()) { (_) -> Disposable in - self.observer.on(event) - - if event.isStopEvent { - self.dispose() - } - - return NopDisposable.instance - } + self.scheduler.schedule((self, event), action: cachedScheduleLambda) } override func dispose() { From cd9f7654d55638784d8603333ff4e1c47a899fab Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 1 Nov 2015 22:12:52 +0100 Subject: [PATCH 148/210] Sink modifications. --- .../Observables/Implementations/Sink.swift | 25 ++++++------------- RxTests/PerformanceTests/main.swift | 10 ++++---- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/RxSwift/Observables/Implementations/Sink.swift b/RxSwift/Observables/Implementations/Sink.swift index e6ce7278..43527b69 100644 --- a/RxSwift/Observables/Implementations/Sink.swift +++ b/RxSwift/Observables/Implementations/Sink.swift @@ -9,18 +9,20 @@ import Foundation class Sink : SingleAssignmentDisposable { - private var _lock = SpinLock() - // state - private var _observer: O? + private var _observer: O var observer: O? { get { - _lock.lock(); defer { _lock.unlock() } + //_lock.lock(); defer { _lock.unlock() } return _observer } } - + + func forward(event: Event) { + _observer.on(event) + } + init(observer: O) { #if TRACE_RESOURCES OSAtomicIncrement32(&resourceCount) @@ -28,19 +30,6 @@ class Sink : SingleAssignmentDisposable { _observer = observer } - private func _disposeObserver() { - _lock.lock(); defer { _lock.unlock() } - - _observer = nil - } - - override func dispose() { - if !disposed { - _disposeObserver() - } - super.dispose() - } - deinit { #if TRACE_RESOURCES OSAtomicDecrement32(&resourceCount) diff --git a/RxTests/PerformanceTests/main.swift b/RxTests/PerformanceTests/main.swift index 1d80a525..f2a0b7f1 100644 --- a/RxTests/PerformanceTests/main.swift +++ b/RxTests/PerformanceTests/main.swift @@ -24,14 +24,14 @@ compareTwoImplementations(benchmarkTime: true, first: { //let a = just(1) //combineLatest(a, - publishSubject + publishSubject//.asDriver(onErrorJustReturn: -1) .shareReplay(1) .map { $0 } - .filter { _ in true }// ){ x, _ in x } - //.map { $0 } - //.flatMap { just($0) } + .filter { _ in true }//){ x, _ in x } + .map { $0 } + .flatMap { just($0) } .subscribeNext { _ in - + } From 59f3b487d213caf7196a5a23ebfb1b12b560a7dc Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 2 Nov 2015 00:17:11 +0100 Subject: [PATCH 149/210] Removes locks from sink forwarding (25% improvement). --- .../Services/ReachabilityService.swift | 2 +- RxSwift/Observables/Implementations/Amb.swift | 4 +-- .../Implementations/AnonymousObservable.swift | 4 +-- .../Observables/Implementations/Buffer.swift | 8 +++--- .../Observables/Implementations/Catch.swift | 16 +++++------ .../CombineLatest+CollectionType.swift | 10 +++---- .../Implementations/CombineLatest.swift | 10 +++---- .../Observables/Implementations/Concat.swift | 4 +-- .../Observables/Implementations/Debug.swift | 2 +- .../Implementations/Deferred.swift | 4 +-- .../Implementations/DelaySubscription.swift | 2 +- .../DistinctUntilChanged.swift | 6 ++--- RxSwift/Observables/Implementations/Do.swift | 4 +-- .../Implementations/ElementAt.swift | 12 ++++----- .../Observables/Implementations/Filter.swift | 6 ++--- .../Observables/Implementations/FlatMap.swift | 12 ++++----- .../Implementations/Generate.swift | 6 ++--- RxSwift/Observables/Implementations/Map.swift | 8 +++--- .../Observables/Implementations/Merge.swift | 20 +++++++------- .../Implementations/Multicast.swift | 4 +-- .../Observables/Implementations/Range.swift | 4 +-- .../Observables/Implementations/Reduce.swift | 10 +++---- .../Implementations/RefCount.swift | 4 +-- .../Observables/Implementations/Repeat.swift | 2 +- .../Observables/Implementations/Sample.swift | 12 ++++----- .../Observables/Implementations/Scan.swift | 8 +++--- .../Implementations/ShareReplay1.swift | 22 ++++++++------- .../Observables/Implementations/Sink.swift | 17 +++++------- .../Observables/Implementations/Skip.swift | 12 ++++----- .../Implementations/SkipUntil.swift | 23 +++++----------- .../Implementations/SkipWhile.swift | 12 ++++----- .../Implementations/SubscribeOn.swift | 2 +- .../Observables/Implementations/Switch.swift | 12 ++++----- .../Observables/Implementations/Take.swift | 16 +++++------ .../Implementations/TakeLast.swift | 6 ++--- .../Implementations/TakeUntil.swift | 27 +++++++------------ .../Implementations/TakeWhile.swift | 16 +++++------ .../Implementations/Throttle.swift | 8 +++--- .../Observables/Implementations/Timer.swift | 6 ++--- .../Observables/Implementations/ToArray.swift | 6 ++--- .../Observables/Implementations/Using.swift | 6 ++--- .../Implementations/WithLatestFrom.swift | 10 +++---- .../Implementations/Zip+CollectionType.swift | 10 +++---- RxSwift/Observables/Implementations/Zip.swift | 10 +++---- RxSwift/ObserverType.swift | 2 +- RxSwift/Observers/TailRecursiveSink.swift | 2 +- .../RxSwiftTests/Tests/ObserverTests.swift | 2 +- 47 files changed, 195 insertions(+), 216 deletions(-) diff --git a/RxExample/RxExample/Services/ReachabilityService.swift b/RxExample/RxExample/Services/ReachabilityService.swift index 8b702585..098dfd0d 100644 --- a/RxExample/RxExample/Services/ReachabilityService.swift +++ b/RxExample/RxExample/Services/ReachabilityService.swift @@ -48,7 +48,7 @@ extension ObservableConvertibleType { } func retryOnBecomesReachable(valueOnFailure:E, reachabilityService:ReachabilityService, orExternalTrigger: Observable) -> Observable{ - return self + return self.asObservable() .catchError { (e) -> Observable in let retryBecauseOfNeworkAvailability = reachabilityService.reachabilityChanged .flatMap { event -> Observable in diff --git a/RxSwift/Observables/Implementations/Amb.swift b/RxSwift/Observables/Implementations/Amb.swift index 439cbd65..8c54f786 100644 --- a/RxSwift/Observables/Implementations/Amb.swift +++ b/RxSwift/Observables/Implementations/Amb.swift @@ -69,7 +69,7 @@ class AmbSink : Sink { let disposeAll = StableCompositeDisposable.create(subscription1, subscription2) let forwardEvent = { (o: AmbObserverType, event: Event) -> Void in - self.observer?.on(event) + self.forwardOn(event) } let decide = { (o: AmbObserverType, event: Event, me: AmbState, otherSubscription: Disposable) in @@ -82,7 +82,7 @@ class AmbSink : Sink { } if self._choice == me { - self.observer?.on(event) + self.forwardOn(event) if event.isStopEvent { self.dispose() } diff --git a/RxSwift/Observables/Implementations/AnonymousObservable.swift b/RxSwift/Observables/Implementations/AnonymousObservable.swift index 506f8b31..eb23374a 100644 --- a/RxSwift/Observables/Implementations/AnonymousObservable.swift +++ b/RxSwift/Observables/Implementations/AnonymousObservable.swift @@ -26,10 +26,10 @@ class AnonymousObservableSink : Sink, ObserverType { if _isStopped == 1 { return } - observer?.on(event) + forwardOn(event) case .Error, .Completed: if OSAtomicCompareAndSwap32(0, 1, &_isStopped) { - self.observer?.on(event) + self.forwardOn(event) self.dispose() } } diff --git a/RxSwift/Observables/Implementations/Buffer.swift b/RxSwift/Observables/Implementations/Buffer.swift index 1c932ed3..b941e64f 100644 --- a/RxSwift/Observables/Implementations/Buffer.swift +++ b/RxSwift/Observables/Implementations/Buffer.swift @@ -62,7 +62,7 @@ class BufferTimeCountSink : ObserverType { } func on(event: Event) { - _parent.observer?.on(event) + _parent.forwardOn(event) switch event { case .Next: @@ -55,9 +55,9 @@ class CatchSink : Sink, ObserverType { func on(event: Event) { switch event { case .Next: - observer?.on(event) + forwardOn(event) case .Completed: - observer?.on(event) + forwardOn(event) dispose() case .Error(let error): do { @@ -68,7 +68,7 @@ class CatchSink : Sink, ObserverType { _subscription.disposable = catchSequence.subscribe(observer) } catch let e { - observer?.on(.Error(e)) + forwardOn(.Error(e)) dispose() } } @@ -108,22 +108,22 @@ class CatchSequenceSink) { switch event { case .Next: - observer?.on(event) + forwardOn(event) case .Error(let error): _lastError = error scheduleMoveNext() case .Completed: - observer?.on(event) + forwardOn(event) dispose() } } override func done() { if let lastError = _lastError { - observer?.on(.Error(lastError)) + forwardOn(.Error(lastError)) } else { - observer?.on(.Completed) + forwardOn(.Completed) } self.dispose() diff --git a/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift b/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift index e1e2bb82..e91669b0 100644 --- a/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift +++ b/RxSwift/Observables/Implementations/CombineLatest+CollectionType.swift @@ -51,7 +51,7 @@ class CombineLatestCollectionTypeSink if _numberOfValues == _arity { do { let result = try getResult() - observer?.on(.Next(result)) + forwardOn(.Next(result)) } catch let e { - observer?.on(.Error(e)) + forwardOn(.Error(e)) dispose() } } @@ -66,14 +66,14 @@ class CombineLatestSink } if allOthersDone { - observer?.on(.Completed) + forwardOn(.Completed) dispose() } } } func fail(error: ErrorType) { - observer?.on(.Error(error)) + forwardOn(.Error(error)) dispose() } @@ -86,7 +86,7 @@ class CombineLatestSink _numberOfDone++ if _numberOfDone == _arity { - observer?.on(.Completed) + forwardOn(.Completed) dispose() } } diff --git a/RxSwift/Observables/Implementations/Concat.swift b/RxSwift/Observables/Implementations/Concat.swift index c9aca4e8..9aac20b0 100644 --- a/RxSwift/Observables/Implementations/Concat.swift +++ b/RxSwift/Observables/Implementations/Concat.swift @@ -19,9 +19,9 @@ class ConcatSink){ switch event { case .Next: - observer?.on(event) + forwardOn(event) case .Error: - observer?.on(event) + forwardOn(event) dispose() case .Completed: scheduleMoveNext() diff --git a/RxSwift/Observables/Implementations/Debug.swift b/RxSwift/Observables/Implementations/Debug.swift index 090849cd..8b125f9c 100644 --- a/RxSwift/Observables/Implementations/Debug.swift +++ b/RxSwift/Observables/Implementations/Debug.swift @@ -26,7 +26,7 @@ class Debug_ : Sink, ObserverType { ? String(eventText.characters.prefix(maxEventTextLength / 2)) + "..." + String(eventText.characters.suffix(maxEventTextLength / 2)) : eventText print("[\(_parent._identifier)] -> Event \(eventNormalized)") - observer?.on(event) + forwardOn(event) } override func dispose() { diff --git a/RxSwift/Observables/Implementations/Deferred.swift b/RxSwift/Observables/Implementations/Deferred.swift index 92b71788..7e526338 100644 --- a/RxSwift/Observables/Implementations/Deferred.swift +++ b/RxSwift/Observables/Implementations/Deferred.swift @@ -25,14 +25,14 @@ class DeferredSink : Sink, ObserverType { return result.subscribe(self) } catch let e { - observer?.on(.Error(e)) + forwardOn(.Error(e)) dispose() return NopDisposable.instance } } func on(event: Event) { - observer?.on(event) + forwardOn(event) switch event { case .Next: diff --git a/RxSwift/Observables/Implementations/DelaySubscription.swift b/RxSwift/Observables/Implementations/DelaySubscription.swift index 147fd032..755a07f0 100644 --- a/RxSwift/Observables/Implementations/DelaySubscription.swift +++ b/RxSwift/Observables/Implementations/DelaySubscription.swift @@ -22,7 +22,7 @@ class DelaySubscriptionSink) { - observer?.on(event) + forwardOn(event) if event.isStopEvent { dispose() } diff --git a/RxSwift/Observables/Implementations/DistinctUntilChanged.swift b/RxSwift/Observables/Implementations/DistinctUntilChanged.swift index d0ab70cf..46b75b98 100644 --- a/RxSwift/Observables/Implementations/DistinctUntilChanged.swift +++ b/RxSwift/Observables/Implementations/DistinctUntilChanged.swift @@ -37,14 +37,14 @@ class DistinctUntilChangedSink: Sink, ObserverType { _currentKey = key - observer?.on(event) + forwardOn(event) } catch let error { - observer?.on(.Error(error)) + forwardOn(.Error(error)) dispose() } case .Error, .Completed: - observer?.on(event) + forwardOn(event) dispose() } } diff --git a/RxSwift/Observables/Implementations/Do.swift b/RxSwift/Observables/Implementations/Do.swift index 5f163cf6..0680c9ab 100644 --- a/RxSwift/Observables/Implementations/Do.swift +++ b/RxSwift/Observables/Implementations/Do.swift @@ -22,13 +22,13 @@ class DoSink : Sink, ObserverType { func on(event: Event) { do { try _parent._eventHandler(event) - observer?.on(event) + forwardOn(event) if event.isStopEvent { dispose() } } catch let error { - observer?.on(.Error(error)) + forwardOn(.Error(error)) dispose() } } diff --git a/RxSwift/Observables/Implementations/ElementAt.swift b/RxSwift/Observables/Implementations/ElementAt.swift index 19401b2c..4360afb1 100644 --- a/RxSwift/Observables/Implementations/ElementAt.swift +++ b/RxSwift/Observables/Implementations/ElementAt.swift @@ -27,27 +27,27 @@ class ElementAtSink : Sink< case .Next(_): if (_i == 0) { - observer?.on(event) - observer?.on(.Completed) + forwardOn(event) + forwardOn(.Completed) self.dispose() } do { try decrementChecked(&_i) } catch(let e) { - observer?.onError(e) + forwardOn(.Error(e)) dispose() return } case .Error(let e): - observer?.on(.Error(e)) + forwardOn(.Error(e)) self.dispose() case .Completed: if (_parent._throwOnEmpty) { - observer?.onError(RxError.ArgumentOutOfRange) + forwardOn(.Error(RxError.ArgumentOutOfRange)) } else { - observer?.on(.Completed) + forwardOn(.Completed) } self.dispose() diff --git a/RxSwift/Observables/Implementations/Filter.swift b/RxSwift/Observables/Implementations/Filter.swift index 8efda19b..3f3f0133 100644 --- a/RxSwift/Observables/Implementations/Filter.swift +++ b/RxSwift/Observables/Implementations/Filter.swift @@ -26,15 +26,15 @@ class FilterSink: Sink, ObserverType { do { let satisfies = try _parent._predicate(value) if satisfies { - observer?.on(.Next(value)) + forwardOn(.Next(value)) } } catch let e { - observer?.on(.Error(e)) + forwardOn(.Error(e)) dispose() } case .Completed, .Error: - observer?.on(event) + forwardOn(event) dispose() } } diff --git a/RxSwift/Observables/Implementations/FlatMap.swift b/RxSwift/Observables/Implementations/FlatMap.swift index f9383df3..a00a6209 100644 --- a/RxSwift/Observables/Implementations/FlatMap.swift +++ b/RxSwift/Observables/Implementations/FlatMap.swift @@ -28,11 +28,11 @@ class FlatMapSinkIter : Sink { if try self._parent._condition(self._state) { let result = try self._parent._resultSelector(self._state) - self.observer?.on(.Next(result)) + self.forwardOn(.Next(result)) recurse(false) } else { - self.observer?.on(.Completed) + self.forwardOn(.Completed) self.dispose() } } catch let error { - self.observer?.on(.Error(error)) + self.forwardOn(.Error(error)) self.dispose() } } diff --git a/RxSwift/Observables/Implementations/Map.swift b/RxSwift/Observables/Implementations/Map.swift index e7817cbe..66424cba 100644 --- a/RxSwift/Observables/Implementations/Map.swift +++ b/RxSwift/Observables/Implementations/Map.swift @@ -31,17 +31,17 @@ class MapSink : Sink, ObserverType { case .Next(let element): do { let mappedElement = try performMap(element) - observer?.on(.Next(mappedElement)) + forwardOn(.Next(mappedElement)) } catch let e { - observer?.on(.Error(e)) + forwardOn(.Error(e)) dispose() } case .Error(let error): - observer?.on(.Error(error)) + forwardOn(.Error(error)) dispose() case .Completed: - observer?.on(.Completed) + forwardOn(.Completed) dispose() } } diff --git a/RxSwift/Observables/Implementations/Merge.swift b/RxSwift/Observables/Implementations/Merge.swift index a0b996ba..d2d97e5b 100644 --- a/RxSwift/Observables/Implementations/Merge.swift +++ b/RxSwift/Observables/Implementations/Merge.swift @@ -37,15 +37,15 @@ class MergeSinkIter) { switch event { case .Next: - _parent.observer?.on(event) + _parent.forwardOn(event) case .Error: - _parent.observer?.on(event) + _parent.forwardOn(event) _parent.dispose() case .Completed: _parent._group.removeDisposable(_disposeKey) if _parent._stopped && _parent._group.count == 1 { - _parent.observer?.on(.Completed) + _parent.forwardOn(.Completed) _parent.dispose() } } @@ -107,13 +107,13 @@ class MergeSink case .Next: rxFatalError("Next should have been handled") case .Error(let error): - observer?.on(.Error(error)) + forwardOn(.Error(error)) dispose() case .Completed: _stopped = true if _group.count == 1 { - observer?.on(.Completed) + forwardOn(.Completed) dispose() } else { @@ -152,9 +152,9 @@ class MergeConcurrentSinkIter) { switch event { case .Next: - _parent.observer?.on(event) + _parent.forwardOn(event) case .Error: - _parent.observer?.on(event) + _parent.forwardOn(event) _parent.dispose() case .Completed: _parent._group.removeDisposable(_disposeKey) @@ -167,7 +167,7 @@ class MergeConcurrentSinkIter: Sink, ObserverType { return BinaryDisposable(subscription, connection) } catch let e { - observer?.on(.Error(e)) + forwardOn(.Error(e)) dispose() return NopDisposable.instance } } func on(event: Event) { - observer?.on(event) + forwardOn(event) switch event { case .Next: break case .Error, .Completed: diff --git a/RxSwift/Observables/Implementations/Range.swift b/RxSwift/Observables/Implementations/Range.swift index 8093416a..17277244 100644 --- a/RxSwift/Observables/Implementations/Range.swift +++ b/RxSwift/Observables/Implementations/Range.swift @@ -47,11 +47,11 @@ class RangeSink<_CompilerWorkaround, O: ObserverType where O.E == Int> : Sink func run() -> Disposable { return _parent._scheduler.scheduleRecursive(0) { i, recurse in if i < self._parent._count { - self.observer?.on(.Next(self._parent._start + i)) + self.forwardOn(.Next(self._parent._start + i)) recurse(i + 1) } else { - self.observer?.on(.Completed) + self.forwardOn(.Completed) self.dispose() } } diff --git a/RxSwift/Observables/Implementations/Reduce.swift b/RxSwift/Observables/Implementations/Reduce.swift index 3276b326..792f3cd6 100644 --- a/RxSwift/Observables/Implementations/Reduce.swift +++ b/RxSwift/Observables/Implementations/Reduce.swift @@ -29,21 +29,21 @@ class ReduceSink : Sink, Observe _accumulation = try _parent._accumulator(_accumulation, value) } catch let e { - observer?.on(.Error(e)) + forwardOn(.Error(e)) dispose() } case .Error(let e): - observer?.on(.Error(e)) + forwardOn(.Error(e)) dispose() case .Completed: do { let result = try _parent._mapResult(_accumulation) - observer?.on(.Next(result)) - observer?.on(.Completed) + forwardOn(.Next(result)) + forwardOn(.Completed) dispose() } catch let e { - observer?.on(.Error(e)) + forwardOn(.Error(e)) dispose() } } diff --git a/RxSwift/Observables/Implementations/RefCount.swift b/RxSwift/Observables/Implementations/RefCount.swift index 4216aefa..904c02c7 100644 --- a/RxSwift/Observables/Implementations/RefCount.swift +++ b/RxSwift/Observables/Implementations/RefCount.swift @@ -55,9 +55,9 @@ class RefCountSink) { switch event { case .Next: - observer?.on(event) + forwardOn(event) case .Error, .Completed: - observer?.on(event) + forwardOn(event) dispose() } } diff --git a/RxSwift/Observables/Implementations/Repeat.swift b/RxSwift/Observables/Implementations/Repeat.swift index 8655d929..13b9cc72 100644 --- a/RxSwift/Observables/Implementations/Repeat.swift +++ b/RxSwift/Observables/Implementations/Repeat.swift @@ -37,7 +37,7 @@ class RepeatElementSink : Sink { func run() -> Disposable { return _parent._scheduler.scheduleRecursive(_parent._element) { e, recurse in - self.observer?.on(.Next(e)) + self.forwardOn(.Next(e)) recurse(e) } } diff --git a/RxSwift/Observables/Implementations/Sample.swift b/RxSwift/Observables/Implementations/Sample.swift index 703b86e1..3c9c7bb4 100644 --- a/RxSwift/Observables/Implementations/Sample.swift +++ b/RxSwift/Observables/Implementations/Sample.swift @@ -38,23 +38,23 @@ class SamplerSink case .Next(let element): _element = element case .Error: - observer?.on(event) + forwardOn(event) dispose() case .Completed: _atEnd = true diff --git a/RxSwift/Observables/Implementations/Scan.swift b/RxSwift/Observables/Implementations/Scan.swift index 9f0f5feb..406fbd5a 100644 --- a/RxSwift/Observables/Implementations/Scan.swift +++ b/RxSwift/Observables/Implementations/Scan.swift @@ -26,17 +26,17 @@ class ScanSink case .Next(let element): do { _accumulate = try _parent._accumulator(_accumulate, element) - observer?.on(.Next(_accumulate)) + forwardOn(.Next(_accumulate)) } catch let error { - observer?.on(.Error(error)) + forwardOn(.Error(error)) dispose() } case .Error(let error): - observer?.on(.Error(error)) + forwardOn(.Error(error)) dispose() case .Completed: - observer?.on(.Completed) + forwardOn(.Completed) dispose() } } diff --git a/RxSwift/Observables/Implementations/ShareReplay1.swift b/RxSwift/Observables/Implementations/ShareReplay1.swift index ba478eea..0707b840 100644 --- a/RxSwift/Observables/Implementations/ShareReplay1.swift +++ b/RxSwift/Observables/Implementations/ShareReplay1.swift @@ -23,7 +23,7 @@ class ShareReplay1 let _lock = NSRecursiveLock() - private var _subscription: Disposable? + private var _connection: SingleAssignmentDisposable? private var _element: Element? private var _stopEvent = nil as Event? private var _observers = Bag>() @@ -32,7 +32,6 @@ class ShareReplay1 self._source = source } - override func subscribe(observer: O) -> Disposable { return synchronizedSubscribe(observer) } @@ -52,7 +51,10 @@ class ShareReplay1 let disposeKey = self._observers.insert(AnyObserver(observer)) if initialCount == 0 { - self._subscription = self._source.subscribe(self) + let connection = SingleAssignmentDisposable() + _connection = connection + + connection.disposable = self._source.subscribe(self) } return SubscriptionDisposable(owner: self, key: disposeKey) @@ -64,9 +66,9 @@ class ShareReplay1 return } - if self._observers.count == 0 { - self._subscription?.dispose() - self._subscription = nil + if _observers.count == 0 { + _connection?.dispose() + _connection = nil } } @@ -75,16 +77,18 @@ class ShareReplay1 } func _synchronized_on(event: Event) { - if self._stopEvent != nil { + if _stopEvent != nil { return } if case .Next(let element) = event { - self._element = element + _element = element } if event.isStopEvent { - self._stopEvent = event + _stopEvent = event + _connection?.dispose() + _connection = nil } _observers.on(event) diff --git a/RxSwift/Observables/Implementations/Sink.swift b/RxSwift/Observables/Implementations/Sink.swift index 43527b69..debaf0aa 100644 --- a/RxSwift/Observables/Implementations/Sink.swift +++ b/RxSwift/Observables/Implementations/Sink.swift @@ -9,25 +9,20 @@ import Foundation class Sink : SingleAssignmentDisposable { - // state - private var _observer: O + let observer: O - var observer: O? { - get { - //_lock.lock(); defer { _lock.unlock() } - return _observer + func forwardOn(event: Event) { + if disposed { + return } - } - - func forward(event: Event) { - _observer.on(event) + observer.on(event) } init(observer: O) { #if TRACE_RESOURCES OSAtomicIncrement32(&resourceCount) #endif - _observer = observer + self.observer = observer } deinit { diff --git a/RxSwift/Observables/Implementations/Skip.swift b/RxSwift/Observables/Implementations/Skip.swift index 8d4ddc53..5c8095d5 100644 --- a/RxSwift/Observables/Implementations/Skip.swift +++ b/RxSwift/Observables/Implementations/Skip.swift @@ -29,16 +29,16 @@ class SkipCountSink : Sin case .Next(let value): if remaining <= 0 { - observer?.on(.Next(value)) + forwardOn(.Next(value)) } else { remaining-- } case .Error: - observer?.on(event) + forwardOn(event) self.dispose() case .Completed: - observer?.on(event) + forwardOn(event) self.dispose() } } @@ -82,13 +82,13 @@ class SkipTimeSink : Sin do { _running = try !_parent._predicate(value) } catch let e { - observer?.onError(e) + forwardOn(.Error(e)) dispose() return } } if _running { - observer?.onNext(value) + forwardOn(.Next(value)) } case .Error, .Completed: - observer?.on(event) + forwardOn(event) dispose() } } @@ -64,17 +64,17 @@ class SkipWhileSinkWithIndex : S } func on(event: Event) { - observer?.on(event) + forwardOn(event) if event.isStopEvent { self.dispose() diff --git a/RxSwift/Observables/Implementations/Switch.swift b/RxSwift/Observables/Implementations/Switch.swift index db5b7662..e6048649 100644 --- a/RxSwift/Observables/Implementations/Switch.swift +++ b/RxSwift/Observables/Implementations/Switch.swift @@ -57,7 +57,7 @@ class SwitchSink let disposable = observable.asObservable().subscribe(observer) d.disposable = disposable case .Error(let error): - observer?.on(.Error(error)) + forwardOn(.Error(error)) dispose() case .Completed: _stopped = true @@ -65,7 +65,7 @@ class SwitchSink _subscriptions.dispose() if !_hasLatest { - observer?.on(.Completed) + forwardOn(.Completed) dispose() } } @@ -108,18 +108,16 @@ class SwitchSinkIter : Sin if _remaining > 0 { _remaining-- - observer?.on(.Next(value)) + forwardOn(.Next(value)) if _remaining == 0 { - observer?.on(.Completed) + forwardOn(.Completed) dispose() } } case .Error: - observer?.on(event) + forwardOn(event) dispose() case .Completed: - observer?.on(event) + forwardOn(event) dispose() } } @@ -94,12 +94,12 @@ class TakeTimeSink) { switch event { case .Next(let value): - observer?.on(.Next(value)) + forwardOn(.Next(value)) case .Error: - observer?.on(event) + forwardOn(event) dispose() case .Completed: - observer?.on(event) + forwardOn(event) dispose() } } @@ -107,7 +107,7 @@ class TakeTimeSink : Sink _elements.dequeue() } case .Error: - observer?.on(event) + forwardOn(event) dispose() case .Completed: for e in _elements { - observer?.on(.Next(e)) + forwardOn(.Next(e)) } - observer?.on(.Completed) + forwardOn(.Completed) dispose() } } diff --git a/RxSwift/Observables/Implementations/TakeUntil.swift b/RxSwift/Observables/Implementations/TakeUntil.swift index 21f492d1..4820b720 100644 --- a/RxSwift/Observables/Implementations/TakeUntil.swift +++ b/RxSwift/Observables/Implementations/TakeUntil.swift @@ -21,16 +21,7 @@ class TakeUntilSinkOther) { switch event { case .Next: - _parent.observer?.on(.Completed) + _parent.forwardOn(.Completed) _parent.dispose() case .Error(let e): - _parent.observer?.on(.Error(e)) + _parent.forwardOn(.Error(e)) _parent.dispose() case .Completed: _parent._open = true - _singleAssignmentDisposable.dispose() + _subscription.dispose() } } @@ -91,12 +82,12 @@ class TakeUntilSink) { switch event { case .Next: - observer?.on(event) + forwardOn(event) case .Error: - observer?.on(event) + forwardOn(event) dispose() case .Completed: - observer?.on(event) + forwardOn(event) dispose() } } @@ -104,10 +95,10 @@ class TakeUntilSink Disposable { let otherObserver = TakeUntilSinkOther(parent: self) let otherSubscription = _parent._other.subscribe(otherObserver) - otherObserver.disposable = otherSubscription + otherObserver._subscription.disposable = otherSubscription let sourceSubscription = _parent._source.subscribe(self) - return StableCompositeDisposable.create(sourceSubscription, otherSubscription) + return StableCompositeDisposable.create(sourceSubscription, otherObserver._subscription) } } diff --git a/RxSwift/Observables/Implementations/TakeWhile.swift b/RxSwift/Observables/Implementations/TakeWhile.swift index 80dadf94..7e395564 100644 --- a/RxSwift/Observables/Implementations/TakeWhile.swift +++ b/RxSwift/Observables/Implementations/TakeWhile.swift @@ -33,19 +33,19 @@ class TakeWhileSink do { _running = try _parent._predicate(value) } catch let e { - observer?.onError(e) + forwardOn(.Error(e)) dispose() return } if _running { - observer?.onNext(value) + forwardOn(.Next(value)) } else { - observer?.onComplete() + forwardOn(.Completed) dispose() } case .Error, .Completed: - observer?.on(event) + forwardOn(event) dispose() } } @@ -79,19 +79,19 @@ class TakeWhileSinkWithIndex d.disposable = scheduler.scheduleRelative(currentId, dueTime: dueTime, action: self.propagate) case .Error: _value = nil - observer?.on(event) + forwardOn(event) dispose() case .Completed: if let value = _value { _value = nil - observer?.on(.Next(value)) + forwardOn(.Next(value)) } - observer?.on(.Completed) + forwardOn(.Completed) dispose() } } @@ -76,7 +76,7 @@ class ThrottleSink if let value = originalValue where _id == currentId { _value = nil - observer?.on(.Next(value)) + forwardOn(.Next(value)) } // } return NopDisposable.instance diff --git a/RxSwift/Observables/Implementations/Timer.swift b/RxSwift/Observables/Implementations/Timer.swift index f6b7d724..d334d0c4 100644 --- a/RxSwift/Observables/Implementations/Timer.swift +++ b/RxSwift/Observables/Implementations/Timer.swift @@ -20,7 +20,7 @@ class TimerSink : Sink func run() -> Disposable { return _parent._scheduler.schedulePeriodic(0 as Int64, startAfter: _parent._dueTime, period: _parent._period!) { state in - self.observer?.on(.Next(state)) + self.forwardOn(.Next(state)) return state &+ 1 } } @@ -38,8 +38,8 @@ class TimerOneOffSink : Si func run() -> Disposable { return _parent._scheduler.scheduleRelative((), dueTime: _parent._dueTime) { (_) -> Disposable in - self.observer?.on(.Next(0)) - self.observer?.on(.Completed) + self.forwardOn(.Next(0)) + self.forwardOn(.Completed) return NopDisposable.instance } diff --git a/RxSwift/Observables/Implementations/ToArray.swift b/RxSwift/Observables/Implementations/ToArray.swift index 1b514d26..bac43dbd 100644 --- a/RxSwift/Observables/Implementations/ToArray.swift +++ b/RxSwift/Observables/Implementations/ToArray.swift @@ -25,11 +25,11 @@ class ToArraySink : Sink< case .Next(let value): self._list.append(value) case .Error(let e): - observer?.on(.Error(e)) + forwardOn(.Error(e)) self.dispose() case .Completed: - observer?.on(.Next(_list)) - observer?.on(.Completed) + forwardOn(.Next(_list)) + forwardOn(.Completed) self.dispose() } } diff --git a/RxSwift/Observables/Implementations/Using.swift b/RxSwift/Observables/Implementations/Using.swift index ed4a891a..628167f3 100644 --- a/RxSwift/Observables/Implementations/Using.swift +++ b/RxSwift/Observables/Implementations/Using.swift @@ -43,12 +43,12 @@ class UsingSink) { switch event { case let .Next(value): - observer?.onNext(value) + forwardOn(.Next(value)) case let .Error(error): - observer?.onError(error) + forwardOn(.Error(error)) dispose() case .Completed: - observer?.onComplete() + forwardOn(.Completed) dispose() } } diff --git a/RxSwift/Observables/Implementations/WithLatestFrom.swift b/RxSwift/Observables/Implementations/WithLatestFrom.swift index 685c56d7..a312456b 100644 --- a/RxSwift/Observables/Implementations/WithLatestFrom.swift +++ b/RxSwift/Observables/Implementations/WithLatestFrom.swift @@ -49,16 +49,16 @@ class WithLatestFromSink : Sink, ZipSinkProtocol { if hasValueAll { do { let result = try getResult() - self.observer?.on(.Next(result)) + self.forwardOn(.Next(result)) } catch let e { - self.observer?.on(.Error(e)) + self.forwardOn(.Error(e)) dispose() } } @@ -72,14 +72,14 @@ class ZipSink : Sink, ZipSinkProtocol { } if allOthersDone { - observer?.on(.Completed) + forwardOn(.Completed) self.dispose() } } } func fail(error: ErrorType) { - observer?.on(.Error(error)) + forwardOn(.Error(error)) dispose() } @@ -96,7 +96,7 @@ class ZipSink : Sink, ZipSinkProtocol { } if allDone { - observer?.on(.Completed) + forwardOn(.Completed) dispose() } } diff --git a/RxSwift/ObserverType.swift b/RxSwift/ObserverType.swift index e609fff7..642f75fd 100644 --- a/RxSwift/ObserverType.swift +++ b/RxSwift/ObserverType.swift @@ -42,7 +42,7 @@ public extension ObserverType { /** Convienence method equivalent to `on(.Completed)` */ - final func onComplete() { + final func onCompleted() { on(.Completed) } diff --git a/RxSwift/Observers/TailRecursiveSink.swift b/RxSwift/Observers/TailRecursiveSink.swift index c4021064..c650b89b 100644 --- a/RxSwift/Observers/TailRecursiveSink.swift +++ b/RxSwift/Observers/TailRecursiveSink.swift @@ -49,7 +49,7 @@ class TailRecursiveSink Date: Mon, 2 Nov 2015 00:41:01 +0100 Subject: [PATCH 150/210] Makes `observer` in `Sink` private again. --- .../Observables/Implementations/DistinctUntilChanged.swift | 2 -- RxSwift/Observables/Implementations/FlatMap.swift | 2 -- RxSwift/Observables/Implementations/Map.swift | 2 -- RxSwift/Observables/Implementations/Sink.swift | 6 +++--- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/RxSwift/Observables/Implementations/DistinctUntilChanged.swift b/RxSwift/Observables/Implementations/DistinctUntilChanged.swift index 46b75b98..ae9f6192 100644 --- a/RxSwift/Observables/Implementations/DistinctUntilChanged.swift +++ b/RxSwift/Observables/Implementations/DistinctUntilChanged.swift @@ -20,8 +20,6 @@ class DistinctUntilChangedSink: Sink, ObserverType { } func on(event: Event) { - let observer = super.observer - switch event { case .Next(let value): do { diff --git a/RxSwift/Observables/Implementations/FlatMap.swift b/RxSwift/Observables/Implementations/FlatMap.swift index a00a6209..458c7cef 100644 --- a/RxSwift/Observables/Implementations/FlatMap.swift +++ b/RxSwift/Observables/Implementations/FlatMap.swift @@ -77,8 +77,6 @@ class FlatMapSink) { - let observer = super.observer - switch event { case .Next(let element): do { diff --git a/RxSwift/Observables/Implementations/Map.swift b/RxSwift/Observables/Implementations/Map.swift index 66424cba..20c6bd1a 100644 --- a/RxSwift/Observables/Implementations/Map.swift +++ b/RxSwift/Observables/Implementations/Map.swift @@ -25,8 +25,6 @@ class MapSink : Sink, ObserverType { } func on(event: Event) { - let observer = super.observer - switch event { case .Next(let element): do { diff --git a/RxSwift/Observables/Implementations/Sink.swift b/RxSwift/Observables/Implementations/Sink.swift index debaf0aa..fec16dd0 100644 --- a/RxSwift/Observables/Implementations/Sink.swift +++ b/RxSwift/Observables/Implementations/Sink.swift @@ -9,20 +9,20 @@ import Foundation class Sink : SingleAssignmentDisposable { - let observer: O + private let _observer: O func forwardOn(event: Event) { if disposed { return } - observer.on(event) + _observer.on(event) } init(observer: O) { #if TRACE_RESOURCES OSAtomicIncrement32(&resourceCount) #endif - self.observer = observer + _observer = observer } deinit { From a1cd43dfe88edafbb5dafc530533d39c8c6d5405 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 2 Nov 2015 10:17:39 +0100 Subject: [PATCH 151/210] Updates documentation. --- RxCocoa/Common/CocoaUnits/Driver/Driver.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver.swift index 7a95e627..2536d8de 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/Driver.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver.swift @@ -40,7 +40,6 @@ extension DriverConvertibleType { - it's stateful, upon subscription (calling subscribe) last element is immediatelly replayed if it was produced - computation of elements is reference counted with respect to the number of observers - if there are no subscribers, it will release sequence computation resources - - it uses lockless versions of optimized operators (main dispatch queue is used as shared lock) `Driver` can be considered a builder pattern for observable sequences that drive the application. From e52b2c94493478fdbaa1e8029485161b4a8b486e Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 2 Nov 2015 10:31:38 +0100 Subject: [PATCH 152/210] Changes `AnonymousObservable` access modifier to `private`. --- RxCocoa/OSX/NSControl+Rx.swift | 4 ++-- RxCocoa/iOS/UIBarButtonItem+Rx.swift | 2 +- RxCocoa/iOS/UIControl+Rx.swift | 4 ++-- RxCocoa/iOS/UIGestureRecognizer+Rx.swift | 2 +- .../Implementations/AnonymousObservable.swift | 12 ++++++------ 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/RxCocoa/OSX/NSControl+Rx.swift b/RxCocoa/OSX/NSControl+Rx.swift index 85b79764..bb7c76f6 100644 --- a/RxCocoa/OSX/NSControl+Rx.swift +++ b/RxCocoa/OSX/NSControl+Rx.swift @@ -24,7 +24,7 @@ extension NSControl { MainScheduler.ensureExecutingOnScheduler() let source = rx_lazyInstanceObservable(&rx_control_events_key) { () -> Observable in - AnonymousObservable { [weak self] observer in + create { [weak self] observer in MainScheduler.ensureExecutingOnScheduler() guard let control = self else { @@ -63,7 +63,7 @@ extension NSControl { MainScheduler.ensureExecutingOnScheduler() let source = rx_lazyInstanceObservable(&rx_value_key) { () -> Observable in - return AnonymousObservable { [weak self] observer in + return create { [weak self] observer in guard let control = self else { observer.on(.Completed) return NopDisposable.instance diff --git a/RxCocoa/iOS/UIBarButtonItem+Rx.swift b/RxCocoa/iOS/UIBarButtonItem+Rx.swift index 34952a29..17c496af 100644 --- a/RxCocoa/iOS/UIBarButtonItem+Rx.swift +++ b/RxCocoa/iOS/UIBarButtonItem+Rx.swift @@ -38,7 +38,7 @@ extension UIBarButtonItem { Reactive wrapper for target action pattern on `self`. */ public var rx_tap: ControlEvent { - let source: Observable = AnonymousObservable { [weak self] observer in + let source: Observable = create { [weak self] observer in guard let control = self else { observer.on(.Completed) diff --git a/RxCocoa/iOS/UIControl+Rx.swift b/RxCocoa/iOS/UIControl+Rx.swift index c72465c8..82371b10 100644 --- a/RxCocoa/iOS/UIControl+Rx.swift +++ b/RxCocoa/iOS/UIControl+Rx.swift @@ -41,7 +41,7 @@ extension UIControl { - parameter controlEvents: Filter for observed event types. */ public func rx_controlEvents(controlEvents: UIControlEvents) -> ControlEvent { - let source: Observable = AnonymousObservable { [weak self] observer in + let source: Observable = create { [weak self] observer in MainScheduler.ensureExecutingOnScheduler() guard let control = self else { @@ -63,7 +63,7 @@ extension UIControl { } func rx_value(getter getter: () -> T, setter: T -> Void) -> ControlProperty { - let source: Observable = AnonymousObservable { [weak self] observer in + let source: Observable = create { [weak self] observer in guard let control = self else { observer.on(.Completed) return NopDisposable.instance diff --git a/RxCocoa/iOS/UIGestureRecognizer+Rx.swift b/RxCocoa/iOS/UIGestureRecognizer+Rx.swift index ec02538d..114f7819 100644 --- a/RxCocoa/iOS/UIGestureRecognizer+Rx.swift +++ b/RxCocoa/iOS/UIGestureRecognizer+Rx.swift @@ -57,7 +57,7 @@ extension UIGestureRecognizer { Reactive wrapper for gesture recognizer events. */ public var rx_event: ControlEvent { - let source: Observable = AnonymousObservable { [weak self] observer in + let source: Observable = create { [weak self] observer in MainScheduler.ensureExecutingOnScheduler() guard let control = self else { diff --git a/RxSwift/Observables/Implementations/AnonymousObservable.swift b/RxSwift/Observables/Implementations/AnonymousObservable.swift index eb23374a..3c981883 100644 --- a/RxSwift/Observables/Implementations/AnonymousObservable.swift +++ b/RxSwift/Observables/Implementations/AnonymousObservable.swift @@ -40,16 +40,16 @@ class AnonymousObservableSink : Sink, ObserverType { } } -public class AnonymousObservable : Producer { - public typealias SubscribeHandler = (AnyObserver) -> Disposable +class AnonymousObservable : Producer { + typealias SubscribeHandler = (AnyObserver) -> Disposable - public let _subscribeHandler: SubscribeHandler - - public init(_ subscribeHandler: SubscribeHandler) { + let _subscribeHandler: SubscribeHandler + + init(_ subscribeHandler: SubscribeHandler) { _subscribeHandler = subscribeHandler } - public override func run(observer: O) -> Disposable { + override func run(observer: O) -> Disposable { let sink = AnonymousObservableSink(observer: observer) sink.disposable = sink.run(self) return sink From 5293690a61b14785aa8407ea9bbd1864499d73df Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 2 Nov 2015 10:36:14 +0100 Subject: [PATCH 153/210] Updates bundle version to 2.0.0. --- RxBlocking/Info.plist | 2 +- RxCocoa/Info.plist | 2 +- RxSwift/Info.plist | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/RxBlocking/Info.plist b/RxBlocking/Info.plist index d3de8eef..7e7479f0 100644 --- a/RxBlocking/Info.plist +++ b/RxBlocking/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0 + 2.0.0 CFBundleSignature ???? CFBundleVersion diff --git a/RxCocoa/Info.plist b/RxCocoa/Info.plist index d3de8eef..7e7479f0 100644 --- a/RxCocoa/Info.plist +++ b/RxCocoa/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0 + 2.0.0 CFBundleSignature ???? CFBundleVersion diff --git a/RxSwift/Info.plist b/RxSwift/Info.plist index d3de8eef..7e7479f0 100644 --- a/RxSwift/Info.plist +++ b/RxSwift/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0 + 2.0.0 CFBundleSignature ???? CFBundleVersion From cab8c40ae18af516d479bff24850933eadc9992d Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 2 Nov 2015 10:46:12 +0100 Subject: [PATCH 154/210] Simplifies `ReachabilityService`. --- .../Services/ReachabilityService.swift | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/RxExample/RxExample/Services/ReachabilityService.swift b/RxExample/RxExample/Services/ReachabilityService.swift index 098dfd0d..9a9225cc 100644 --- a/RxExample/RxExample/Services/ReachabilityService.swift +++ b/RxExample/RxExample/Services/ReachabilityService.swift @@ -10,11 +10,11 @@ import RxSwift #endif -public enum ReachabilityStatus{ +public enum ReachabilityStatus { case Reachable, Unreachable } -class ReachabilityService{ +class ReachabilityService { private let reachabilityRef = try! Reachability.reachabilityForInternetConnection() @@ -43,24 +43,11 @@ class ReachabilityService{ } extension ObservableConvertibleType { - func retryOnBecomesReachable(valueOnFailure:E, reachabilityService: ReachabilityService) -> Observable{ - return retryOnBecomesReachable(valueOnFailure, reachabilityService: reachabilityService, orExternalTrigger: empty()) - } - - func retryOnBecomesReachable(valueOnFailure:E, reachabilityService:ReachabilityService, orExternalTrigger: Observable) -> Observable{ + func retryOnBecomesReachable(valueOnFailure:E, reachabilityService: ReachabilityService) -> Observable { return self.asObservable() .catchError { (e) -> Observable in - let retryBecauseOfNeworkAvailability = reachabilityService.reachabilityChanged - .flatMap { event -> Observable in - if event == .Reachable { - return just() - } else { - return empty() - } - } - - return sequenceOf(retryBecauseOfNeworkAvailability, orExternalTrigger) - .merge() + reachabilityService.reachabilityChanged + .filter { $0 == .Reachable } .flatMap { _ in failWith(e) } .startWith(valueOnFailure) } From 31095a8cb019b4a31d3a4de6170b955b1abeb958 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 2 Nov 2015 11:12:20 +0100 Subject: [PATCH 155/210] Release 2.0.0-beta.2 --- CHANGELOG.md | 18 ++++ README.md | 16 +-- RxBlocking.podspec | 4 +- RxCocoa.podspec | 2 +- RxSwift.podspec | 9 +- .../xcschemes/RxTests-OSX.xcscheme | 99 ------------------- .../xcschemes/RxTests-iOS.xcscheme | 99 ------------------- .../xcschemes/RxTests-tvOS.xcscheme | 56 ----------- 8 files changed, 26 insertions(+), 277 deletions(-) delete mode 100644 RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-OSX.xcscheme delete mode 100644 RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-iOS.xcscheme delete mode 100644 RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-tvOS.xcscheme diff --git a/CHANGELOG.md b/CHANGELOG.md index 491a5c21..63db7731 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,24 @@ All notable changes to this project will be documented in this file. --- +## [2.0.0-beta.2](https://github.com/ReactiveX/RxSwift/releases/tag/2.0.0-beta.2) + +#### Updated + +* Optimizations. System now performs significantly fewer allocations and is several times faster then 2.0.0-beta.1 +* Makes `AnonymousObservable` private in favor of `create` method. +* Adds `toArray` operator (non blocking version). +* Adds `withLatestFrom` operator, and also extends `Driver` with that operation. +* Adds `elementAt` operator (non blocking version). +* Adds `takeLast` operator. +* Improves `RxExample` app. Adds retries example when network becomes available again. +* Adds composite extensions to `Bag` (`on`, `disposeAllIn`). +* Renames mistyped extension on `ObserverType` from `onComplete` to `onCompleted`. + +#### Fixed + +* Fixes minimal platform version in OSX version of library to 10.9 + ## [2.0.0-beta.1](https://github.com/ReactiveX/RxSwift/releases/tag/2.0.0-beta.1) #### Updated diff --git a/README.md b/README.md index 80a8a0c3..2f475f0e 100644 --- a/README.md +++ b/README.md @@ -475,24 +475,12 @@ $ pod install ### [Carthage](https://github.com/Carthage/Carthage) -**For Xcode 7.0** +**Xcode 7.1 required** Add this to `Cartfile` ``` -git "git@github.com:ReactiveX/RxSwift.git" "2.0.0-beta.1" -``` - -``` -$ carthage update -``` - -**For Xcode 7.1 and tvOS support** - -Add this to `Cartfile` - -``` -git "git@github.com:ReactiveX/RxSwift.git" "master-7.1" +git "git@github.com:ReactiveX/RxSwift.git" "2.0.0-beta.2" ``` ``` diff --git a/RxBlocking.podspec b/RxBlocking.podspec index b5ba454a..3029cbe9 100644 --- a/RxBlocking.podspec +++ b/RxBlocking.podspec @@ -1,9 +1,9 @@ Pod::Spec.new do |s| s.name = "RxBlocking" - s.version = "2.0.0-beta.1" + s.version = "2.0.0-beta.2" s.summary = "RxSwift Blocking operatos" s.description = <<-DESC - Set of blocking operators for unit testing + Set of blocking operators for RxSwift. DESC s.homepage = "https://github.com/ReactiveX/RxSwift" s.license = 'MIT' diff --git a/RxCocoa.podspec b/RxCocoa.podspec index 3fc18e9e..b8406de4 100644 --- a/RxCocoa.podspec +++ b/RxCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "RxCocoa" - s.version = "2.0.0-beta.1" + s.version = "2.0.0-beta.2" s.summary = "RxSwift Cocoa extensions" s.description = <<-DESC * UI extensions diff --git a/RxSwift.podspec b/RxSwift.podspec index 47f4ca0f..46c7743c 100644 --- a/RxSwift.podspec +++ b/RxSwift.podspec @@ -1,19 +1,16 @@ Pod::Spec.new do |s| s.name = "RxSwift" - s.version = "2.0.0-beta.1" + s.version = "2.0.0-beta.2" s.summary = "Microsoft Reactive Extensions (Rx) for Swift and iOS/OSX platform" s.description = <<-DESC - This is a Swift port of Reactive extensions. + This is a Swift port of ReactiveX. [https://github.com/ReactiveX](https://github.com/ReactiveX) - [https://github.com/Reactive-Extensions](https://github.com/Reactive-Extensions) - - Like the original Rx, its intention is to enable easy composition of asynchronous operations and event streams. + Like the original [Rx](https://github.com/Reactive-extensions/Rx.Net), its intention is to enable easy composition of asynchronous operations and event streams. It tries to port as many concepts from the original Rx as possible, but some concepts were adapted for more pleasant and performant integration with iOS/OSX environment. Probably the best analogy for those who have never heard of Rx would be: - ``` git diff | grep bug | less # linux pipes - programs communicate by sending # sequences of bytes, words, lines, '\0' terminated strings... diff --git a/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-OSX.xcscheme b/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-OSX.xcscheme deleted file mode 100644 index a9d93f0f..00000000 --- a/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-OSX.xcscheme +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-iOS.xcscheme b/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-iOS.xcscheme deleted file mode 100644 index 1b2aaccd..00000000 --- a/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-iOS.xcscheme +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-tvOS.xcscheme b/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-tvOS.xcscheme deleted file mode 100644 index 339189bd..00000000 --- a/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-tvOS.xcscheme +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - From 0b8bf6ca5a93553c3a4785319e60a6a050dc0bbb Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 2 Nov 2015 11:13:09 +0100 Subject: [PATCH 156/210] Improves validate Podspecs scheme. --- scripts/validate-podspec.sh | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/scripts/validate-podspec.sh b/scripts/validate-podspec.sh index ec516a67..eb1e4fb7 100755 --- a/scripts/validate-podspec.sh +++ b/scripts/validate-podspec.sh @@ -3,6 +3,15 @@ set -e +function cleanup { + pushd ~/.cocoapods/repos/master + git clean -d -f + git reset master --hard + popd +} + +trap cleanup EXIT + VERSION=`cat RxSwift.podspec | grep -E "s.version\s+=" | cut -d '"' -f 2` pushd ~/.cocoapods/repos/master @@ -27,8 +36,3 @@ sed -E "s/s.source[^\}]+\}/s.source = { :git => '\/Users\/kzaher\/Proj pod lib lint RxSwift.podspec pod lib lint RxCocoa.podspec pod lib lint RxBlocking.podspec - -pushd ~/.cocoapods/repos/master -git clean -d -f -git reset master --hard -popd From e39f5dbce18632bbcbfb11437fdb3b572e376f97 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 2 Nov 2015 11:14:42 +0100 Subject: [PATCH 157/210] Shares tvOS schemes. --- .../xcschemes/RxBlocking-tvOS.xcscheme | 80 +++++++++++++++ .../xcschemes/RxCocoa-tvOS.xcscheme | 80 +++++++++++++++ .../xcschemes/RxSwift-tvOS.xcscheme | 80 +++++++++++++++ .../xcschemes/RxTests-OSX.xcscheme | 99 +++++++++++++++++++ .../xcschemes/RxTests-iOS.xcscheme | 99 +++++++++++++++++++ .../xcschemes/RxTests-tvOS.xcscheme | 56 +++++++++++ 6 files changed, 494 insertions(+) create mode 100644 Rx.xcodeproj/xcshareddata/xcschemes/RxBlocking-tvOS.xcscheme create mode 100644 Rx.xcodeproj/xcshareddata/xcschemes/RxCocoa-tvOS.xcscheme create mode 100644 Rx.xcodeproj/xcshareddata/xcschemes/RxSwift-tvOS.xcscheme create mode 100644 RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-OSX.xcscheme create mode 100644 RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-iOS.xcscheme create mode 100644 RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-tvOS.xcscheme diff --git a/Rx.xcodeproj/xcshareddata/xcschemes/RxBlocking-tvOS.xcscheme b/Rx.xcodeproj/xcshareddata/xcschemes/RxBlocking-tvOS.xcscheme new file mode 100644 index 00000000..f4419b2e --- /dev/null +++ b/Rx.xcodeproj/xcshareddata/xcschemes/RxBlocking-tvOS.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Rx.xcodeproj/xcshareddata/xcschemes/RxCocoa-tvOS.xcscheme b/Rx.xcodeproj/xcshareddata/xcschemes/RxCocoa-tvOS.xcscheme new file mode 100644 index 00000000..7b92bd16 --- /dev/null +++ b/Rx.xcodeproj/xcshareddata/xcschemes/RxCocoa-tvOS.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Rx.xcodeproj/xcshareddata/xcschemes/RxSwift-tvOS.xcscheme b/Rx.xcodeproj/xcshareddata/xcschemes/RxSwift-tvOS.xcscheme new file mode 100644 index 00000000..4fe17aac --- /dev/null +++ b/Rx.xcodeproj/xcshareddata/xcschemes/RxSwift-tvOS.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-OSX.xcscheme b/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-OSX.xcscheme new file mode 100644 index 00000000..a9d93f0f --- /dev/null +++ b/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-OSX.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-iOS.xcscheme b/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-iOS.xcscheme new file mode 100644 index 00000000..1b2aaccd --- /dev/null +++ b/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-iOS.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-tvOS.xcscheme b/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-tvOS.xcscheme new file mode 100644 index 00000000..339189bd --- /dev/null +++ b/RxTests/RxTests.xcodeproj/xcshareddata/xcschemes/RxTests-tvOS.xcscheme @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + From d4cda2430e354d99583df4deec12a458c0a14288 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Thu, 5 Nov 2015 12:34:20 +0100 Subject: [PATCH 158/210] Blocking operators run runloop while blocking. --- Rx.xcodeproj/project.pbxproj | 10 + RxBlocking/BlockingObservable+Operators.swift | 189 ++++++++---------- RxBlocking/RunLoopLock.swift | 33 +++ .../Tests/Observable+BlockingTest.swift | 12 +- 4 files changed, 135 insertions(+), 109 deletions(-) create mode 100644 RxBlocking/RunLoopLock.swift diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index 99c1d709..0d2175a6 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -358,6 +358,10 @@ C88254341B8A752B00B02D69 /* UITableView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88254121B8A752B00B02D69 /* UITableView+Rx.swift */; }; C88254351B8A752B00B02D69 /* UITextField+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88254131B8A752B00B02D69 /* UITextField+Rx.swift */; }; C88254361B8A752B00B02D69 /* UITextView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88254141B8A752B00B02D69 /* UITextView+Rx.swift */; }; + C88E296B1BEB712E001CCB92 /* RunLoopLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88E296A1BEB712E001CCB92 /* RunLoopLock.swift */; }; + C88E296C1BEB712E001CCB92 /* RunLoopLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88E296A1BEB712E001CCB92 /* RunLoopLock.swift */; }; + C88E296D1BEB712E001CCB92 /* RunLoopLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88E296A1BEB712E001CCB92 /* RunLoopLock.swift */; }; + C88E296E1BEB712E001CCB92 /* RunLoopLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88E296A1BEB712E001CCB92 /* RunLoopLock.swift */; }; C8941BDF1BD5695C00A0E874 /* BlockingObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8941BDE1BD5695C00A0E874 /* BlockingObservable.swift */; }; C8941BE01BD5695C00A0E874 /* BlockingObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8941BDE1BD5695C00A0E874 /* BlockingObservable.swift */; }; C8941BE11BD5695C00A0E874 /* BlockingObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8941BDE1BD5695C00A0E874 /* BlockingObservable.swift */; }; @@ -1001,6 +1005,7 @@ C88254131B8A752B00B02D69 /* UITextField+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextField+Rx.swift"; sourceTree = ""; }; C88254141B8A752B00B02D69 /* UITextView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextView+Rx.swift"; sourceTree = ""; }; C88BB8711B07E5ED0064D411 /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C88E296A1BEB712E001CCB92 /* RunLoopLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RunLoopLock.swift; sourceTree = ""; }; C8941BDE1BD5695C00A0E874 /* BlockingObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockingObservable.swift; sourceTree = ""; }; C8941BE31BD56B0700A0E874 /* BlockingObservable+Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BlockingObservable+Operators.swift"; sourceTree = ""; }; C89CDB351BCB0DD7002063D9 /* ShareReplay1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ShareReplay1.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; @@ -1400,6 +1405,7 @@ C8093F581B8A73A20088E94D /* ObservableConvertibleType+Blocking.swift */, C8941BDE1BD5695C00A0E874 /* BlockingObservable.swift */, C8941BE31BD56B0700A0E874 /* BlockingObservable+Operators.swift */, + C88E296A1BEB712E001CCB92 /* RunLoopLock.swift */, C8093F591B8A73A20088E94D /* README.md */, ); path = RxBlocking; @@ -2121,6 +2127,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C88E296B1BEB712E001CCB92 /* RunLoopLock.swift in Sources */, C8941BDF1BD5695C00A0E874 /* BlockingObservable.swift in Sources */, C8941BE41BD56B0700A0E874 /* BlockingObservable+Operators.swift in Sources */, C8093F5E1B8A73A20088E94D /* ObservableConvertibleType+Blocking.swift in Sources */, @@ -2131,6 +2138,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C88E296C1BEB712E001CCB92 /* RunLoopLock.swift in Sources */, C8941BE01BD5695C00A0E874 /* BlockingObservable.swift in Sources */, C8941BE51BD56B0700A0E874 /* BlockingObservable+Operators.swift in Sources */, C8093F5F1B8A73A20088E94D /* ObservableConvertibleType+Blocking.swift in Sources */, @@ -2595,6 +2603,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C88E296E1BEB712E001CCB92 /* RunLoopLock.swift in Sources */, C8941BE21BD5695C00A0E874 /* BlockingObservable.swift in Sources */, C8941BE71BD56B0700A0E874 /* BlockingObservable+Operators.swift in Sources */, C8F0C04F1BBBFBCE001B112F /* ObservableConvertibleType+Blocking.swift in Sources */, @@ -2803,6 +2812,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C88E296D1BEB712E001CCB92 /* RunLoopLock.swift in Sources */, C8941BE11BD5695C00A0E874 /* BlockingObservable.swift in Sources */, C8941BE61BD56B0700A0E874 /* BlockingObservable+Operators.swift in Sources */, D2EBEB8A1BB9B9EE003A27DC /* ObservableConvertibleType+Blocking.swift in Sources */, diff --git a/RxBlocking/BlockingObservable+Operators.swift b/RxBlocking/BlockingObservable+Operators.swift index 76e816f0..3f05801c 100644 --- a/RxBlocking/BlockingObservable+Operators.swift +++ b/RxBlocking/BlockingObservable+Operators.swift @@ -13,44 +13,39 @@ import Foundation extension BlockingObservable { /** - Blocks current thread until sequence terminates. - - If sequence terminates with error, terminating error will be thrown. - - - returns: All elements of sequence. - */ - public func toArray() throws -> [E] { - let condition = NSCondition() - - var elements: [E] = Array() - - var error: ErrorType? - - var ended = false + Blocks current thread until sequence terminates. - _ = self.source.subscribe { e in - switch e { - case .Next(let element): - elements.append(element) - case .Error(let e): - error = e - condition.lock() - ended = true - condition.signal() - condition.unlock() - case .Completed: - condition.lock() - ended = true - condition.signal() - condition.unlock() + If sequence terminates with error, terminating error will be thrown. + + - returns: All elements of sequence. + */ + public func toArray() throws -> [E] { + var elements: [E] = Array() + + var error: ErrorType? + + let lock = RunLoopLock() + + let d = SingleAssignmentDisposable() + + lock.dispatch { + d.disposable = self.source.subscribe { e in + switch e { + case .Next(let element): + elements.append(element) + case .Error(let e): + error = e + lock.stop() + case .Completed: + lock.stop() + } } } - condition.lock() - while !ended { - condition.wait() - } - condition.unlock() - + + lock.run() + + d.dispose() + if let error = error { throw error } @@ -61,99 +56,87 @@ extension BlockingObservable { extension BlockingObservable { /** - Blocks current thread until sequence produces first element. - - If sequence terminates with error before producing first element, terminating error will be thrown. - - - returns: First element of sequence. If sequence is empty `nil` is returned. - */ + Blocks current thread until sequence produces first element. + + If sequence terminates with error before producing first element, terminating error will be thrown. + + - returns: First element of sequence. If sequence is empty `nil` is returned. + */ public func first() throws -> E? { - let condition = NSCondition() - var element: E? - + var error: ErrorType? - - var ended = false - + let d = SingleAssignmentDisposable() - - d.disposable = self.source.subscribe { e in - switch e { - case .Next(let e): - if element == nil { - element = e + + let lock = RunLoopLock() + + lock.dispatch { + d.disposable = self.source.subscribe { e in + switch e { + case .Next(let e): + if element == nil { + element = e + } + break + case .Error(let e): + error = e + default: + break } - break - case .Error(let e): - error = e - default: - break + + lock.stop() } - - condition.lock() - ended = true - condition.signal() - condition.unlock() - } - - condition.lock() - while !ended { - condition.wait() } + + lock.run() + d.dispose() - condition.unlock() - + if let error = error { throw error } - + return element } } extension BlockingObservable { /** - Blocks current thread until sequence terminates. - - If sequence terminates with error, terminating error will be thrown. - - - returns: Last element in the sequence. If sequence is empty `nil` is returned. - */ + Blocks current thread until sequence terminates. + + If sequence terminates with error, terminating error will be thrown. + + - returns: Last element in the sequence. If sequence is empty `nil` is returned. + */ public func last() throws -> E? { - let condition = NSCondition() - var element: E? - + var error: ErrorType? - - var ended = false - + let d = SingleAssignmentDisposable() - - d.disposable = self.source.subscribe { e in - switch e { - case .Next(let e): - element = e - return - case .Error(let e): - error = e - default: - break + + let lock = RunLoopLock() + + lock.dispatch { + d.disposable = self.source.subscribe { e in + switch e { + case .Next(let e): + element = e + return + case .Error(let e): + error = e + default: + break + } + + lock.stop() } - - condition.lock() - ended = true - condition.signal() - condition.unlock() } - condition.lock() - while !ended { - condition.wait() - } + lock.run() + d.dispose() - condition.unlock() if let error = error { throw error diff --git a/RxBlocking/RunLoopLock.swift b/RxBlocking/RunLoopLock.swift new file mode 100644 index 00000000..5ea9ba73 --- /dev/null +++ b/RxBlocking/RunLoopLock.swift @@ -0,0 +1,33 @@ +// +// RunLoopLock.swift +// Rx +// +// Created by Krunoslav Zaher on 11/5/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +class RunLoopLock : NSObject { + let currentRunLoop: CFRunLoopRef + + override init() { + currentRunLoop = CFRunLoopGetCurrent() + } + + func dispatch(action: () -> ()) { + CFRunLoopPerformBlock(currentRunLoop, kCFRunLoopDefaultMode, action) + CFRunLoopWakeUp(currentRunLoop) + } + + func stop() { + CFRunLoopPerformBlock(currentRunLoop, kCFRunLoopDefaultMode) { + CFRunLoopStop(self.currentRunLoop) + } + CFRunLoopWakeUp(currentRunLoop) + } + + func run() { + CFRunLoopRun() + } +} diff --git a/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift b/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift index 448cc3ae..00b4c410 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift @@ -30,8 +30,8 @@ extension ObservableBlockingTest { try (failWith(testError) as Observable).toBlocking().toArray() XCTFail("It should fail") } - catch { - + catch let e { + XCTAssertTrue(e as NSError === testError) } } @@ -67,8 +67,8 @@ extension ObservableBlockingTest { try (failWith(testError) as Observable).toBlocking().first() XCTFail() } - catch { - + catch let e { + XCTAssertTrue(e as NSError === testError) } } @@ -104,8 +104,8 @@ extension ObservableBlockingTest { try (failWith(testError) as Observable).toBlocking().last() XCTFail() } - catch { - + catch let e { + XCTAssertTrue(e as NSError === testError) } } From f20618ed756d442fd662dea56f6cb29c8b7a4f24 Mon Sep 17 00:00:00 2001 From: Fernando Paredes Date: Thu, 5 Nov 2015 09:57:57 -0800 Subject: [PATCH 159/210] Update README to use standard Carthage syntax --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2f475f0e..1a8b0aa6 100644 --- a/README.md +++ b/README.md @@ -480,7 +480,7 @@ $ pod install Add this to `Cartfile` ``` -git "git@github.com:ReactiveX/RxSwift.git" "2.0.0-beta.2" +github "ReactiveX/RxSwift" "2.0.0-beta.2" ``` ``` From e7723a21738a87e875a42fcdb60c9445bbebed52 Mon Sep 17 00:00:00 2001 From: Junior B Date: Fri, 30 Oct 2015 09:06:10 +0100 Subject: [PATCH 160/210] Adds `RefCountDisposable` and `BooleanDisposable` and necessary tests --- Rx.xcodeproj/project.pbxproj | 38 ++++- RxSwift/Disposables/BooleanDisposable.swift | 47 +++++++ RxSwift/Disposables/RefCountDisposable.swift | 131 ++++++++++++++++++ .../Observables/Implementations/Window.swift | 10 ++ .../RxSwiftTests/Tests/DisposableTest.swift | 46 ++++++ 5 files changed, 268 insertions(+), 4 deletions(-) create mode 100644 RxSwift/Disposables/BooleanDisposable.swift create mode 100644 RxSwift/Disposables/RefCountDisposable.swift create mode 100644 RxSwift/Observables/Implementations/Window.swift diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index 0d2175a6..a2d22f45 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -564,6 +564,18 @@ C8F0C0441BBBFBB9001B112F /* _RXSwizzling.h in Headers */ = {isa = PBXBuildFile; fileRef = C8093E881B8A732E0088E94D /* _RXSwizzling.h */; settings = {ATTRIBUTES = (Public, ); }; }; C8F0C0451BBBFBB9001B112F /* _RXKVOObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = C8093E861B8A732E0088E94D /* _RXKVOObserver.h */; settings = {ATTRIBUTES = (Public, ); }; }; C8F0C04F1BBBFBCE001B112F /* ObservableConvertibleType+Blocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093F581B8A73A20088E94D /* ObservableConvertibleType+Blocking.swift */; }; + CB883B3B1BE24355000AC2EE /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B3A1BE24355000AC2EE /* Window.swift */; }; + CB883B3C1BE24355000AC2EE /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B3A1BE24355000AC2EE /* Window.swift */; }; + CB883B3D1BE24355000AC2EE /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B3A1BE24355000AC2EE /* Window.swift */; }; + CB883B3E1BE24355000AC2EE /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B3A1BE24355000AC2EE /* Window.swift */; }; + CB883B401BE24C15000AC2EE /* RefCountDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B3F1BE24C15000AC2EE /* RefCountDisposable.swift */; }; + CB883B411BE24C15000AC2EE /* RefCountDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B3F1BE24C15000AC2EE /* RefCountDisposable.swift */; }; + CB883B421BE24C15000AC2EE /* RefCountDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B3F1BE24C15000AC2EE /* RefCountDisposable.swift */; }; + CB883B431BE24C15000AC2EE /* RefCountDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B3F1BE24C15000AC2EE /* RefCountDisposable.swift */; }; + CB883B451BE256D4000AC2EE /* BooleanDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B441BE256D4000AC2EE /* BooleanDisposable.swift */; }; + CB883B461BE256D4000AC2EE /* BooleanDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B441BE256D4000AC2EE /* BooleanDisposable.swift */; }; + CB883B471BE256D4000AC2EE /* BooleanDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B441BE256D4000AC2EE /* BooleanDisposable.swift */; }; + CB883B481BE256D4000AC2EE /* BooleanDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B441BE256D4000AC2EE /* BooleanDisposable.swift */; }; CBEE771F1BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; }; CBEE77201BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; }; CBEE77211BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; }; @@ -1022,6 +1034,9 @@ C8F0C0021BBBFB8B001B112F /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8F0C04B1BBBFBB9001B112F /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8F0C0581BBBFBCE001B112F /* RxBlocking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxBlocking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CB883B3A1BE24355000AC2EE /* Window.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = ""; }; + CB883B3F1BE24C15000AC2EE /* RefCountDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefCountDisposable.swift; sourceTree = ""; }; + CB883B441BE256D4000AC2EE /* BooleanDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BooleanDisposable.swift; sourceTree = ""; }; CBEE771E1BD649A000AD584C /* ToArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToArray.swift; sourceTree = ""; }; D2138C751BB9BE9800339B5C /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D2245A1A1BD5657300E7146F /* WithLatestFrom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WithLatestFrom.swift; sourceTree = ""; }; @@ -1181,6 +1196,7 @@ children = ( C8093C541B8A72BE0088E94D /* AnonymousDisposable.swift */, C8093C551B8A72BE0088E94D /* BinaryDisposable.swift */, + CB883B441BE256D4000AC2EE /* BooleanDisposable.swift */, C8093C571B8A72BE0088E94D /* CompositeDisposable.swift */, C8093C581B8A72BE0088E94D /* DisposeBag.swift */, C8093C591B8A72BE0088E94D /* DisposeBase.swift */, @@ -1188,6 +1204,7 @@ C8093C5A1B8A72BE0088E94D /* NAryDisposable.swift */, C8093C5B1B8A72BE0088E94D /* NAryDisposable.tt */, C8093C5C1B8A72BE0088E94D /* NopDisposable.swift */, + CB883B3F1BE24C15000AC2EE /* RefCountDisposable.swift */, C8093C5D1B8A72BE0088E94D /* ScheduledDisposable.swift */, C8093C5E1B8A72BE0088E94D /* ScopedDisposable.swift */, C8093C5F1B8A72BE0088E94D /* SerialDisposable.swift */, @@ -1217,8 +1234,6 @@ C8093C6A1B8A72BE0088E94D /* Implementations */ = { isa = PBXGroup; children = ( - C84CC53F1BDC3B3700E06A64 /* ElementAt.swift */, - D2245A1A1BD5657300E7146F /* WithLatestFrom.swift */, C8093C6B1B8A72BE0088E94D /* Amb.swift */, C8C3DA111B93A3EA004D233E /* AnonymousObservable.swift */, C821DBA11BA4DCAB008F3809 /* Buffer.swift */, @@ -1234,6 +1249,7 @@ C8093C771B8A72BE0088E94D /* DelaySubscription.swift */, C8093C781B8A72BE0088E94D /* DistinctUntilChanged.swift */, C8093C791B8A72BE0088E94D /* Do.swift */, + C84CC53F1BDC3B3700E06A64 /* ElementAt.swift */, C8C3DA051B9393AC004D233E /* Empty.swift */, C8C3DA081B93941E004D233E /* FailWith.swift */, C8093C7A1B8A72BE0088E94D /* Filter.swift */, @@ -1257,6 +1273,7 @@ C8093C881B8A72BE0088E94D /* Sink.swift */, C8093C891B8A72BE0088E94D /* Skip.swift */, D285BAC31BC0231000B3F602 /* SkipUntil.swift */, + D22B6D251BC8504A00BCE0AB /* SkipWhile.swift */, C8093C8A1B8A72BE0088E94D /* StartWith.swift */, C8093C8B1B8A72BE0088E94D /* SubscribeOn.swift */, C8093C8C1B8A72BE0088E94D /* Switch.swift */, @@ -1267,12 +1284,13 @@ C8093C901B8A72BE0088E94D /* Throttle.swift */, C8093C911B8A72BE0088E94D /* Timer.swift */, CBEE771E1BD649A000AD584C /* ToArray.swift */, + D235B23D1BD003DD007E84DA /* Using.swift */, + CB883B3A1BE24355000AC2EE /* Window.swift */, + D2245A1A1BD5657300E7146F /* WithLatestFrom.swift */, C8093C941B8A72BE0088E94D /* Zip.swift */, C8093C921B8A72BE0088E94D /* Zip+arity.swift */, C8093C931B8A72BE0088E94D /* Zip+arity.tt */, C8C3D9FD1B935EDF004D233E /* Zip+CollectionType.swift */, - D22B6D251BC8504A00BCE0AB /* SkipWhile.swift */, - D235B23D1BD003DD007E84DA /* Using.swift */, ); path = Implementations; sourceTree = ""; @@ -2164,12 +2182,14 @@ C8093CF01B8A72BE0088E94D /* StableCompositeDisposable.swift in Sources */, D2245A1C1BD63C4600E7146F /* WithLatestFrom.swift in Sources */, C8093D4E1B8A72BE0088E94D /* Zip+arity.swift in Sources */, + CB883B3C1BE24355000AC2EE /* Window.swift in Sources */, C8093D4C1B8A72BE0088E94D /* Timer.swift in Sources */, C8C3DA071B9393AC004D233E /* Empty.swift in Sources */, C8093D881B8A72BE0088E94D /* RxBox.swift in Sources */, C8093D3A1B8A72BE0088E94D /* Sink.swift in Sources */, C8093D461B8A72BE0088E94D /* TakeUntil.swift in Sources */, C8093D941B8A72BE0088E94D /* MainScheduler.swift in Sources */, + CB883B461BE256D4000AC2EE /* BooleanDisposable.swift in Sources */, C84B38EF1BA433CD001B7D88 /* Generate.swift in Sources */, C8093D161B8A72BE0088E94D /* Deferred.swift in Sources */, C8093DA41B8A72BE0088E94D /* ReplaySubject.swift in Sources */, @@ -2193,6 +2213,7 @@ C8093CD81B8A72BE0088E94D /* BinaryDisposable.swift in Sources */, C89CDB371BCB0DD7002063D9 /* ShareReplay1.swift in Sources */, C8093D2A1B8A72BE0088E94D /* ObserveOn.swift in Sources */, + CB883B411BE24C15000AC2EE /* RefCountDisposable.swift in Sources */, C8093D361B8A72BE0088E94D /* Sample.swift in Sources */, C84CC54F1BDCF48200E06A64 /* LockOwnerType.swift in Sources */, D2752D621BC5551A0070C418 /* SkipUntil.swift in Sources */, @@ -2292,12 +2313,14 @@ C8093CEF1B8A72BE0088E94D /* StableCompositeDisposable.swift in Sources */, D2245A1B1BD5657300E7146F /* WithLatestFrom.swift in Sources */, C8093D4D1B8A72BE0088E94D /* Zip+arity.swift in Sources */, + CB883B3B1BE24355000AC2EE /* Window.swift in Sources */, C8093D4B1B8A72BE0088E94D /* Timer.swift in Sources */, C8C3DA061B9393AC004D233E /* Empty.swift in Sources */, C8093D871B8A72BE0088E94D /* RxBox.swift in Sources */, C8093D391B8A72BE0088E94D /* Sink.swift in Sources */, C8093D451B8A72BE0088E94D /* TakeUntil.swift in Sources */, C8093D931B8A72BE0088E94D /* MainScheduler.swift in Sources */, + CB883B451BE256D4000AC2EE /* BooleanDisposable.swift in Sources */, C84B38EE1BA433CD001B7D88 /* Generate.swift in Sources */, C8093D151B8A72BE0088E94D /* Deferred.swift in Sources */, C8093DA31B8A72BE0088E94D /* ReplaySubject.swift in Sources */, @@ -2321,6 +2344,7 @@ C8093CD71B8A72BE0088E94D /* BinaryDisposable.swift in Sources */, C89CDB361BCB0DD7002063D9 /* ShareReplay1.swift in Sources */, C8093D291B8A72BE0088E94D /* ObserveOn.swift in Sources */, + CB883B401BE24C15000AC2EE /* RefCountDisposable.swift in Sources */, C8093D351B8A72BE0088E94D /* Sample.swift in Sources */, C84CC54E1BDCF48200E06A64 /* LockOwnerType.swift in Sources */, D285BAC41BC0231000B3F602 /* SkipUntil.swift in Sources */, @@ -2420,12 +2444,14 @@ C8F0BF9C1BBBFB8B001B112F /* StableCompositeDisposable.swift in Sources */, D2245A1E1BD63C4A00E7146F /* WithLatestFrom.swift in Sources */, C8F0BF9D1BBBFB8B001B112F /* Zip+arity.swift in Sources */, + CB883B3E1BE24355000AC2EE /* Window.swift in Sources */, C8F0BF9E1BBBFB8B001B112F /* Timer.swift in Sources */, C8F0BF9F1BBBFB8B001B112F /* Empty.swift in Sources */, C8F0BFA01BBBFB8B001B112F /* RxBox.swift in Sources */, C8F0BFA11BBBFB8B001B112F /* Sink.swift in Sources */, C8F0BFA21BBBFB8B001B112F /* TakeUntil.swift in Sources */, C8F0BFA31BBBFB8B001B112F /* MainScheduler.swift in Sources */, + CB883B481BE256D4000AC2EE /* BooleanDisposable.swift in Sources */, C8F0BFA41BBBFB8B001B112F /* Generate.swift in Sources */, C8F0BFA51BBBFB8B001B112F /* Deferred.swift in Sources */, C8F0BFA61BBBFB8B001B112F /* ReplaySubject.swift in Sources */, @@ -2449,6 +2475,7 @@ C8F0BFB61BBBFB8B001B112F /* BinaryDisposable.swift in Sources */, C89CDB391BCB0DD7002063D9 /* ShareReplay1.swift in Sources */, C8F0BFB71BBBFB8B001B112F /* ObserveOn.swift in Sources */, + CB883B431BE24C15000AC2EE /* RefCountDisposable.swift in Sources */, C8F0BFB81BBBFB8B001B112F /* Sample.swift in Sources */, C84CC5511BDCF48200E06A64 /* LockOwnerType.swift in Sources */, D21C29311BC6A1C300448E70 /* SkipUntil.swift in Sources */, @@ -2699,12 +2726,14 @@ D2EBEB1B1BB9B6C1003A27DC /* Repeat.swift in Sources */, D2245A1D1BD63C4700E7146F /* WithLatestFrom.swift in Sources */, D2EBEAF81BB9B6B2003A27DC /* ScopedDisposable.swift in Sources */, + CB883B3D1BE24355000AC2EE /* Window.swift in Sources */, D2EBEAEA1BB9B697003A27DC /* SchedulerType.swift in Sources */, D2EBEB031BB9B6C1003A27DC /* CombineLatest+CollectionType.swift in Sources */, D2EBEADC1BB9B697003A27DC /* Cancelable.swift in Sources */, D2EBEAE41BB9B697003A27DC /* ObservableType.swift in Sources */, D2EBEB331BB9B6CA003A27DC /* Observable+Time.swift in Sources */, D2EBEB191BB9B6C1003A27DC /* Reduce.swift in Sources */, + CB883B471BE256D4000AC2EE /* BooleanDisposable.swift in Sources */, D2EBEB001BB9B6BA003A27DC /* Catch.swift in Sources */, D2EBEB161BB9B6C1003A27DC /* ObserveOnSerialDispatchQueue.swift in Sources */, D2EBEB061BB9B6C1003A27DC /* Debug.swift in Sources */, @@ -2728,6 +2757,7 @@ D2EBEB091BB9B6C1003A27DC /* DistinctUntilChanged.swift in Sources */, D2EBEB2A1BB9B6C5003A27DC /* Zip+CollectionType.swift in Sources */, C89CDB381BCB0DD7002063D9 /* ShareReplay1.swift in Sources */, + CB883B421BE24C15000AC2EE /* RefCountDisposable.swift in Sources */, D2EBEB401BB9B6DE003A27DC /* BehaviorSubject.swift in Sources */, C84CC5501BDCF48200E06A64 /* LockOwnerType.swift in Sources */, D2EBEB271BB9B6C1003A27DC /* Timer.swift in Sources */, diff --git a/RxSwift/Disposables/BooleanDisposable.swift b/RxSwift/Disposables/BooleanDisposable.swift new file mode 100644 index 00000000..ec33b80b --- /dev/null +++ b/RxSwift/Disposables/BooleanDisposable.swift @@ -0,0 +1,47 @@ +// +// BooleanDisposable.swift +// Rx +// +// Created by Junior B. on 10/29/15. +// Copyright (c) 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +/** +Represents a disposable resource that can be checked for disposal status. +*/ +public class BooleanDisposable : Disposable, Cancelable { + + internal static let BooleanDisposableTrue = BooleanDisposable(disposed: true) + private var _disposed = false + + /** + Initializes a new instance of the `BooleanDisposable` class + */ + public init() { + } + + /** + Initializes a new instance of the `BooleanDisposable` class with given value + */ + public init(disposed: Bool) { + self._disposed = disposed + } + + /** + - returns: Was resource disposed. + */ + public var disposed: Bool { + get { + return _disposed + } + } + + /** + Sets the status to disposed, which can be observer through the `disposed` property. + */ + public func dispose() { + _disposed = true + } +} \ No newline at end of file diff --git a/RxSwift/Disposables/RefCountDisposable.swift b/RxSwift/Disposables/RefCountDisposable.swift new file mode 100644 index 00000000..4b44b848 --- /dev/null +++ b/RxSwift/Disposables/RefCountDisposable.swift @@ -0,0 +1,131 @@ +// +// RefCountDisposable.swift +// Rx +// +// Created by Junior B. on 10/29/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +/** + Represents a disposable resource that only disposes its underlying disposable resource when all dependent disposable objects have been disposed. + */ +public class RefCountDisposable : DisposeBase, Cancelable { + private var _lock = SpinLock() + private var _disposable = nil as Disposable? + private var _primaryDisposed = false + private var _count = 0 + + /** + - returns: Was resource disposed. + */ + public var disposed: Bool { + get { + return _disposable == nil + } + } + + /** + Initializes a new instance of the `RefCountDisposable`. + */ + public init(disposable: Disposable) { + _disposable = disposable + super.init() + } + + /** + Holds a dependent disposable that when disposed decreases the refcount on the underlying disposable. + + When getter is called, a dependent disposable contributing to the reference count that manages the underlying disposable's lifetime is returned. + */ + public var disposable: Disposable { + get { + return _lock.calculateLocked { + if let _ = _disposable { + + do{ + try incrementChecked(&_count) + } catch (_){ + rxFatalError("RefCountDisposable increment failed") + } + + return RefCountInnerDisposable(self) + } else { + return NopDisposable.instance + } + } + } + } + + /** + Disposes the underlying disposable only when all dependent disposables have been disposed. + */ + public func dispose() { + let disposable: Disposable? = _lock.calculateLocked { + if let d = _disposable where !_primaryDisposed + { + _primaryDisposed = true; + + if (_count == 0) + { + _disposable = nil + return d + } + } + + return nil + } + + if let disposable = disposable { + disposable.dispose() + } + } + + private func release() { + let disposable: Disposable? = _lock.calculateLocked { + if let d = _disposable + { + do{ + try decrementChecked(&_count) + } catch (_){ + rxFatalError("RefCountDisposable decrement on release failed") + } + + guard _count >= 0 else { + rxFatalError("RefCountDisposable counter is lower than 0") + } + + if _primaryDisposed && _count == 0 { + _disposable = nil + return d + } + } + + return nil + } + + if let disposable = disposable { + disposable.dispose() + } + } +} + +internal final class RefCountInnerDisposable: DisposeBase, Disposable +{ + private let _parent: RefCountDisposable + private var _disposed: Int32 = 0 + + init(_ parent: RefCountDisposable) + { + _parent = parent; + super.init() + } + + internal func dispose() + { + if OSAtomicCompareAndSwap32(0, 1, &_disposed) { + _parent.release() + } + } +} \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Window.swift b/RxSwift/Observables/Implementations/Window.swift new file mode 100644 index 00000000..c1a3a954 --- /dev/null +++ b/RxSwift/Observables/Implementations/Window.swift @@ -0,0 +1,10 @@ +// +// Buffer.swift +// Rx +// +// Created by Junior B. on 29/10/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + diff --git a/RxTests/RxSwiftTests/Tests/DisposableTest.swift b/RxTests/RxSwiftTests/Tests/DisposableTest.swift index 4a309809..1b56f12e 100644 --- a/RxTests/RxSwiftTests/Tests/DisposableTest.swift +++ b/RxTests/RxSwiftTests/Tests/DisposableTest.swift @@ -123,4 +123,50 @@ class DisposableTest : RxTest { XCTAssertEqual(numberDisposed, 2) XCTAssertEqual(compositeDisposable.count, 0) } + + func testRefCountDisposable_RefCounting() { + let d = BooleanDisposable() + let r = RefCountDisposable(disposable: d) + + XCTAssertEqual(r.disposed, false) + + let d1 = r.disposable; + let d2 = r.disposable; + + XCTAssertEqual(d.disposed, false) + + d1.dispose() + XCTAssertEqual(d.disposed, false) + + d2.dispose() + XCTAssertEqual(d.disposed, false) + + r.dispose() + XCTAssertEqual(d.disposed, true) + + let d3 = r.disposable; + d3.dispose() + } + + func testRefCountDisposable_PrimaryDisposesFirst() { + let d = BooleanDisposable() + let r = RefCountDisposable(disposable: d) + + XCTAssertEqual(r.disposed, false) + + let d1 = r.disposable; + let d2 = r.disposable; + + XCTAssertEqual(d.disposed, false) + + d1.dispose() + XCTAssertEqual(d.disposed, false) + + r.dispose() + XCTAssertEqual(d.disposed, false) + + d2.dispose() + XCTAssertEqual(d.disposed, true) + + } } \ No newline at end of file From 7fe9a87cb52adf192262f9e1ecbb413dabdad883 Mon Sep 17 00:00:00 2001 From: Junior B Date: Fri, 30 Oct 2015 14:57:13 +0100 Subject: [PATCH 161/210] Add `window` operator, time/count version --- Rx.xcodeproj/project.pbxproj | 10 + RxExample/RxExample.xcodeproj/project.pbxproj | 20 +- .../Observables/Implementations/AddRef.swift | 47 ++++ .../Observables/Implementations/Window.swift | 132 ++++++++++ RxSwift/Observables/Observable+Time.swift | 19 ++ .../Tests/Observable+TimeTest.swift | 233 ++++++++++++++++-- 6 files changed, 443 insertions(+), 18 deletions(-) create mode 100644 RxSwift/Observables/Implementations/AddRef.swift diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index a2d22f45..d71823d7 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -576,6 +576,10 @@ CB883B461BE256D4000AC2EE /* BooleanDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B441BE256D4000AC2EE /* BooleanDisposable.swift */; }; CB883B471BE256D4000AC2EE /* BooleanDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B441BE256D4000AC2EE /* BooleanDisposable.swift */; }; CB883B481BE256D4000AC2EE /* BooleanDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B441BE256D4000AC2EE /* BooleanDisposable.swift */; }; + CB883B4A1BE369AA000AC2EE /* AddRef.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B491BE369AA000AC2EE /* AddRef.swift */; }; + CB883B4B1BE369AA000AC2EE /* AddRef.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B491BE369AA000AC2EE /* AddRef.swift */; }; + CB883B4C1BE369AA000AC2EE /* AddRef.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B491BE369AA000AC2EE /* AddRef.swift */; }; + CB883B4D1BE369AA000AC2EE /* AddRef.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B491BE369AA000AC2EE /* AddRef.swift */; }; CBEE771F1BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; }; CBEE77201BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; }; CBEE77211BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; }; @@ -1037,6 +1041,7 @@ CB883B3A1BE24355000AC2EE /* Window.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = ""; }; CB883B3F1BE24C15000AC2EE /* RefCountDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefCountDisposable.swift; sourceTree = ""; }; CB883B441BE256D4000AC2EE /* BooleanDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BooleanDisposable.swift; sourceTree = ""; }; + CB883B491BE369AA000AC2EE /* AddRef.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddRef.swift; sourceTree = ""; }; CBEE771E1BD649A000AD584C /* ToArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToArray.swift; sourceTree = ""; }; D2138C751BB9BE9800339B5C /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D2245A1A1BD5657300E7146F /* WithLatestFrom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WithLatestFrom.swift; sourceTree = ""; }; @@ -1234,6 +1239,7 @@ C8093C6A1B8A72BE0088E94D /* Implementations */ = { isa = PBXGroup; children = ( + CB883B491BE369AA000AC2EE /* AddRef.swift */, C8093C6B1B8A72BE0088E94D /* Amb.swift */, C8C3DA111B93A3EA004D233E /* AnonymousObservable.swift */, C821DBA11BA4DCAB008F3809 /* Buffer.swift */, @@ -2271,6 +2277,7 @@ C8640A041BA5B12A00D3C4E8 /* Repeat.swift in Sources */, C8093CF41B8A72BE0088E94D /* Error.swift in Sources */, C8093D141B8A72BE0088E94D /* Debug.swift in Sources */, + CB883B4B1BE369AA000AC2EE /* AddRef.swift in Sources */, C8093CCE1B8A72BE0088E94D /* Bag.swift in Sources */, C8093D301B8A72BE0088E94D /* Producer.swift in Sources */, C8093CF81B8A72BE0088E94D /* ImmediateSchedulerType.swift in Sources */, @@ -2402,6 +2409,7 @@ C8640A031BA5B12A00D3C4E8 /* Repeat.swift in Sources */, C8093CF31B8A72BE0088E94D /* Error.swift in Sources */, C8093D131B8A72BE0088E94D /* Debug.swift in Sources */, + CB883B4A1BE369AA000AC2EE /* AddRef.swift in Sources */, C8093CCD1B8A72BE0088E94D /* Bag.swift in Sources */, C8093D2F1B8A72BE0088E94D /* Producer.swift in Sources */, C8093CF71B8A72BE0088E94D /* ImmediateSchedulerType.swift in Sources */, @@ -2533,6 +2541,7 @@ C8F0BFE71BBBFB8B001B112F /* Repeat.swift in Sources */, C8F0BFE81BBBFB8B001B112F /* Error.swift in Sources */, C8F0BFE91BBBFB8B001B112F /* Debug.swift in Sources */, + CB883B4D1BE369AA000AC2EE /* AddRef.swift in Sources */, C8F0BFEA1BBBFB8B001B112F /* Bag.swift in Sources */, C8F0BFEB1BBBFB8B001B112F /* Producer.swift in Sources */, C8F0BFEC1BBBFB8B001B112F /* ImmediateSchedulerType.swift in Sources */, @@ -2815,6 +2824,7 @@ D2EBEB3D1BB9B6D8003A27DC /* SchedulerServices+Emulation.swift in Sources */, D2EBEB1C1BB9B6C1003A27DC /* Sample.swift in Sources */, D2EBEAFD1BB9B6BA003A27DC /* AnonymousObservable.swift in Sources */, + CB883B4C1BE369AA000AC2EE /* AddRef.swift in Sources */, D2EBEAFA1BB9B6B2003A27DC /* SingleAssignmentDisposable.swift in Sources */, D2EBEAF31BB9B6AE003A27DC /* DisposeBag.swift in Sources */, D2EBEAED1BB9B6A4003A27DC /* Bag.swift in Sources */, diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index 02b11de6..fcb9a82f 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -343,6 +343,10 @@ C8DF92EB1B0B38C0009BCF9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; }; C8DF92F61B0B43A4009BCF9A /* IntroductionExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DF92F51B0B43A4009BCF9A /* IntroductionExampleViewController.swift */; }; C8E9D2AF1BD3FD960079D0DB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; }; + CB883B501BE3AC54000AC2EE /* BooleanDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B4E1BE3AC54000AC2EE /* BooleanDisposable.swift */; }; + CB883B511BE3AC54000AC2EE /* RefCountDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B4F1BE3AC54000AC2EE /* RefCountDisposable.swift */; }; + CB883B601BE3AC72000AC2EE /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B5E1BE3AC72000AC2EE /* Window.swift */; }; + CB883B611BE3AC72000AC2EE /* AddRef.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B5F1BE3AC72000AC2EE /* AddRef.swift */; }; CBEE77541BD8C7B700AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE77531BD8C7B700AD584C /* ToArray.swift */; }; D2245A191BD5654C00E7146F /* WithLatestFrom.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2245A0B1BD564A700E7146F /* WithLatestFrom.swift */; }; D2AF91981BD3D95900A008C1 /* Using.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2AF91881BD2C51900A008C1 /* Using.swift */; }; @@ -733,6 +737,10 @@ C8DF92F01B0B3E67009BCF9A /* Info-OSX.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-OSX.plist"; sourceTree = ""; }; C8DF92F21B0B3E71009BCF9A /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = ""; }; C8DF92F51B0B43A4009BCF9A /* IntroductionExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = IntroductionExampleViewController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + CB883B4E1BE3AC54000AC2EE /* BooleanDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BooleanDisposable.swift; sourceTree = ""; }; + CB883B4F1BE3AC54000AC2EE /* RefCountDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefCountDisposable.swift; sourceTree = ""; }; + CB883B5E1BE3AC72000AC2EE /* Window.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = ""; }; + CB883B5F1BE3AC72000AC2EE /* AddRef.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddRef.swift; sourceTree = ""; }; CBEE77531BD8C7B700AD584C /* ToArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToArray.swift; sourceTree = ""; }; D2245A0B1BD564A700E7146F /* WithLatestFrom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WithLatestFrom.swift; sourceTree = ""; }; D2AF91881BD2C51900A008C1 /* Using.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Using.swift; sourceTree = ""; }; @@ -1105,12 +1113,14 @@ C84CC5831BDD484400E06A64 /* SubscriptionDisposable.swift */, C89464331BC6C2B00055219D /* AnonymousDisposable.swift */, C89464341BC6C2B00055219D /* BinaryDisposable.swift */, + CB883B4E1BE3AC54000AC2EE /* BooleanDisposable.swift */, C89464351BC6C2B00055219D /* CompositeDisposable.swift */, C89464361BC6C2B00055219D /* DisposeBag.swift */, C89464371BC6C2B00055219D /* DisposeBase.swift */, C89464381BC6C2B00055219D /* NAryDisposable.swift */, C89464391BC6C2B00055219D /* NAryDisposable.tt */, C894643A1BC6C2B00055219D /* NopDisposable.swift */, + CB883B4F1BE3AC54000AC2EE /* RefCountDisposable.swift */, C894643B1BC6C2B00055219D /* ScheduledDisposable.swift */, C894643C1BC6C2B00055219D /* ScopedDisposable.swift */, C894643D1BC6C2B00055219D /* SerialDisposable.swift */, @@ -1140,7 +1150,7 @@ C89464481BC6C2B00055219D /* Implementations */ = { isa = PBXGroup; children = ( - C84CC52D1BDC344100E06A64 /* ElementAt.swift */, + CB883B5F1BE3AC72000AC2EE /* AddRef.swift */, C89464491BC6C2B00055219D /* Amb.swift */, C894644A1BC6C2B00055219D /* AnonymousObservable.swift */, C894644C1BC6C2B00055219D /* Buffer.swift */, @@ -1156,6 +1166,7 @@ C89464561BC6C2B00055219D /* DelaySubscription.swift */, C89464571BC6C2B00055219D /* DistinctUntilChanged.swift */, C89464581BC6C2B00055219D /* Do.swift */, + C84CC52D1BDC344100E06A64 /* ElementAt.swift */, C89464591BC6C2B00055219D /* Empty.swift */, C894645A1BC6C2B00055219D /* FailWith.swift */, C894645B1BC6C2B00055219D /* Filter.swift */, @@ -1191,11 +1202,12 @@ C89464751BC6C2B00055219D /* Timer.swift */, CBEE77531BD8C7B700AD584C /* ToArray.swift */, D2AF91881BD2C51900A008C1 /* Using.swift */, + CB883B5E1BE3AC72000AC2EE /* Window.swift */, + D2245A0B1BD564A700E7146F /* WithLatestFrom.swift */, C89464791BC6C2B00055219D /* Zip.swift */, C89464761BC6C2B00055219D /* Zip+arity.swift */, C89464771BC6C2B00055219D /* Zip+arity.tt */, C89464781BC6C2B00055219D /* Zip+CollectionType.swift */, - D2245A0B1BD564A700E7146F /* WithLatestFrom.swift */, ); path = Implementations; sourceTree = ""; @@ -1735,6 +1747,7 @@ C89465881BC6C2BC0055219D /* RxScrollViewDelegateProxy.swift in Sources */, C89464B71BC6C2B00055219D /* Observable+Extensions.swift in Sources */, C89464A01BC6C2B00055219D /* Lock.swift in Sources */, + CB883B601BE3AC72000AC2EE /* Window.swift in Sources */, C89464C91BC6C2B00055219D /* Do.swift in Sources */, C89464A41BC6C2B00055219D /* Queue.swift in Sources */, C89464B91BC6C2B00055219D /* ObservableConvertibleType.swift in Sources */, @@ -1855,11 +1868,13 @@ C894656E1BC6C2BC0055219D /* DelegateProxy.swift in Sources */, C89464EF1BC6C2B00055219D /* Observable+Debug.swift in Sources */, C89464E91BC6C2B00055219D /* Zip+CollectionType.swift in Sources */, + CB883B511BE3AC54000AC2EE /* RefCountDisposable.swift in Sources */, C89465761BC6C2BC0055219D /* KVOObserver.swift in Sources */, C89465641BC6C2BC0055219D /* _RXSwizzling.m in Sources */, C89464CA1BC6C2B00055219D /* Empty.swift in Sources */, C803973B1BD3E17D009D8B26 /* ActivityIndicator.swift in Sources */, C89464C61BC6C2B00055219D /* Deferred.swift in Sources */, + CB883B611BE3AC72000AC2EE /* AddRef.swift in Sources */, D2AF91981BD3D95900A008C1 /* Using.swift in Sources */, C8297E501B6CF905000589EA /* TableViewController.swift in Sources */, C8297E511B6CF905000589EA /* PartialUpdatesViewController.swift in Sources */, @@ -1889,6 +1904,7 @@ C89465711BC6C2BC0055219D /* Observable+Bind.swift in Sources */, C89464B01BC6C2B00055219D /* SerialDisposable.swift in Sources */, C89464A21BC6C2B00055219D /* Bag.swift in Sources */, + CB883B501BE3AC54000AC2EE /* BooleanDisposable.swift in Sources */, C894657B1BC6C2BC0055219D /* RxCLLocationManagerDelegateProxy.swift in Sources */, C8297E581B6CF905000589EA /* User.swift in Sources */, C89464B41BC6C2B00055219D /* Event.swift in Sources */, diff --git a/RxSwift/Observables/Implementations/AddRef.swift b/RxSwift/Observables/Implementations/AddRef.swift new file mode 100644 index 00000000..36176dff --- /dev/null +++ b/RxSwift/Observables/Implementations/AddRef.swift @@ -0,0 +1,47 @@ +// +// AddRef.swift +// Rx +// +// Created by Junior B. on 30/10/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +class AddRefSink : Sink, ObserverType { + typealias Element = O.E + + override init(observer: O, cancel: Disposable) { + super.init(observer: observer, cancel: cancel) + } + + func on(event: Event) { + switch event { + case .Next(_): + observer?.on(event) + case .Completed, .Error(_): + observer?.on(event) + dispose() + } + } +} + +class AddRef : Producer { + typealias EventHandler = Event throws -> Void + + private let _source: Observable + private let _refCount: RefCountDisposable + + init(source: Observable, refCount: RefCountDisposable) { + _source = source + _refCount = refCount + } + + override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + let d = StableCompositeDisposable.create(_refCount.disposable, cancel) + + let sink = AddRefSink(observer: observer, cancel: d) + setSink(sink) + return _source.subscribeSafe(sink) + } +} \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Window.swift b/RxSwift/Observables/Implementations/Window.swift index c1a3a954..48a911de 100644 --- a/RxSwift/Observables/Implementations/Window.swift +++ b/RxSwift/Observables/Implementations/Window.swift @@ -8,3 +8,135 @@ import Foundation +class WindowTimeCountSink> : Sink, ObserverType { + typealias Parent = WindowTimeCount + typealias E = Element + + private let _parent: Parent + + private let _lock = NSRecursiveLock() + + private var _subject = PublishSubject() + private var _count = 0 + private var _windowId = 0 + + private let _timerD = SerialDisposable() + private let _refCountDisposable: RefCountDisposable + private let _groupDisposable = CompositeDisposable() + + init(parent: Parent, observer: O, cancel: Disposable) { + _parent = parent + + _groupDisposable.addDisposable(_timerD) + + _refCountDisposable = RefCountDisposable(disposable: _groupDisposable) + super.init(observer: observer, cancel: cancel) + } + + func run() -> Disposable { + + observer?.on(.Next(AddRef(source: _subject, refCount: _refCountDisposable).asObservable())) + createTimer(0) + + _groupDisposable.addDisposable(_parent._source.subscribeSafe(self)) + return _refCountDisposable + } + + func startNewWindowAndCompleteCurrentOne() { + _subject.on(.Completed) + _subject = PublishSubject() + + observer?.on(.Next(AddRef(source: _subject, refCount: _refCountDisposable).asObservable())) + } + + func on(event: Event) { + + var newWindow = false + var newId = 0 + + _lock.performLocked { + switch event { + case .Next(let element): + _subject.on(.Next(element)) + + do { + try incrementChecked(&_count) + } catch (let e) { + _subject.on(.Error(e as ErrorType)) + dispose() + } + + if (_count == _parent._count) { + newWindow = true + _count = 0 + newId = ++_windowId + self.startNewWindowAndCompleteCurrentOne() + } + + case .Error(let error): + _subject.on(.Error(error)) + observer?.on(.Error(error)) + dispose() + case .Completed: + _subject.on(.Completed) + observer?.on(.Completed) + dispose() + } + } + + if newWindow { + createTimer(newId) + } + } + + func createTimer(windowId: Int) { + if _timerD.disposed { + return + } + + if _windowId != windowId { + return + } + + _timerD.disposable = _parent._scheduler.scheduleRelative(windowId, dueTime: _parent._timeSpan) { previousWindowId in + + var newId = 0 + + self._lock.performLocked { + if previousWindowId != self._windowId { + return + } + + self._count = 0 + self._windowId = self._windowId &+ 1 + newId = self._windowId + self.startNewWindowAndCompleteCurrentOne() + } + + self.createTimer(newId) + + return NopDisposable.instance + } + } +} + +class WindowTimeCount : Producer> { + + private let _timeSpan: S.TimeInterval + private let _count: Int + private let _scheduler: S + private let _source: Observable + + init(source: Observable, timeSpan: S.TimeInterval, count: Int, scheduler: S) { + _source = source + _timeSpan = timeSpan + _count = count + _scheduler = scheduler + } + + override func run>(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { + let sink = WindowTimeCountSink(parent: self, observer: observer, cancel: cancel) + setSink(sink) + return sink.run() + } +} diff --git a/RxSwift/Observables/Observable+Time.swift b/RxSwift/Observables/Observable+Time.swift index 6917b79a..7b46104a 100644 --- a/RxSwift/Observables/Observable+Time.swift +++ b/RxSwift/Observables/Observable+Time.swift @@ -209,3 +209,22 @@ extension ObservableType { return BufferTimeCount(source: self.asObservable(), timeSpan: timeSpan, count: count, scheduler: scheduler) } } + +// MARK: window + +extension ObservableType { + + /** + Projects each element of an observable sequence into a window that is completed when either it’s full or a given amount of time has elapsed. + + - parameter timeSpan: Maximum time length of a window. + - parameter count: Maximum element count of a window. + - parameter scheduler: Scheduler to run windowing timers on. + - returns: An observable sequence of windows (instances of `Observable`). + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func window(timeSpan timeSpan: S.TimeInterval, count: Int, scheduler: S) + -> Observable> { + return WindowTimeCount(source: self.asObservable(), timeSpan: timeSpan, count: count, scheduler: scheduler) + } +} diff --git a/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift b/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift index 8ccc7784..5b7ce9cb 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift @@ -18,8 +18,7 @@ class ObservableTimeTest : RxTest { } } -// throttle - +// MARK: Throttle extension ObservableTimeTest { func test_ThrottleTimeSpan_AllPass() { let scheduler = TestScheduler(initialClock: 0) @@ -274,8 +273,7 @@ extension ObservableTimeTest { } } -// sample - +// MARK: Sample extension ObservableTimeTest { func testSample_Sampler_SamplerThrows() { let scheduler = TestScheduler(initialClock: 0) @@ -710,8 +708,7 @@ extension ObservableTimeTest { } } -// interval - +// MARK: Interval extension ObservableTimeTest { func testInterval_TimeSpan_Basic() { @@ -821,8 +818,7 @@ extension ObservableTimeTest { } } -// take - +// MARK: Take extension ObservableTimeTest { func testTake_TakeZero() { @@ -997,8 +993,7 @@ extension ObservableTimeTest { } -// take - +// MARK: Delay Subscription extension ObservableTimeTest { func testDelaySubscription_TimeSpan_Simple() { @@ -1073,7 +1068,7 @@ extension ObservableTimeTest { } } -// skip +// MARK: Skip extension ObservableTimeTest { func testSkip_Zero() { let scheduler = TestScheduler(initialClock: 0) @@ -1183,9 +1178,9 @@ extension ObservableTimeTest { } } -// +// MARK: Buffer extension ObservableTimeTest { - func bufferWithTimeOrCount_Basic() { + func testBufferWithTimeOrCount_Basic() { let scheduler = TestScheduler(initialClock: 0) let xs = scheduler.createHotObservable([ @@ -1221,7 +1216,7 @@ extension ObservableTimeTest { ]) } - func bufferWithTimeOrCount_Error() { + func testBufferWithTimeOrCount_Error() { let scheduler = TestScheduler(initialClock: 0) let xs = scheduler.createHotObservable([ @@ -1256,7 +1251,7 @@ extension ObservableTimeTest { ]) } - func bufferWithTimeOrCount_Disposed() { + func testBufferWithTimeOrCount_Disposed() { let scheduler = TestScheduler(initialClock: 0) let xs = scheduler.createHotObservable([ @@ -1287,7 +1282,7 @@ extension ObservableTimeTest { ]) } - func bufferWithTimeOrCount_Default() { + func testBufferWithTimeOrCount_Default() { let backgroundScheduler = SerialDispatchQueueScheduler(globalConcurrentQueuePriority: .Default) let result = try! range(1, 10, backgroundScheduler) @@ -1299,4 +1294,210 @@ extension ObservableTimeTest { XCTAssertEqual(result!, [4, 5, 6]) } +} + +// MARK: Window +extension ObservableTimeTest { + func testWindowWithTimeOrCount_Basic() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(205, 1), + next(210, 2), + next(240, 3), + next(280, 4), + next(320, 5), + next(350, 6), + next(370, 7), + next(420, 8), + next(470, 9), + completed(600) + ]) + + let res = scheduler.start { () -> Observable in + let window: Observable> = xs.window(timeSpan: 70, count: 3, scheduler: scheduler) + let mappedWithIndex = window.mapWithIndex { (o: Observable, i: Int) -> Observable in + return o.map { (e: Int) -> String in + return "\(i) \(e)" + } + } + let result = mappedWithIndex.merge() + return result + } + + XCTAssertEqual(res.messages, [ + next(205, "0 1"), + next(210, "0 2"), + next(240, "0 3"), + next(280, "1 4"), + next(320, "2 5"), + next(350, "2 6"), + next(370, "2 7"), + next(420, "3 8"), + next(470, "4 9"), + completed(600) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 600) + ]) + } + + func testWindowWithTimeOrCount_Error() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(205, 1), + next(210, 2), + next(240, 3), + next(280, 4), + next(320, 5), + next(350, 6), + next(370, 7), + next(420, 8), + next(470, 9), + error(600, testError) + ]) + + let res = scheduler.start { () -> Observable in + let window: Observable> = xs.window(timeSpan: 70, count: 3, scheduler: scheduler) + let mappedWithIndex = window.mapWithIndex { (o: Observable, i: Int) -> Observable in + return o.map { (e: Int) -> String in + return "\(i) \(e)" + } + } + let result = mappedWithIndex.merge() + return result + } + + XCTAssertEqual(res.messages, [ + next(205, "0 1"), + next(210, "0 2"), + next(240, "0 3"), + next(280, "1 4"), + next(320, "2 5"), + next(350, "2 6"), + next(370, "2 7"), + next(420, "3 8"), + next(470, "4 9"), + error(600, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 600) + ]) + } + + func testWindowWithTimeOrCount_Disposed() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(105, 0), + next(205, 1), + next(210, 2), + next(240, 3), + next(280, 4), + next(320, 5), + next(350, 6), + next(370, 7), + next(420, 8), + next(470, 9), + completed(600) + ]) + + let res = scheduler.start(370) { () -> Observable in + let window: Observable> = xs.window(timeSpan: 70, count: 3, scheduler: scheduler) + let mappedWithIndex = window.mapWithIndex { (o: Observable, i: Int) -> Observable in + return o.map { (e: Int) -> String in + return "\(i) \(e)" + } + } + let result = mappedWithIndex.merge() + return result + } + + XCTAssertEqual(res.messages, [ + next(205, "0 1"), + next(210, "0 2"), + next(240, "0 3"), + next(280, "1 4"), + next(320, "2 5"), + next(350, "2 6"), + next(370, "2 7") + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 370) + ]) + } + + /* + func testWindowWithTimeOrCount_BasicPeriod() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + next(240, 3), + next(270, 4), + next(320, 5), + next(360, 6), + next(390, 7), + next(410, 8), + next(460, 9), + next(470, 10), + completed(490) + ]) + + let res = scheduler.start { () -> Observable in + let window: Observable> = xs.window(timeSpan: 100, count: 3, scheduler: scheduler) + let mappedWithIndex = window.mapWithIndex { (o: Observable, i: Int) -> Observable in + return o.map { (e: Int) -> String in + return "\(i) \(e)" + }.concat(just("\(i) end")) + } + let result = mappedWithIndex.merge() + return result + } + + XCTAssertEqual(res.messages, [ + next(210, "0 2"), + next(240, "0 3"), + next(270, "0 4"), + next(300, "0 end"), + next(320, "1 5"), + next(360, "1 6"), + next(390, "1 7"), + next(400, "1 end"), + next(410, "2 8"), + next(460, "2 9"), + next(470, "2 10"), + next(490, "2 end"), + completed(490) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 490) + ]) + + }*/ + + func windowWithTimeOrCount_Default() { + let backgroundScheduler = SerialDispatchQueueScheduler(globalConcurrentQueuePriority: .Default) + + let result = try! range(1, 10, backgroundScheduler) + .window(timeSpan: 1000, count: 3, scheduler: backgroundScheduler) + .mapWithIndex { (o: Observable, i: Int) -> Observable in + return o.map { (e: Int) -> String in + return "\(i) \(e)" + } + } + .merge() + .skip(4) + .toBlocking() + .first() + + XCTAssertEqual(result!, "1 5") + } + } \ No newline at end of file From a99409d6e4761f4b5779ad3e35f483231e874bf0 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sat, 7 Nov 2015 12:56:01 +0100 Subject: [PATCH 162/210] Unification with optimization refactorings. --- RxSwift/Disposables/RefCountDisposable.swift | 48 +++++----- .../Observables/Implementations/AddRef.swift | 20 ++-- .../Observables/Implementations/Buffer.swift | 6 +- .../Observables/Implementations/Window.swift | 93 ++++++++++--------- .../RxSwiftTests/Tests/DisposableTest.swift | 10 +- 5 files changed, 94 insertions(+), 83 deletions(-) diff --git a/RxSwift/Disposables/RefCountDisposable.swift b/RxSwift/Disposables/RefCountDisposable.swift index 4b44b848..53e308d4 100644 --- a/RxSwift/Disposables/RefCountDisposable.swift +++ b/RxSwift/Disposables/RefCountDisposable.swift @@ -22,6 +22,7 @@ public class RefCountDisposable : DisposeBase, Cancelable { */ public var disposed: Bool { get { + _lock.lock(); defer { _lock.unlock() } return _disposable == nil } } @@ -39,21 +40,19 @@ public class RefCountDisposable : DisposeBase, Cancelable { When getter is called, a dependent disposable contributing to the reference count that manages the underlying disposable's lifetime is returned. */ - public var disposable: Disposable { - get { - return _lock.calculateLocked { - if let _ = _disposable { - - do{ - try incrementChecked(&_count) - } catch (_){ - rxFatalError("RefCountDisposable increment failed") - } - - return RefCountInnerDisposable(self) - } else { - return NopDisposable.instance + public func retain() -> Disposable { + return _lock.calculateLocked { + if let _ = _disposable { + + do { + try incrementChecked(&_count) + } catch (_) { + rxFatalError("RefCountDisposable increment failed") } + + return RefCountInnerDisposable(self) + } else { + return NopDisposable.instance } } } @@ -62,33 +61,32 @@ public class RefCountDisposable : DisposeBase, Cancelable { Disposes the underlying disposable only when all dependent disposables have been disposed. */ public func dispose() { - let disposable: Disposable? = _lock.calculateLocked { - if let d = _disposable where !_primaryDisposed + let oldDisposable: Disposable? = _lock.calculateLocked { + if let oldDisposable = _disposable where !_primaryDisposed { _primaryDisposed = true; if (_count == 0) { _disposable = nil - return d + return oldDisposable } } return nil } - if let disposable = disposable { + if let disposable = oldDisposable { disposable.dispose() } } private func release() { - let disposable: Disposable? = _lock.calculateLocked { - if let d = _disposable - { - do{ + let oldDisposable: Disposable? = _lock.calculateLocked { + if let oldDisposable = _disposable { + do { try decrementChecked(&_count) - } catch (_){ + } catch (_) { rxFatalError("RefCountDisposable decrement on release failed") } @@ -98,14 +96,14 @@ public class RefCountDisposable : DisposeBase, Cancelable { if _primaryDisposed && _count == 0 { _disposable = nil - return d + return oldDisposable } } return nil } - if let disposable = disposable { + if let disposable = oldDisposable { disposable.dispose() } } diff --git a/RxSwift/Observables/Implementations/AddRef.swift b/RxSwift/Observables/Implementations/AddRef.swift index 36176dff..50358ec3 100644 --- a/RxSwift/Observables/Implementations/AddRef.swift +++ b/RxSwift/Observables/Implementations/AddRef.swift @@ -11,16 +11,16 @@ import Foundation class AddRefSink : Sink, ObserverType { typealias Element = O.E - override init(observer: O, cancel: Disposable) { - super.init(observer: observer, cancel: cancel) + override init(observer: O) { + super.init(observer: observer) } func on(event: Event) { switch event { case .Next(_): - observer?.on(event) + forwardOn(event) case .Completed, .Error(_): - observer?.on(event) + forwardOn(event) dispose() } } @@ -37,11 +37,11 @@ class AddRef : Producer { _refCount = refCount } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let d = StableCompositeDisposable.create(_refCount.disposable, cancel) - - let sink = AddRefSink(observer: observer, cancel: d) - setSink(sink) - return _source.subscribeSafe(sink) + override func run(observer: O) -> Disposable { + let releaseDisposable = _refCount.retain() + let sink = AddRefSink(observer: observer) + sink.disposable = StableCompositeDisposable.create(releaseDisposable, _source.subscribeSafe(sink)) + + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Buffer.swift b/RxSwift/Observables/Implementations/Buffer.swift index b941e64f..bc07a900 100644 --- a/RxSwift/Observables/Implementations/Buffer.swift +++ b/RxSwift/Observables/Implementations/Buffer.swift @@ -99,8 +99,12 @@ class BufferTimeCountSink> : Sink, ObserverType { +class WindowTimeCountSink> + : Sink + , ObserverType + , LockOwnerType + , SynchronizedOnType { typealias Parent = WindowTimeCount typealias E = Element private let _parent: Parent - private let _lock = NSRecursiveLock() + let _lock = NSRecursiveLock() private var _subject = PublishSubject() private var _count = 0 @@ -24,19 +28,19 @@ class WindowTimeCountSink Disposable { - observer?.on(.Next(AddRef(source: _subject, refCount: _refCountDisposable).asObservable())) - createTimer(0) + forwardOn(.Next(AddRef(source: _subject, refCount: _refCountDisposable).asObservable())) + createTimer(_windowId) _groupDisposable.addDisposable(_parent._source.subscribeSafe(self)) return _refCountDisposable @@ -46,44 +50,45 @@ class WindowTimeCountSink() - observer?.on(.Next(AddRef(source: _subject, refCount: _refCountDisposable).asObservable())) + forwardOn(.Next(AddRef(source: _subject, refCount: _refCountDisposable).asObservable())) } - + func on(event: Event) { - + synchronizedOn(event) + } + + func _synchronized_on(event: Event) { var newWindow = false var newId = 0 - _lock.performLocked { - switch event { - case .Next(let element): - _subject.on(.Next(element)) - - do { - try incrementChecked(&_count) - } catch (let e) { - _subject.on(.Error(e as ErrorType)) - dispose() - } - - if (_count == _parent._count) { - newWindow = true - _count = 0 - newId = ++_windowId - self.startNewWindowAndCompleteCurrentOne() - } - - case .Error(let error): - _subject.on(.Error(error)) - observer?.on(.Error(error)) - dispose() - case .Completed: - _subject.on(.Completed) - observer?.on(.Completed) + switch event { + case .Next(let element): + _subject.on(.Next(element)) + + do { + try incrementChecked(&_count) + } catch (let e) { + _subject.on(.Error(e as ErrorType)) dispose() } + + if (_count == _parent._count) { + newWindow = true + _count = 0 + newId = ++_windowId + self.startNewWindowAndCompleteCurrentOne() + } + + case .Error(let error): + _subject.on(.Error(error)) + forwardOn(.Error(error)) + dispose() + case .Completed: + _subject.on(.Completed) + forwardOn(.Completed) + dispose() } - + if newWindow { createTimer(newId) } @@ -97,8 +102,12 @@ class WindowTimeCountSink : Producer> _scheduler = scheduler } - override func run>(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = WindowTimeCountSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run() + override func run>(observer: O) -> Disposable { + let sink = WindowTimeCountSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink } } diff --git a/RxTests/RxSwiftTests/Tests/DisposableTest.swift b/RxTests/RxSwiftTests/Tests/DisposableTest.swift index 1b56f12e..5ed560b8 100644 --- a/RxTests/RxSwiftTests/Tests/DisposableTest.swift +++ b/RxTests/RxSwiftTests/Tests/DisposableTest.swift @@ -130,8 +130,8 @@ class DisposableTest : RxTest { XCTAssertEqual(r.disposed, false) - let d1 = r.disposable; - let d2 = r.disposable; + let d1 = r.retain() + let d2 = r.retain() XCTAssertEqual(d.disposed, false) @@ -144,7 +144,7 @@ class DisposableTest : RxTest { r.dispose() XCTAssertEqual(d.disposed, true) - let d3 = r.disposable; + let d3 = r.retain(); d3.dispose() } @@ -154,8 +154,8 @@ class DisposableTest : RxTest { XCTAssertEqual(r.disposed, false) - let d1 = r.disposable; - let d2 = r.disposable; + let d1 = r.retain() + let d2 = r.retain() XCTAssertEqual(d.disposed, false) From 3052e9415c02db1aa5192f0c4c12f92a829bc9b0 Mon Sep 17 00:00:00 2001 From: Kyle Fang Date: Sat, 7 Nov 2015 13:00:16 -0600 Subject: [PATCH 163/210] Remove Unneeded dispose bag --- .../GitHubSignup/Views/GitHubSignupViewController.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift b/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift index 55707b26..98b65cf8 100644 --- a/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift +++ b/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift @@ -156,8 +156,6 @@ class GitHubSignupViewController : ViewController { let tapBackground = UITapGestureRecognizer(target: self, action: Selector("dismissKeyboard:")) view.addGestureRecognizer(tapBackground) - self.disposeBag = DisposeBag() - let API = self.API let validationService = ValidationService(API: API) From 4c83408a4611f3d0522ac3ebeeb9493a96081ed8 Mon Sep 17 00:00:00 2001 From: yury Date: Sun, 8 Nov 2015 01:04:09 +0300 Subject: [PATCH 164/210] FlatMapFirst with two tests --- .../Observables/Implementations/FlatMap.swift | 9 +- ...Observable+StandardSequenceOperators.swift | 17 ++ ...rvable+StandardSequenceOperatorsTest.swift | 233 ++++++++++++++++++ 3 files changed, 258 insertions(+), 1 deletion(-) diff --git a/RxSwift/Observables/Implementations/FlatMap.swift b/RxSwift/Observables/Implementations/FlatMap.swift index 458c7cef..60ceca12 100644 --- a/RxSwift/Observables/Implementations/FlatMap.swift +++ b/RxSwift/Observables/Implementations/FlatMap.swift @@ -79,6 +79,9 @@ class FlatMapSink) { switch event { case .Next(let element): + if _parent._onlyFirst && _group.count > FlatMapNoIterators { + return + } do { let value = try performMap(element) subscribeInner(value.asObservable()) @@ -156,15 +159,19 @@ class FlatMap: Producer { private let _selector1: Selector1? private let _selector2: Selector2? - init(source: Observable, selector: Selector1) { + private let _onlyFirst: Bool + + init(source: Observable, selector: Selector1, onlyFirst:Bool = false) { _source = source _selector1 = selector + _onlyFirst = onlyFirst _selector2 = nil } init(source: Observable, selector: Selector2) { _source = source _selector2 = selector + _onlyFirst = false _selector1 = nil } diff --git a/RxSwift/Observables/Observable+StandardSequenceOperators.swift b/RxSwift/Observables/Observable+StandardSequenceOperators.swift index 5278c3de..3dbed145 100644 --- a/RxSwift/Observables/Observable+StandardSequenceOperators.swift +++ b/RxSwift/Observables/Observable+StandardSequenceOperators.swift @@ -201,6 +201,23 @@ extension ObservableType { } } +// MARK: flatMapFirst + +extension ObservableType { + + /** + Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence. + + - parameter selector: A transform function to apply to each element. + - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func flatMapFirst(selector: (E) throws -> O) + -> Observable { + return FlatMap(source: asObservable(), selector: selector, onlyFirst: true) + } +} + // elementAt extension ObservableType { diff --git a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift index 0d77a9e6..945dfa77 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift @@ -1042,6 +1042,238 @@ extension ObservableStandardSequenceOperatorsTest { } } +// MARK: flatMapFirst +extension ObservableStandardSequenceOperatorsTest { + + func testFlatMapFirst_Complete() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(5, scheduler.createColdObservable([ + error(1, testError) + ])), + next(105, scheduler.createColdObservable([ + error(1, testError) + ])), + next(300, scheduler.createColdObservable([ + next(10, 102), + next(90, 103), + next(110, 104), + next(190, 105), + next(440, 106), + completed(460) + ])), + next(400, scheduler.createColdObservable([ + next(180, 202), + next(190, 203), + completed(205) + ])), + next(550, scheduler.createColdObservable([ + next(10, 301), + next(50, 302), + next(70, 303), + next(260, 304), + next(310, 305), + completed(410) + ])), + next(750, scheduler.createColdObservable([ + completed(40) + ])), + next(850, scheduler.createColdObservable([ + next(80, 401), + next(90, 402), + completed(100) + ])), + completed(900) + ]) + + let res = scheduler.start { + xs.flatMapFirst { $0 } + } + + XCTAssertEqual(res.messages, [ + next(310, 102), + next(390, 103), + next(410, 104), + next(490, 105), + next(740, 106), + next(930, 401), + next(940, 402), + completed(950) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 900) + ]) + + XCTAssertEqual(xs.recordedEvents[2].value.subscriptions, [ + Subscription(300, 760) + ]) + + XCTAssertEqual(xs.recordedEvents[3].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[4].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[5].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[6].value.subscriptions, [ + Subscription(850, 950) + ]) + } + + + func testFlatMapFirst_Complete_InnerNotComplete() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(5, scheduler.createColdObservable([ + error(1, testError) + ])), + next(105, scheduler.createColdObservable([ + error(1, testError) + ])), + next(300, scheduler.createColdObservable([ + next(10, 102), + next(90, 103), + next(110, 104), + next(190, 105), + next(440, 106), + completed(460) + ])), + next(400, scheduler.createColdObservable([ + next(180, 202), + next(190, 203), + completed(205) + ])), + next(550, scheduler.createColdObservable([ + next(10, 301), + next(50, 302), + next(70, 303), + next(260, 304), + next(310, 305), + completed(410) + ])), + next(750, scheduler.createColdObservable([ + completed(40) + ])), + next(850, scheduler.createColdObservable([ + next(80, 401), + next(90, 402), + completed(100) + ])), + ]) + + let res = scheduler.start { + xs.flatMapFirst { $0 } + } + + XCTAssertEqual(res.messages, [ + next(310, 102), + next(390, 103), + next(410, 104), + next(490, 105), + next(740, 106), + next(930, 401), + next(940, 402), + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 1000) + ]) + + XCTAssertEqual(xs.recordedEvents[2].value.subscriptions, [ + Subscription(300, 760) + ]) + + XCTAssertEqual(xs.recordedEvents[3].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[4].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[5].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[6].value.subscriptions, [ + Subscription(850, 950) + ]) + } + + func testFlatMapFirst_Complete_OuterNotComplete() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(5, scheduler.createColdObservable([ + error(1, testError) + ])), + next(105, scheduler.createColdObservable([ + error(1, testError) + ])), + next(300, scheduler.createColdObservable([ + next(10, 102), + next(90, 103), + next(110, 104), + next(190, 105), + next(440, 106), + completed(460) + ])), + next(400, scheduler.createColdObservable([ + next(180, 202), + next(190, 203), + ])), + next(550, scheduler.createColdObservable([ + next(10, 301), + next(50, 302), + next(70, 303), + next(260, 304), + next(310, 305), + completed(410) + ])), + next(750, scheduler.createColdObservable([ + completed(40) + ])), + next(850, scheduler.createColdObservable([ + next(80, 401), + next(90, 402), + completed(100) + ])), + completed(900) + ]) + + let res = scheduler.start { + xs.flatMapFirst { $0 } + } + + XCTAssertEqual(res.messages, [ + next(310, 102), + next(390, 103), + next(410, 104), + next(490, 105), + next(740, 106), + next(930, 401), + next(940, 402), + completed(950), + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 900) + ]) + + XCTAssertEqual(xs.recordedEvents[2].value.subscriptions, [ + Subscription(300, 760) + ]) + + XCTAssertEqual(xs.recordedEvents[3].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[4].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[5].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[6].value.subscriptions, [ + Subscription(850, 950) + ]) + } + + +} + // MARK: flatMap extension ObservableStandardSequenceOperatorsTest { @@ -1113,6 +1345,7 @@ extension ObservableStandardSequenceOperatorsTest { Subscription(200, 900) ]) + XCTAssertEqual(xs.recordedEvents[2].value.subscriptions, [ Subscription(300, 760) ]) From ecf7d2ae71d404c3899127def69b5f0dce2a1eba Mon Sep 17 00:00:00 2001 From: yury Date: Sun, 8 Nov 2015 01:54:17 +0300 Subject: [PATCH 165/210] Port other flatMap tests --- ...rvable+StandardSequenceOperatorsTest.swift | 379 ++++++++++++++++-- 1 file changed, 352 insertions(+), 27 deletions(-) diff --git a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift index 945dfa77..e6d5e060 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift @@ -1044,10 +1044,10 @@ extension ObservableStandardSequenceOperatorsTest { // MARK: flatMapFirst extension ObservableStandardSequenceOperatorsTest { - + func testFlatMapFirst_Complete() { let scheduler = TestScheduler(initialClock: 0) - + let xs = scheduler.createHotObservable([ next(5, scheduler.createColdObservable([ error(1, testError) @@ -1086,11 +1086,11 @@ extension ObservableStandardSequenceOperatorsTest { ])), completed(900) ]) - + let res = scheduler.start { xs.flatMapFirst { $0 } } - + XCTAssertEqual(res.messages, [ next(310, 102), next(390, 103), @@ -1101,7 +1101,7 @@ extension ObservableStandardSequenceOperatorsTest { next(940, 402), completed(950) ]) - + XCTAssertEqual(xs.subscriptions, [ Subscription(200, 900) ]) @@ -1113,7 +1113,7 @@ extension ObservableStandardSequenceOperatorsTest { XCTAssertEqual(xs.recordedEvents[3].value.subscriptions, []) XCTAssertEqual(xs.recordedEvents[4].value.subscriptions, []) - + XCTAssertEqual(xs.recordedEvents[5].value.subscriptions, []) XCTAssertEqual(xs.recordedEvents[6].value.subscriptions, [ @@ -1121,10 +1121,10 @@ extension ObservableStandardSequenceOperatorsTest { ]) } - + func testFlatMapFirst_Complete_InnerNotComplete() { let scheduler = TestScheduler(initialClock: 0) - + let xs = scheduler.createHotObservable([ next(5, scheduler.createColdObservable([ error(1, testError) @@ -1162,11 +1162,11 @@ extension ObservableStandardSequenceOperatorsTest { completed(100) ])), ]) - + let res = scheduler.start { xs.flatMapFirst { $0 } } - + XCTAssertEqual(res.messages, [ next(310, 102), next(390, 103), @@ -1176,29 +1176,29 @@ extension ObservableStandardSequenceOperatorsTest { next(930, 401), next(940, 402), ]) - + XCTAssertEqual(xs.subscriptions, [ Subscription(200, 1000) ]) - + XCTAssertEqual(xs.recordedEvents[2].value.subscriptions, [ Subscription(300, 760) ]) - + XCTAssertEqual(xs.recordedEvents[3].value.subscriptions, []) - + XCTAssertEqual(xs.recordedEvents[4].value.subscriptions, []) - + XCTAssertEqual(xs.recordedEvents[5].value.subscriptions, []) - + XCTAssertEqual(xs.recordedEvents[6].value.subscriptions, [ Subscription(850, 950) ]) } - + func testFlatMapFirst_Complete_OuterNotComplete() { let scheduler = TestScheduler(initialClock: 0) - + let xs = scheduler.createHotObservable([ next(5, scheduler.createColdObservable([ error(1, testError) @@ -1236,11 +1236,11 @@ extension ObservableStandardSequenceOperatorsTest { ])), completed(900) ]) - + let res = scheduler.start { xs.flatMapFirst { $0 } } - + XCTAssertEqual(res.messages, [ next(310, 102), next(390, 103), @@ -1251,27 +1251,352 @@ extension ObservableStandardSequenceOperatorsTest { next(940, 402), completed(950), ]) - + XCTAssertEqual(xs.subscriptions, [ Subscription(200, 900) ]) - + XCTAssertEqual(xs.recordedEvents[2].value.subscriptions, [ Subscription(300, 760) ]) - + XCTAssertEqual(xs.recordedEvents[3].value.subscriptions, []) - + XCTAssertEqual(xs.recordedEvents[4].value.subscriptions, []) - + XCTAssertEqual(xs.recordedEvents[5].value.subscriptions, []) - + XCTAssertEqual(xs.recordedEvents[6].value.subscriptions, [ Subscription(850, 950) ]) } + func testFlatMapFirst_Complete_ErrorOuter() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(5, scheduler.createColdObservable([ + error(1, testError) + ])), + next(105, scheduler.createColdObservable([ + error(1, testError) + ])), + next(300, scheduler.createColdObservable([ + next(10, 102), + next(90, 103), + next(110, 104), + next(190, 105), + next(440, 106), + completed(460) + ])), + next(400, scheduler.createColdObservable([ + next(180, 202), + next(190, 203), + ])), + next(550, scheduler.createColdObservable([ + next(10, 301), + next(50, 302), + next(70, 303), + next(260, 304), + next(310, 305), + completed(410) + ])), + next(750, scheduler.createColdObservable([ + completed(40) + ])), + next(850, scheduler.createColdObservable([ + next(80, 401), + next(90, 402), + completed(100) + ])), + error(900, testError) + ]) + + let res = scheduler.start { + xs.flatMapFirst { $0 } + } + + XCTAssertEqual(res.messages, [ + next(310, 102), + next(390, 103), + next(410, 104), + next(490, 105), + next(740, 106), + error(900, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 900) + ]) + + XCTAssertEqual(xs.recordedEvents[2].value.subscriptions, [ + Subscription(300, 760) + ]) + + XCTAssertEqual(xs.recordedEvents[3].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[4].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[5].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[6].value.subscriptions, [ + Subscription(850, 900) + ]) + } + + func testFlatMapFirst_Error_Inner() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(5, scheduler.createColdObservable([ + error(1, testError) + ])), + next(105, scheduler.createColdObservable([ + error(1, testError) + ])), + next(300, scheduler.createColdObservable([ + next(10, 102), + next(90, 103), + next(110, 104), + next(190, 105), + next(440, 106), + error(460, testError) + ])), + next(400, scheduler.createColdObservable([ + next(180, 202), + next(190, 203), + completed(205) + ])), + next(550, scheduler.createColdObservable([ + next(10, 301), + next(50, 302), + next(70, 303), + next(260, 304), + next(310, 305), + completed(410) + ])), + next(750, scheduler.createColdObservable([ + completed(40) + ])), + next(850, scheduler.createColdObservable([ + next(80, 401), + next(90, 402), + completed(100) + ])), + completed(900) + ]) + + let res = scheduler.start { + xs.flatMapFirst { $0 } + } + + XCTAssertEqual(res.messages, [ + next(310, 102), + next(390, 103), + next(410, 104), + next(490, 105), + next(740, 106), + error(760, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 760) + ]) + + XCTAssertEqual(xs.recordedEvents[2].value.subscriptions, [ + Subscription(300, 760) + ]) + + XCTAssertEqual(xs.recordedEvents[3].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[4].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[5].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[6].value.subscriptions, [ + ]) + } + + func testFlatMapFirst_Dispose() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(5, scheduler.createColdObservable([ + error(1, testError) + ])), + next(105, scheduler.createColdObservable([ + error(1, testError) + ])), + next(300, scheduler.createColdObservable([ + next(10, 102), + next(90, 103), + next(110, 104), + next(190, 105), + next(440, 106), + completed(460) + ])), + next(400, scheduler.createColdObservable([ + next(180, 202), + next(190, 203), + completed(205) + ])), + next(550, scheduler.createColdObservable([ + next(10, 301), + next(50, 302), + next(70, 303), + next(260, 304), + next(310, 305), + completed(410) + ])), + next(750, scheduler.createColdObservable([ + completed(40) + ])), + next(850, scheduler.createColdObservable([ + next(80, 401), + next(90, 402), + completed(100) + ])), + completed(900) + ]) + + let res = scheduler.start(700) { + xs.flatMapFirst { $0 } + } + + XCTAssertEqual(res.messages, [ + next(310, 102), + next(390, 103), + next(410, 104), + next(490, 105), + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 700) + ]) + + XCTAssertEqual(xs.recordedEvents[2].value.subscriptions, [ + Subscription(300, 700) + ]) + + XCTAssertEqual(xs.recordedEvents[3].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[4].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[5].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[6].value.subscriptions, []) + } + + func testFlatMapFirst_SelectorThrows() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(5, scheduler.createColdObservable([ + error(1, testError) + ])), + next(105, scheduler.createColdObservable([ + error(1, testError) + ])), + next(300, scheduler.createColdObservable([ + next(10, 102), + next(90, 103), + next(110, 104), + next(190, 105), + next(440, 106), + completed(460) + ])), + next(400, scheduler.createColdObservable([ + next(180, 202), + next(190, 203), + completed(205) + ])), + next(550, scheduler.createColdObservable([ + next(10, 301), + next(50, 302), + next(70, 303), + next(260, 304), + next(310, 305), + completed(410) + ])), + next(750, scheduler.createColdObservable([ + completed(40) + ])), + next(850, scheduler.createColdObservable([ + next(80, 401), + next(90, 402), + completed(100) + ])), + completed(900) + ]) + + var invoked = 0 + let res = scheduler.start { + return xs.flatMapFirst { (x: ColdObservable) -> ColdObservable in + invoked++ + if invoked == 2 { + throw testError + } + return x + } + } + + XCTAssertEqual(res.messages, [ + next(310, 102), + next(390, 103), + next(410, 104), + next(490, 105), + next(740, 106), + error(850, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 850) + ]) + + XCTAssertEqual(xs.recordedEvents[2].value.subscriptions, [ + Subscription(300, 760) + ]) + + XCTAssertEqual(xs.recordedEvents[3].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[4].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[5].value.subscriptions, []) + + XCTAssertEqual(xs.recordedEvents[6].value.subscriptions, []) + } + + func testFlatMapFirst_UseFunction() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(210, 4), + next(220, 3), + next(250, 5), + next(270, 1), + completed(290) + ]) + + let res = scheduler.start { + xs.flatMapFirst { (x) in + return interval(10, scheduler).map { _ in x } .take(x) + } + } + + XCTAssertEqual(res.messages, [ + next(220, 4), + next(230, 4), + next(240, 4), + next(250, 4), + next(280, 1), + completed(290) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 290) + ]) + } } // MARK: flatMap @@ -4377,4 +4702,4 @@ extension ObservableStandardSequenceOperatorsTest { Subscription(200, 210) ]) } -} \ No newline at end of file +} From ce983c44d03a4f34efe8d0ea01b3669be475ec36 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 8 Nov 2015 17:51:18 +0100 Subject: [PATCH 166/210] Polishing and optimizing `retryWhen`. --- Rx.xcodeproj/project.pbxproj | 50 ++++- RxExample/RxExample.xcodeproj/project.pbxproj | 64 +++--- RxSwift/AnyObserver.swift | 13 +- RxSwift/Concurrency/AsyncLock.swift | 94 +++++---- .../Implementations/AnonymousObservable.swift | 4 +- .../Observables/Implementations/Catch.swift | 12 +- .../Observables/Implementations/Concat.swift | 12 +- .../Implementations/RetryWhen.swift | 185 ++++++++++-------- .../Observables/Implementations/Sink.swift | 2 +- RxSwift/Observables/Observable+Single.swift | 16 +- RxSwift/Observers/TailRecursiveSink.swift | 64 +++--- RxSwift/Schedulers/AnonymousInvocable.swift | 21 ++ .../Schedulers/CurrentThreadScheduler.swift | 2 +- RxSwift/Schedulers/ImmediateScheduler.swift | 6 +- .../Schedulers/InvocableScheduledItem.swift | 24 +++ RxSwift/Schedulers/InvocableType.swift | 19 ++ RxSwift/Schedulers/ScheduledItem.swift | 38 ++-- RxSwift/Schedulers/ScheduledItemType.swift | 15 ++ .../Mocks/ColdObservable.swift | 11 +- .../Tests/Observable+SingleTest.swift | 173 ++++++++++++---- 20 files changed, 563 insertions(+), 262 deletions(-) create mode 100644 RxSwift/Schedulers/AnonymousInvocable.swift create mode 100644 RxSwift/Schedulers/InvocableScheduledItem.swift create mode 100644 RxSwift/Schedulers/InvocableType.swift create mode 100644 RxSwift/Schedulers/ScheduledItemType.swift diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index 91cab0be..2162ab6c 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -564,6 +564,26 @@ C8F0C0441BBBFBB9001B112F /* _RXSwizzling.h in Headers */ = {isa = PBXBuildFile; fileRef = C8093E881B8A732E0088E94D /* _RXSwizzling.h */; settings = {ATTRIBUTES = (Public, ); }; }; C8F0C0451BBBFBB9001B112F /* _RXKVOObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = C8093E861B8A732E0088E94D /* _RXKVOObserver.h */; settings = {ATTRIBUTES = (Public, ); }; }; C8F0C04F1BBBFBCE001B112F /* ObservableConvertibleType+Blocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093F581B8A73A20088E94D /* ObservableConvertibleType+Blocking.swift */; }; + C8F6A0EF1BEE2CB6007DF367 /* ScheduledItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A0EE1BEE2CB6007DF367 /* ScheduledItemType.swift */; }; + C8F6A0F01BEE2CB6007DF367 /* ScheduledItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A0EE1BEE2CB6007DF367 /* ScheduledItemType.swift */; }; + C8F6A0F11BEE2CB6007DF367 /* ScheduledItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A0EE1BEE2CB6007DF367 /* ScheduledItemType.swift */; }; + C8F6A0F21BEE2CB6007DF367 /* ScheduledItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A0EE1BEE2CB6007DF367 /* ScheduledItemType.swift */; }; + C8F6A0F41BEE3395007DF367 /* InvocableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A0F31BEE3395007DF367 /* InvocableType.swift */; }; + C8F6A0F51BEE3395007DF367 /* InvocableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A0F31BEE3395007DF367 /* InvocableType.swift */; }; + C8F6A0F61BEE3395007DF367 /* InvocableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A0F31BEE3395007DF367 /* InvocableType.swift */; }; + C8F6A0F71BEE3395007DF367 /* InvocableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A0F31BEE3395007DF367 /* InvocableType.swift */; }; + C8F6A0F91BEE33C1007DF367 /* InvocableScheduledItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A0F81BEE33C1007DF367 /* InvocableScheduledItem.swift */; }; + C8F6A0FA1BEE33C1007DF367 /* InvocableScheduledItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A0F81BEE33C1007DF367 /* InvocableScheduledItem.swift */; }; + C8F6A0FB1BEE33C1007DF367 /* InvocableScheduledItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A0F81BEE33C1007DF367 /* InvocableScheduledItem.swift */; }; + C8F6A0FC1BEE33C1007DF367 /* InvocableScheduledItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A0F81BEE33C1007DF367 /* InvocableScheduledItem.swift */; }; + C8F6A0FE1BEE42DD007DF367 /* AnonymousInvocable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A0FD1BEE42DD007DF367 /* AnonymousInvocable.swift */; }; + C8F6A0FF1BEE42DD007DF367 /* AnonymousInvocable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A0FD1BEE42DD007DF367 /* AnonymousInvocable.swift */; }; + C8F6A1001BEE42DD007DF367 /* AnonymousInvocable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A0FD1BEE42DD007DF367 /* AnonymousInvocable.swift */; }; + C8F6A1011BEE42DD007DF367 /* AnonymousInvocable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A0FD1BEE42DD007DF367 /* AnonymousInvocable.swift */; }; + CB255BD71BC46A9C00798A4C /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB255BD61BC46A9C00798A4C /* RetryWhen.swift */; }; + CB255BD81BC46A9C00798A4C /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB255BD61BC46A9C00798A4C /* RetryWhen.swift */; }; + CB255BD91BC46A9C00798A4C /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB255BD61BC46A9C00798A4C /* RetryWhen.swift */; }; + CB255BDA1BC46A9C00798A4C /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB255BD61BC46A9C00798A4C /* RetryWhen.swift */; }; CB883B3B1BE24355000AC2EE /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B3A1BE24355000AC2EE /* Window.swift */; }; CB883B3C1BE24355000AC2EE /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B3A1BE24355000AC2EE /* Window.swift */; }; CB883B3D1BE24355000AC2EE /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B3A1BE24355000AC2EE /* Window.swift */; }; @@ -584,10 +604,6 @@ CBEE77201BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; }; CBEE77211BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; }; CBEE77221BD649A000AD584C /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEE771E1BD649A000AD584C /* ToArray.swift */; }; - CB255BD71BC46A9C00798A4C /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB255BD61BC46A9C00798A4C /* RetryWhen.swift */; settings = {ASSET_TAGS = (); }; }; - CB255BD81BC46A9C00798A4C /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB255BD61BC46A9C00798A4C /* RetryWhen.swift */; settings = {ASSET_TAGS = (); }; }; - CB255BD91BC46A9C00798A4C /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB255BD61BC46A9C00798A4C /* RetryWhen.swift */; settings = {ASSET_TAGS = (); }; }; - CB255BDA1BC46A9C00798A4C /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB255BD61BC46A9C00798A4C /* RetryWhen.swift */; settings = {ASSET_TAGS = (); }; }; D203C4F31BB9C4CA00D02D00 /* RxCollectionViewReactiveArrayDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88253F11B8A752B00B02D69 /* RxCollectionViewReactiveArrayDataSource.swift */; }; D203C4F41BB9C52400D02D00 /* RxTableViewReactiveArrayDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88253F21B8A752B00B02D69 /* RxTableViewReactiveArrayDataSource.swift */; }; D203C4F51BB9C52900D02D00 /* ItemEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88253F41B8A752B00B02D69 /* ItemEvents.swift */; }; @@ -1042,12 +1058,16 @@ C8F0C0021BBBFB8B001B112F /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8F0C04B1BBBFBB9001B112F /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8F0C0581BBBFBCE001B112F /* RxBlocking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxBlocking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C8F6A0EE1BEE2CB6007DF367 /* ScheduledItemType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduledItemType.swift; sourceTree = ""; }; + C8F6A0F31BEE3395007DF367 /* InvocableType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InvocableType.swift; sourceTree = ""; }; + C8F6A0F81BEE33C1007DF367 /* InvocableScheduledItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InvocableScheduledItem.swift; sourceTree = ""; }; + C8F6A0FD1BEE42DD007DF367 /* AnonymousInvocable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnonymousInvocable.swift; sourceTree = ""; }; + CB255BD61BC46A9C00798A4C /* RetryWhen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RetryWhen.swift; sourceTree = ""; }; CB883B3A1BE24355000AC2EE /* Window.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = ""; }; CB883B3F1BE24C15000AC2EE /* RefCountDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefCountDisposable.swift; sourceTree = ""; }; CB883B441BE256D4000AC2EE /* BooleanDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BooleanDisposable.swift; sourceTree = ""; }; CB883B491BE369AA000AC2EE /* AddRef.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddRef.swift; sourceTree = ""; }; CBEE771E1BD649A000AD584C /* ToArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToArray.swift; sourceTree = ""; }; - CB255BD61BC46A9C00798A4C /* RetryWhen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RetryWhen.swift; sourceTree = ""; }; D2138C751BB9BE9800339B5C /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D2245A1A1BD5657300E7146F /* WithLatestFrom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WithLatestFrom.swift; sourceTree = ""; }; D22B6D251BC8504A00BCE0AB /* SkipWhile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipWhile.swift; sourceTree = ""; }; @@ -1331,6 +1351,10 @@ C84B38E71BA43380001B7D88 /* ScheduledItem.swift */, C8093CBB1B8A72BE0088E94D /* SchedulerServices+Emulation.swift */, C8093CBC1B8A72BE0088E94D /* SerialDispatchQueueScheduler.swift */, + C8F6A0EE1BEE2CB6007DF367 /* ScheduledItemType.swift */, + C8F6A0F31BEE3395007DF367 /* InvocableType.swift */, + C8F6A0F81BEE33C1007DF367 /* InvocableScheduledItem.swift */, + C8F6A0FD1BEE42DD007DF367 /* AnonymousInvocable.swift */, ); path = Schedulers; sourceTree = ""; @@ -2212,8 +2236,10 @@ C8093D4A1B8A72BE0088E94D /* Throttle.swift in Sources */, C8B145011BD2D80100267DCE /* ImmediateScheduler.swift in Sources */, C8093D061B8A72BE0088E94D /* Catch.swift in Sources */, + C8F6A0FA1BEE33C1007DF367 /* InvocableScheduledItem.swift in Sources */, C8093D0C1B8A72BE0088E94D /* CombineLatest.swift in Sources */, D2FC15B31BCB95E5007361FF /* SkipWhile.swift in Sources */, + C8F6A0F51BEE3395007DF367 /* InvocableType.swift in Sources */, C8093D5E1B8A72BE0088E94D /* Observable+Multiple.swift in Sources */, C8093D741B8A72BE0088E94D /* ObserverBase.swift in Sources */, C8093D121B8A72BE0088E94D /* ConnectableObservable.swift in Sources */, @@ -2230,6 +2256,7 @@ C8093D361B8A72BE0088E94D /* Sample.swift in Sources */, C84CC54F1BDCF48200E06A64 /* LockOwnerType.swift in Sources */, D2752D621BC5551A0070C418 /* SkipUntil.swift in Sources */, + C8F6A0FF1BEE42DD007DF367 /* AnonymousInvocable.swift in Sources */, C84CC5541BDCF49300E06A64 /* SynchronizedOnType.swift in Sources */, C8093CEA1B8A72BE0088E94D /* ScopedDisposable.swift in Sources */, C8093D261B8A72BE0088E94D /* Multicast.swift in Sources */, @@ -2284,6 +2311,7 @@ C8640A041BA5B12A00D3C4E8 /* Repeat.swift in Sources */, C8093CF41B8A72BE0088E94D /* Error.swift in Sources */, C8093D141B8A72BE0088E94D /* Debug.swift in Sources */, + C8F6A0F01BEE2CB6007DF367 /* ScheduledItemType.swift in Sources */, CB883B4B1BE369AA000AC2EE /* AddRef.swift in Sources */, C8093CCE1B8A72BE0088E94D /* Bag.swift in Sources */, C8093D301B8A72BE0088E94D /* Producer.swift in Sources */, @@ -2345,8 +2373,10 @@ C8093D491B8A72BE0088E94D /* Throttle.swift in Sources */, C8B145001BD2D80100267DCE /* ImmediateScheduler.swift in Sources */, C8093D051B8A72BE0088E94D /* Catch.swift in Sources */, + C8F6A0F91BEE33C1007DF367 /* InvocableScheduledItem.swift in Sources */, C8093D0B1B8A72BE0088E94D /* CombineLatest.swift in Sources */, D22B6D261BC8504A00BCE0AB /* SkipWhile.swift in Sources */, + C8F6A0F41BEE3395007DF367 /* InvocableType.swift in Sources */, C8093D5D1B8A72BE0088E94D /* Observable+Multiple.swift in Sources */, C8093D731B8A72BE0088E94D /* ObserverBase.swift in Sources */, C8093D111B8A72BE0088E94D /* ConnectableObservable.swift in Sources */, @@ -2363,6 +2393,7 @@ C8093D351B8A72BE0088E94D /* Sample.swift in Sources */, C84CC54E1BDCF48200E06A64 /* LockOwnerType.swift in Sources */, D285BAC41BC0231000B3F602 /* SkipUntil.swift in Sources */, + C8F6A0FE1BEE42DD007DF367 /* AnonymousInvocable.swift in Sources */, C84CC5531BDCF49300E06A64 /* SynchronizedOnType.swift in Sources */, C8093CE91B8A72BE0088E94D /* ScopedDisposable.swift in Sources */, C8093D251B8A72BE0088E94D /* Multicast.swift in Sources */, @@ -2417,6 +2448,7 @@ C8640A031BA5B12A00D3C4E8 /* Repeat.swift in Sources */, C8093CF31B8A72BE0088E94D /* Error.swift in Sources */, C8093D131B8A72BE0088E94D /* Debug.swift in Sources */, + C8F6A0EF1BEE2CB6007DF367 /* ScheduledItemType.swift in Sources */, CB883B4A1BE369AA000AC2EE /* AddRef.swift in Sources */, C8093CCD1B8A72BE0088E94D /* Bag.swift in Sources */, C8093D2F1B8A72BE0088E94D /* Producer.swift in Sources */, @@ -2478,8 +2510,10 @@ C8F0BFAA1BBBFB8B001B112F /* Throttle.swift in Sources */, C8B145031BD2D80100267DCE /* ImmediateScheduler.swift in Sources */, C8F0BFAC1BBBFB8B001B112F /* Catch.swift in Sources */, + C8F6A0FC1BEE33C1007DF367 /* InvocableScheduledItem.swift in Sources */, C8F0BFAD1BBBFB8B001B112F /* CombineLatest.swift in Sources */, D2FC15B51BCB95E8007361FF /* SkipWhile.swift in Sources */, + C8F6A0F71BEE3395007DF367 /* InvocableType.swift in Sources */, C8F0BFAE1BBBFB8B001B112F /* Observable+Multiple.swift in Sources */, C8F0BFAF1BBBFB8B001B112F /* ObserverBase.swift in Sources */, C8F0BFB01BBBFB8B001B112F /* ConnectableObservable.swift in Sources */, @@ -2496,6 +2530,7 @@ C8F0BFB81BBBFB8B001B112F /* Sample.swift in Sources */, C84CC5511BDCF48200E06A64 /* LockOwnerType.swift in Sources */, D21C29311BC6A1C300448E70 /* SkipUntil.swift in Sources */, + C8F6A1011BEE42DD007DF367 /* AnonymousInvocable.swift in Sources */, C84CC5561BDCF49300E06A64 /* SynchronizedOnType.swift in Sources */, C8F0BFB91BBBFB8B001B112F /* ScopedDisposable.swift in Sources */, C8F0BFBA1BBBFB8B001B112F /* Multicast.swift in Sources */, @@ -2550,6 +2585,7 @@ C8F0BFE71BBBFB8B001B112F /* Repeat.swift in Sources */, C8F0BFE81BBBFB8B001B112F /* Error.swift in Sources */, C8F0BFE91BBBFB8B001B112F /* Debug.swift in Sources */, + C8F6A0F21BEE2CB6007DF367 /* ScheduledItemType.swift in Sources */, CB883B4D1BE369AA000AC2EE /* AddRef.swift in Sources */, C8F0BFEA1BBBFB8B001B112F /* Bag.swift in Sources */, C8F0BFEB1BBBFB8B001B112F /* Producer.swift in Sources */, @@ -2762,8 +2798,10 @@ D2EBEB2D1BB9B6CA003A27DC /* Observable+Concurrency.swift in Sources */, D2EBEB381BB9B6D8003A27DC /* ConcurrentDispatchQueueScheduler.swift in Sources */, C8B145021BD2D80100267DCE /* ImmediateScheduler.swift in Sources */, + C8F6A0FB1BEE33C1007DF367 /* InvocableScheduledItem.swift in Sources */, D2EBEB131BB9B6C1003A27DC /* Multicast.swift in Sources */, D2EBEB111BB9B6C1003A27DC /* Map.swift in Sources */, + C8F6A0F61BEE3395007DF367 /* InvocableType.swift in Sources */, D2FC15B41BCB95E7007361FF /* SkipWhile.swift in Sources */, D2EBEB071BB9B6C1003A27DC /* Deferred.swift in Sources */, D2EBEB2C1BB9B6CA003A27DC /* Observable+Binding.swift in Sources */, @@ -2780,6 +2818,7 @@ D2EBEB401BB9B6DE003A27DC /* BehaviorSubject.swift in Sources */, C84CC5501BDCF48200E06A64 /* LockOwnerType.swift in Sources */, D2EBEB271BB9B6C1003A27DC /* Timer.swift in Sources */, + C8F6A1001BEE42DD007DF367 /* AnonymousInvocable.swift in Sources */, C84CC5551BDCF49300E06A64 /* SynchronizedOnType.swift in Sources */, D2752D631BC5551B0070C418 /* SkipUntil.swift in Sources */, D2EBEB351BB9B6D2003A27DC /* ObserverBase.swift in Sources */, @@ -2834,6 +2873,7 @@ D2EBEB3D1BB9B6D8003A27DC /* SchedulerServices+Emulation.swift in Sources */, D2EBEB1C1BB9B6C1003A27DC /* Sample.swift in Sources */, D2EBEAFD1BB9B6BA003A27DC /* AnonymousObservable.swift in Sources */, + C8F6A0F11BEE2CB6007DF367 /* ScheduledItemType.swift in Sources */, CB883B4C1BE369AA000AC2EE /* AddRef.swift in Sources */, D2EBEAFA1BB9B6B2003A27DC /* SingleAssignmentDisposable.swift in Sources */, D2EBEAF31BB9B6AE003A27DC /* DisposeBag.swift in Sources */, diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index fcb9a82f..3bcbc508 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -239,15 +239,6 @@ C89464FA1BC6C2B00055219D /* ObserverType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894648A1BC6C2B00055219D /* ObserverType.swift */; }; C89464FB1BC6C2B00055219D /* Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894648B1BC6C2B00055219D /* Rx.swift */; }; C89464FC1BC6C2B00055219D /* RxBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894648C1BC6C2B00055219D /* RxBox.swift */; }; - C89464FD1BC6C2B00055219D /* ConcurrentDispatchQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894648E1BC6C2B00055219D /* ConcurrentDispatchQueueScheduler.swift */; }; - C89464FE1BC6C2B00055219D /* CurrentThreadScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894648F1BC6C2B00055219D /* CurrentThreadScheduler.swift */; }; - C89464FF1BC6C2B00055219D /* DispatchQueueSchedulerPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464901BC6C2B00055219D /* DispatchQueueSchedulerPriority.swift */; }; - C89465001BC6C2B00055219D /* MainScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464911BC6C2B00055219D /* MainScheduler.swift */; }; - C89465011BC6C2B00055219D /* OperationQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464921BC6C2B00055219D /* OperationQueueScheduler.swift */; }; - C89465021BC6C2B00055219D /* RecursiveScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464931BC6C2B00055219D /* RecursiveScheduler.swift */; }; - C89465031BC6C2B00055219D /* ScheduledItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464941BC6C2B00055219D /* ScheduledItem.swift */; }; - C89465041BC6C2B00055219D /* SchedulerServices+Emulation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464951BC6C2B00055219D /* SchedulerServices+Emulation.swift */; }; - C89465051BC6C2B00055219D /* SerialDispatchQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464961BC6C2B00055219D /* SerialDispatchQueueScheduler.swift */; }; C89465061BC6C2B00055219D /* SchedulerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464971BC6C2B00055219D /* SchedulerType.swift */; }; C89465071BC6C2B00055219D /* BehaviorSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464991BC6C2B00055219D /* BehaviorSubject.swift */; }; C89465081BC6C2B00055219D /* PublishSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894649A1BC6C2B00055219D /* PublishSubject.swift */; }; @@ -326,8 +317,6 @@ C8A468F31B8A8C2600BF917B /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8A468EB1B8A8BC900BF917B /* RxSwift.framework */; }; C8A7501F1B94E77C00D8D046 /* RxDataSourceStarterKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8A7501E1B94E77C00D8D046 /* RxDataSourceStarterKit.swift */; }; C8A750201B94E78200D8D046 /* RxDataSourceStarterKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8A7501E1B94E77C00D8D046 /* RxDataSourceStarterKit.swift */; }; - C8B145141BD2E4D000267DCE /* ConcurrentMainScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B145051BD2E45200267DCE /* ConcurrentMainScheduler.swift */; }; - C8B145161BD2E4D500267DCE /* ImmediateScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B145041BD2E45200267DCE /* ImmediateScheduler.swift */; }; C8C46DA81B47F7110020D71E /* CollectionViewImageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C46DA31B47F7110020D71E /* CollectionViewImageCell.swift */; }; C8C46DA91B47F7110020D71E /* WikipediaImageCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C8C46DA41B47F7110020D71E /* WikipediaImageCell.xib */; }; C8C46DAA1B47F7110020D71E /* WikipediaSearchCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C46DA51B47F7110020D71E /* WikipediaSearchCell.swift */; }; @@ -343,6 +332,22 @@ C8DF92EB1B0B38C0009BCF9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; }; C8DF92F61B0B43A4009BCF9A /* IntroductionExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DF92F51B0B43A4009BCF9A /* IntroductionExampleViewController.swift */; }; C8E9D2AF1BD3FD960079D0DB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; }; + C8F6A1271BEF9DA3007DF367 /* AnonymousInvocable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A1041BEF9D83007DF367 /* AnonymousInvocable.swift */; }; + C8F6A1281BEF9DA3007DF367 /* InvocableScheduledItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A1051BEF9D83007DF367 /* InvocableScheduledItem.swift */; }; + C8F6A1291BEF9DA3007DF367 /* InvocableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A1061BEF9D83007DF367 /* InvocableType.swift */; }; + C8F6A12A1BEF9DA3007DF367 /* ScheduledItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A1071BEF9D83007DF367 /* ScheduledItemType.swift */; }; + C8F6A12B1BEF9DA3007DF367 /* ConcurrentDispatchQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894648E1BC6C2B00055219D /* ConcurrentDispatchQueueScheduler.swift */; }; + C8F6A12C1BEF9DA3007DF367 /* ConcurrentMainScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B145051BD2E45200267DCE /* ConcurrentMainScheduler.swift */; }; + C8F6A12D1BEF9DA3007DF367 /* CurrentThreadScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894648F1BC6C2B00055219D /* CurrentThreadScheduler.swift */; }; + C8F6A12E1BEF9DA3007DF367 /* DispatchQueueSchedulerPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464901BC6C2B00055219D /* DispatchQueueSchedulerPriority.swift */; }; + C8F6A12F1BEF9DA3007DF367 /* ImmediateScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B145041BD2E45200267DCE /* ImmediateScheduler.swift */; }; + C8F6A1301BEF9DA3007DF367 /* MainScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464911BC6C2B00055219D /* MainScheduler.swift */; }; + C8F6A1311BEF9DA3007DF367 /* OperationQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464921BC6C2B00055219D /* OperationQueueScheduler.swift */; }; + C8F6A1321BEF9DA3007DF367 /* RecursiveScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464931BC6C2B00055219D /* RecursiveScheduler.swift */; }; + C8F6A1331BEF9DA3007DF367 /* ScheduledItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464941BC6C2B00055219D /* ScheduledItem.swift */; }; + C8F6A1341BEF9DA3007DF367 /* SchedulerServices+Emulation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464951BC6C2B00055219D /* SchedulerServices+Emulation.swift */; }; + C8F6A1351BEF9DA3007DF367 /* SerialDispatchQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464961BC6C2B00055219D /* SerialDispatchQueueScheduler.swift */; }; + C8F6A1381BEF9DF6007DF367 /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A1361BEF9DD4007DF367 /* RetryWhen.swift */; }; CB883B501BE3AC54000AC2EE /* BooleanDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B4E1BE3AC54000AC2EE /* BooleanDisposable.swift */; }; CB883B511BE3AC54000AC2EE /* RefCountDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B4F1BE3AC54000AC2EE /* RefCountDisposable.swift */; }; CB883B601BE3AC72000AC2EE /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B5E1BE3AC72000AC2EE /* Window.swift */; }; @@ -737,6 +742,11 @@ C8DF92F01B0B3E67009BCF9A /* Info-OSX.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-OSX.plist"; sourceTree = ""; }; C8DF92F21B0B3E71009BCF9A /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = ""; }; C8DF92F51B0B43A4009BCF9A /* IntroductionExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = IntroductionExampleViewController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + C8F6A1041BEF9D83007DF367 /* AnonymousInvocable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnonymousInvocable.swift; sourceTree = ""; }; + C8F6A1051BEF9D83007DF367 /* InvocableScheduledItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InvocableScheduledItem.swift; sourceTree = ""; }; + C8F6A1061BEF9D83007DF367 /* InvocableType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InvocableType.swift; sourceTree = ""; }; + C8F6A1071BEF9D83007DF367 /* ScheduledItemType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduledItemType.swift; sourceTree = ""; }; + C8F6A1361BEF9DD4007DF367 /* RetryWhen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RetryWhen.swift; sourceTree = ""; }; CB883B4E1BE3AC54000AC2EE /* BooleanDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BooleanDisposable.swift; sourceTree = ""; }; CB883B4F1BE3AC54000AC2EE /* RefCountDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefCountDisposable.swift; sourceTree = ""; }; CB883B5E1BE3AC72000AC2EE /* Window.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = ""; }; @@ -1150,6 +1160,7 @@ C89464481BC6C2B00055219D /* Implementations */ = { isa = PBXGroup; children = ( + C8F6A1361BEF9DD4007DF367 /* RetryWhen.swift */, CB883B5F1BE3AC72000AC2EE /* AddRef.swift */, C89464491BC6C2B00055219D /* Amb.swift */, C894644A1BC6C2B00055219D /* AnonymousObservable.swift */, @@ -1225,6 +1236,10 @@ C894648D1BC6C2B00055219D /* Schedulers */ = { isa = PBXGroup; children = ( + C8F6A1041BEF9D83007DF367 /* AnonymousInvocable.swift */, + C8F6A1051BEF9D83007DF367 /* InvocableScheduledItem.swift */, + C8F6A1061BEF9D83007DF367 /* InvocableType.swift */, + C8F6A1071BEF9D83007DF367 /* ScheduledItemType.swift */, C894648E1BC6C2B00055219D /* ConcurrentDispatchQueueScheduler.swift */, C8B145051BD2E45200267DCE /* ConcurrentMainScheduler.swift */, C894648F1BC6C2B00055219D /* CurrentThreadScheduler.swift */, @@ -1677,19 +1692,21 @@ C84CC58B1BDD486300E06A64 /* LockOwnerType.swift in Sources */, C89465971BC6C2BC0055219D /* UIScrollView+Rx.swift in Sources */, C8297E2F1B6CF905000589EA /* RxTableViewSectionedAnimatedDataSource.swift in Sources */, - C89464FF1BC6C2B00055219D /* DispatchQueueSchedulerPriority.swift in Sources */, C89464D41BC6C2B00055219D /* ObserveOn.swift in Sources */, C894659B1BC6C2BC0055219D /* UIStepper+Rx.swift in Sources */, C8A7501F1B94E77C00D8D046 /* RxDataSourceStarterKit.swift in Sources */, C89464F61BC6C2B00055219D /* AnonymousObserver.swift in Sources */, C89464C81BC6C2B00055219D /* DistinctUntilChanged.swift in Sources */, C89464CF1BC6C2B00055219D /* Just.swift in Sources */, + C8F6A1381BEF9DF6007DF367 /* RetryWhen.swift in Sources */, C8297E301B6CF905000589EA /* RandomUserAPI.swift in Sources */, C89465921BC6C2BC0055219D /* UIControl+Rx.swift in Sources */, C894650B1BC6C2B00055219D /* Variable.swift in Sources */, C8297E311B6CF905000589EA /* SearchResultViewModel.swift in Sources */, + C8F6A12D1BEF9DA3007DF367 /* CurrentThreadScheduler.swift in Sources */, C89464D91BC6C2B00055219D /* RefCount.swift in Sources */, C89465731BC6C2BC0055219D /* Deallocating.swift in Sources */, + C8F6A1271BEF9DA3007DF367 /* AnonymousInvocable.swift in Sources */, C89464A51BC6C2B00055219D /* Disposable.swift in Sources */, C89464F91BC6C2B00055219D /* ObserverType+Extensions.swift in Sources */, C84CC58D1BDD486300E06A64 /* SynchronizedOnType.swift in Sources */, @@ -1709,6 +1726,7 @@ C89465651BC6C2BC0055219D /* CLLocationManager+Rx.swift in Sources */, C80DDED81BCE9046006A1832 /* Driver.swift in Sources */, C80DDED71BCE9046006A1832 /* Driver+Subscription.swift in Sources */, + C8F6A1321BEF9DA3007DF367 /* RecursiveScheduler.swift in Sources */, 07A5C3DC1B70B703001EFE5C /* CalculatorViewController.swift in Sources */, C89465961BC6C2BC0055219D /* UILabel+Rx.swift in Sources */, C894659C1BC6C2BC0055219D /* UISwitch+Rx.swift in Sources */, @@ -1720,9 +1738,7 @@ C8297E341B6CF905000589EA /* UIImageView+Extensions.swift in Sources */, B1604CC41BE5B8CE002E1279 /* DownloadableImage.swift in Sources */, C80DDED61BCE9046006A1832 /* Driver+Operators.swift in Sources */, - C89465051BC6C2B00055219D /* SerialDispatchQueueScheduler.swift in Sources */, C89464B81BC6C2B00055219D /* Observable.swift in Sources */, - C89465001BC6C2B00055219D /* MainScheduler.swift in Sources */, C8297E351B6CF905000589EA /* NumberSectionView.swift in Sources */, C894659E1BC6C2BC0055219D /* UITextField+Rx.swift in Sources */, C89464D11BC6C2B00055219D /* Merge.swift in Sources */, @@ -1738,7 +1754,6 @@ C8297E391B6CF905000589EA /* CollectionViewImageCell.swift in Sources */, C894649E1BC6C2B00055219D /* Cancelable.swift in Sources */, C89464E01BC6C2B00055219D /* SubscribeOn.swift in Sources */, - C89465031BC6C2B00055219D /* ScheduledItem.swift in Sources */, C89465801BC6C2BC0055219D /* RxTableViewReactiveArrayDataSource.swift in Sources */, C89464AF1BC6C2B00055219D /* ScopedDisposable.swift in Sources */, C89464FA1BC6C2B00055219D /* ObserverType.swift in Sources */, @@ -1762,7 +1777,7 @@ C89464F21BC6C2B00055219D /* Observable+StandardSequenceOperators.swift in Sources */, C89464CC1BC6C2B00055219D /* Filter.swift in Sources */, C80DDED31BCE9046006A1832 /* ControlProperty+Driver.swift in Sources */, - C89465021BC6C2B00055219D /* RecursiveScheduler.swift in Sources */, + C8F6A12E1BEF9DA3007DF367 /* DispatchQueueSchedulerPriority.swift in Sources */, C89464C11BC6C2B00055219D /* CombineLatest+CollectionType.swift in Sources */, C89465671BC6C2BC0055219D /* ControlEvent.swift in Sources */, C8297E3B1B6CF905000589EA /* String+extensions.swift in Sources */, @@ -1777,7 +1792,6 @@ B1604CC21BE5B895002E1279 /* ReachabilityService.swift in Sources */, C89464DE1BC6C2B00055219D /* Skip.swift in Sources */, C89464DB1BC6C2B00055219D /* Sample.swift in Sources */, - C89465011BC6C2B00055219D /* OperationQueueScheduler.swift in Sources */, C89465791BC6C2BC0055219D /* NSObject+Rx.swift in Sources */, C89464F31BC6C2B00055219D /* Observable+Time.swift in Sources */, C89464F11BC6C2B00055219D /* Observable+Single.swift in Sources */, @@ -1796,18 +1810,20 @@ C8297E401B6CF905000589EA /* ImageService.swift in Sources */, C89464AD1BC6C2B00055219D /* NopDisposable.swift in Sources */, C84CC5901BDD486300E06A64 /* AsyncLock.swift in Sources */, + C8F6A1311BEF9DA3007DF367 /* OperationQueueScheduler.swift in Sources */, CBEE77541BD8C7B700AD584C /* ToArray.swift in Sources */, C89465771BC6C2BC0055219D /* NSNotificationCenter+Rx.swift in Sources */, C89465091BC6C2B00055219D /* ReplaySubject.swift in Sources */, + C8F6A1281BEF9DA3007DF367 /* InvocableScheduledItem.swift in Sources */, C8297E411B6CF905000589EA /* RxCollectionViewSectionedReloadDataSource.swift in Sources */, C84CC58E1BDD486300E06A64 /* SynchronizedSubscribeType.swift in Sources */, + C8F6A12F1BEF9DA3007DF367 /* ImmediateScheduler.swift in Sources */, C89464A81BC6C2B00055219D /* CompositeDisposable.swift in Sources */, C89464D21BC6C2B00055219D /* Multicast.swift in Sources */, C89465821BC6C2BC0055219D /* RxCollectionViewDataSourceType.swift in Sources */, C8297E421B6CF905000589EA /* WikipediaSearchResult.swift in Sources */, C89465701BC6C2BC0055219D /* Logging.swift in Sources */, C89464A31BC6C2B00055219D /* InfiniteSequence.swift in Sources */, - C89465041BC6C2B00055219D /* SchedulerServices+Emulation.swift in Sources */, C89465611BC6C2BC0055219D /* _RX.m in Sources */, C80DDED41BCE9046006A1832 /* Driver+Operators+arity.swift in Sources */, C89465841BC6C2BC0055219D /* RxActionSheetDelegateProxy.swift in Sources */, @@ -1816,12 +1832,12 @@ C803974A1BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift in Sources */, C84CC52E1BDC344100E06A64 /* ElementAt.swift in Sources */, C8297E431B6CF905000589EA /* GitHubAPI.swift in Sources */, + C8F6A1331BEF9DA3007DF367 /* ScheduledItem.swift in Sources */, C89464EB1BC6C2B00055219D /* Observable+Aggregate.swift in Sources */, C8297E441B6CF905000589EA /* PseudoRandomGenerator.swift in Sources */, C8297E451B6CF905000589EA /* SectionedViewType.swift in Sources */, C89464D51BC6C2B00055219D /* ObserveOnSerialDispatchQueue.swift in Sources */, C894658E1BC6C2BC0055219D /* UIAlertView+Rx.swift in Sources */, - C8B145161BD2E4D500267DCE /* ImmediateScheduler.swift in Sources */, C8297E461B6CF905000589EA /* Example.swift in Sources */, C89465081BC6C2B00055219D /* PublishSubject.swift in Sources */, C89464FC1BC6C2B00055219D /* RxBox.swift in Sources */, @@ -1833,7 +1849,6 @@ B18F3BC11BD93E00000AAC79 /* Reachability.swift in Sources */, C89465901BC6C2BC0055219D /* UIButton+Rx.swift in Sources */, C89464DD1BC6C2B00055219D /* Sink.swift in Sources */, - C89464FE1BC6C2B00055219D /* CurrentThreadScheduler.swift in Sources */, C89464BE1BC6C2B00055219D /* Catch.swift in Sources */, C89CDB721BCC45EE002063D9 /* SkipUntil.swift in Sources */, B1604CCB1BE5BC45002E1279 /* UIImageView+DownloadableImage.swift in Sources */, @@ -1843,8 +1858,8 @@ C89465951BC6C2BC0055219D /* UIImageView+Rx.swift in Sources */, C89464E31BC6C2B00055219D /* TakeUntil.swift in Sources */, C89464FB1BC6C2B00055219D /* Rx.swift in Sources */, - C89464FD1BC6C2B00055219D /* ConcurrentDispatchQueueScheduler.swift in Sources */, C84CC5911BDD48B800E06A64 /* SubscriptionDisposable.swift in Sources */, + C8F6A1341BEF9DA3007DF367 /* SchedulerServices+Emulation.swift in Sources */, C89464C71BC6C2B00055219D /* DelaySubscription.swift in Sources */, C8297E481B6CF905000589EA /* Differentiator.swift in Sources */, C8297E491B6CF905000589EA /* WikipediaSearchCell.swift in Sources */, @@ -1855,6 +1870,8 @@ C8297E4C1B6CF905000589EA /* APIWrappersViewController.swift in Sources */, C89465621BC6C2BC0055219D /* _RXDelegateProxy.m in Sources */, C89464D31BC6C2B00055219D /* Never.swift in Sources */, + C8F6A12A1BEF9DA3007DF367 /* ScheduledItemType.swift in Sources */, + C8F6A1351BEF9DA3007DF367 /* SerialDispatchQueueScheduler.swift in Sources */, C8297E4D1B6CF905000589EA /* RxTableViewSectionedReloadDataSource.swift in Sources */, C89465931BC6C2BC0055219D /* UIDatePicker+Rx.swift in Sources */, C84CC58F1BDD486300E06A64 /* SynchronizedUnsubscribeType.swift in Sources */, @@ -1862,9 +1879,11 @@ C89CDB711BCC45E5002063D9 /* ShareReplay1.swift in Sources */, C89464BD1BC6C2B00055219D /* Buffer.swift in Sources */, C89464B31BC6C2B00055219D /* Error.swift in Sources */, + C8F6A1301BEF9DA3007DF367 /* MainScheduler.swift in Sources */, C89464C51BC6C2B00055219D /* Debug.swift in Sources */, C89464AB1BC6C2B00055219D /* NAryDisposable.swift in Sources */, C89465631BC6C2BC0055219D /* _RXKVOObserver.m in Sources */, + C8F6A12B1BEF9DA3007DF367 /* ConcurrentDispatchQueueScheduler.swift in Sources */, C894656E1BC6C2BC0055219D /* DelegateProxy.swift in Sources */, C89464EF1BC6C2B00055219D /* Observable+Debug.swift in Sources */, C89464E91BC6C2B00055219D /* Zip+CollectionType.swift in Sources */, @@ -1878,7 +1897,6 @@ D2AF91981BD3D95900A008C1 /* Using.swift in Sources */, C8297E501B6CF905000589EA /* TableViewController.swift in Sources */, C8297E511B6CF905000589EA /* PartialUpdatesViewController.swift in Sources */, - C8B145141BD2E4D000267DCE /* ConcurrentMainScheduler.swift in Sources */, C8297E521B6CF905000589EA /* Dependencies.swift in Sources */, C80DDED91BCE9046006A1832 /* ObservableConvertibleType+Driver.swift in Sources */, C89464E11BC6C2B00055219D /* Switch.swift in Sources */, @@ -1888,11 +1906,13 @@ C894658B1BC6C2BC0055219D /* RxTableViewDelegateProxy.swift in Sources */, C8297E531B6CF905000589EA /* WikipediaAPI.swift in Sources */, C89465071BC6C2B00055219D /* BehaviorSubject.swift in Sources */, + C8F6A1291BEF9DA3007DF367 /* InvocableType.swift in Sources */, C89465911BC6C2BC0055219D /* UICollectionView+Rx.swift in Sources */, C89465781BC6C2BC0055219D /* NSObject+Rx+CoreGraphics.swift in Sources */, C89464D01BC6C2B00055219D /* Map.swift in Sources */, C8297E541B6CF905000589EA /* AppDelegate.swift in Sources */, C894659D1BC6C2BC0055219D /* UITableView+Rx.swift in Sources */, + C8F6A12C1BEF9DA3007DF367 /* ConcurrentMainScheduler.swift in Sources */, C894657F1BC6C2BC0055219D /* RxCollectionViewReactiveArrayDataSource.swift in Sources */, C8297E551B6CF905000589EA /* RxTableViewSectionedDataSource.swift in Sources */, C8297E561B6CF905000589EA /* WikipediaPage.swift in Sources */, diff --git a/RxSwift/AnyObserver.swift b/RxSwift/AnyObserver.swift index a22101bc..fff46cd4 100644 --- a/RxSwift/AnyObserver.swift +++ b/RxSwift/AnyObserver.swift @@ -41,9 +41,7 @@ public struct AnyObserver : ObserverType { - parameter observer: Observer that receives sequence events. */ public init(_ observer: O) { - self.observer = { e in - return observer.on(e) - } + self.observer = observer.on } /** @@ -54,6 +52,15 @@ public struct AnyObserver : ObserverType { public func on(event: Event) { return self.observer(event) } + + /** + Erases type of observer and returns canonical observer. + + - returns: type erased observer. + */ + func asObserver() -> AnyObserver { + return self + } } extension ObserverType { diff --git a/RxSwift/Concurrency/AsyncLock.swift b/RxSwift/Concurrency/AsyncLock.swift index 04b57b6d..e453f992 100644 --- a/RxSwift/Concurrency/AsyncLock.swift +++ b/RxSwift/Concurrency/AsyncLock.swift @@ -18,50 +18,74 @@ and pending work. That means that enqueued work could possibly be executed later on a different thread. */ -class AsyncLock : Disposable { +class AsyncLock + : Disposable + , Lock + , SynchronizedDisposeType { typealias Action = () -> Void - private let _lock = NSRecursiveLock() + var _lock = SpinLock() - private var _queue: Queue = Queue(capacity: 2) + private var _queue: Queue = Queue(capacity: 0) - private var _isAcquired: Bool = false + private var _isExecuting: Bool = false private var _hasFaulted: Bool = false - - init() { - + + // lock { + func lock() { + _lock.lock() } - - func wait(action: Action) { - let isOwner = _lock.calculateLocked { () -> Bool in + + func unlock() { + _lock.unlock() + } + // } + + private func enqueue(action: I) -> I? { + _lock.lock(); defer { _lock.unlock() } // { if _hasFaulted { - return false + return nil } - - _queue.enqueue(action) - let isOwner = !_isAcquired - _isAcquired = true - - return isOwner - } + + if _isExecuting { + _queue.enqueue(action) + return nil + } + + _isExecuting = true + + return action + // } + } + + private func dequeue() -> I? { + _lock.lock(); defer { _lock.unlock() } // { + if _queue.count > 0 { + return _queue.dequeue() + } + else { + _isExecuting = false + return nil + } + // } + } + + func invoke(action: I) { + let firstEnqueuedAction = enqueue(action) - if !isOwner { + if let firstEnqueuedAction = firstEnqueuedAction { + firstEnqueuedAction.invoke() + } + else { + // action is enqueued, it's somebody else's concern now return } while true { - let nextAction = _lock.calculateLocked { () -> Action? in - if _queue.count > 0 { - return _queue.dequeue() - } - else { - _isAcquired = false - return nil - } - } - + let nextAction = dequeue() + if let nextAction = nextAction { - nextAction() + nextAction.invoke() } else { return @@ -70,9 +94,11 @@ class AsyncLock : Disposable { } func dispose() { - _lock.performLocked { oldState in - _queue = Queue(capacity: 0) - _hasFaulted = true - } + synchronizedDispose() + } + + func _synchronized_dispose() { + _queue = Queue(capacity: 0) + _hasFaulted = true } } diff --git a/RxSwift/Observables/Implementations/AnonymousObservable.swift b/RxSwift/Observables/Implementations/AnonymousObservable.swift index 3c981883..9ea13e16 100644 --- a/RxSwift/Observables/Implementations/AnonymousObservable.swift +++ b/RxSwift/Observables/Implementations/AnonymousObservable.swift @@ -29,8 +29,8 @@ class AnonymousObservableSink : Sink, ObserverType { forwardOn(event) case .Error, .Completed: if OSAtomicCompareAndSwap32(0, 1, &_isStopped) { - self.forwardOn(event) - self.dispose() + forwardOn(event) + dispose() } } } diff --git a/RxSwift/Observables/Implementations/Catch.swift b/RxSwift/Observables/Implementations/Catch.swift index f1077b60..adf53c0e 100644 --- a/RxSwift/Observables/Implementations/Catch.swift +++ b/RxSwift/Observables/Implementations/Catch.swift @@ -95,7 +95,9 @@ class Catch : Producer { // catch enumerable -class CatchSequenceSink : TailRecursiveSink { +class CatchSequenceSink + : TailRecursiveSink + , ObserverType { typealias Element = O.E typealias Parent = CatchSequence @@ -105,18 +107,22 @@ class CatchSequenceSink) { + func on(event: Event) { switch event { case .Next: forwardOn(event) case .Error(let error): _lastError = error - scheduleMoveNext() + schedule(.MoveNext) case .Completed: forwardOn(event) dispose() } } + + override func subscribeToNext(source: Observable) -> Disposable { + return source.subscribe(self) + } override func done() { if let lastError = _lastError { diff --git a/RxSwift/Observables/Implementations/Concat.swift b/RxSwift/Observables/Implementations/Concat.swift index 9aac20b0..c8f5205a 100644 --- a/RxSwift/Observables/Implementations/Concat.swift +++ b/RxSwift/Observables/Implementations/Concat.swift @@ -9,14 +9,16 @@ import Foundation -class ConcatSink : TailRecursiveSink { +class ConcatSink + : TailRecursiveSink + , ObserverType { typealias Element = O.E override init(observer: O) { super.init(observer: observer) } - override func on(event: Event){ + func on(event: Event){ switch event { case .Next: forwardOn(event) @@ -24,9 +26,13 @@ class ConcatSink) -> Disposable { + return source.subscribe(self) + } override func extract(observable: Observable) -> S.Generator? { if let source = observable as? Concat { diff --git a/RxSwift/Observables/Implementations/RetryWhen.swift b/RxSwift/Observables/Implementations/RetryWhen.swift index c32f1a93..3ab3fa0d 100644 --- a/RxSwift/Observables/Implementations/RetryWhen.swift +++ b/RxSwift/Observables/Implementations/RetryWhen.swift @@ -8,126 +8,145 @@ import Foundation -class RetryTriggerSink : ObserverType { - typealias E = RetryTriggerType +class RetryTriggerSink + : ObserverType { + typealias E = TriggerObservable.E - typealias Parent = RetryWhenSequenceSink - - let parent: Parent + typealias Parent = RetryWhenSequenceSinkIter + private let _parent: Parent + init(parent: Parent) { - self.parent = parent + _parent = parent } - + func on(event: Event) { - switch event { - case .Next: - parent.lock.performLocked() { - parent.handlerSubscription.dispose() - parent.lastError = nil - parent.scheduleMoveNext() - } - case .Error(_): - parent.lock.performLocked() { - parent.done() - } - case .Completed: - parent.lock.performLocked() { - parent.lastError = nil - parent.done() - } - } + switch event { + case .Next: + _parent._parent._lastError = nil + _parent._parent.schedule(.MoveNext) + case .Error(let e): + _parent._parent.forwardOn(.Error(e)) + _parent._parent.dispose() + case .Completed: + _parent._parent.forwardOn(.Completed) + _parent._parent.dispose() + } } } -class RetryWhenSequenceSink : TailRecursiveSink { - typealias Element = O.E - typealias Parent = RetryWhenSequence - - let lock = NSRecursiveLock() - - let parent: Parent - - var lastError: ErrorType? - let errorSubject = PublishSubject() - let handler: Observable - let handlerSubscription = SerialDisposable() - - init(parent: Parent, observer: O, cancel: Disposable) { - self.parent = parent - self.handler = parent.notificationHandler(errorSubject.asObservable()) - super.init(observer: observer, cancel: cancel) +class RetryWhenSequenceSinkIter + : SingleAssignmentDisposable + , ObserverType { + typealias E = O.E + typealias Parent = RetryWhenSequenceSink + + private let _parent: Parent + private let _errorHandlerSubscription = SingleAssignmentDisposable() + + init(parent: Parent) { + _parent = parent } - - override func on(event: Event) { - guard lastError == nil else { - return - } - + + func on(event: Event) { switch event { case .Next: - observer?.on(event) + _parent.forwardOn(event) case .Error(let error): - lock.performLocked() { - self.lastError = error - handlerSubscription.disposable = handler.subscribeSafe(RetryTriggerSink(parent: self)) - errorSubject.on(.Next(error as! Error)) + _parent._lastError = error + + if let failedWith = error as? Error { + // dispose current subscription + super.dispose() + + let errorHandlerSubscription = _parent._notifier.subscribe(RetryTriggerSink(parent: self)) + _errorHandlerSubscription.disposable = errorHandlerSubscription + _parent._errorSubject.on(.Next(failedWith)) + } + else { + _parent.forwardOn(.Error(error)) + _parent.dispose() } case .Completed: - observer?.on(event) - handlerSubscription.dispose() - dispose() + _parent.forwardOn(event) + _parent.dispose() } } + + override func dispose() { + super.dispose() + _errorHandlerSubscription.dispose() + } +} + +class RetryWhenSequenceSink + : TailRecursiveSink { + typealias Element = O.E + typealias Parent = RetryWhenSequence + + let _lock = NSRecursiveLock() + + private let _parent: Parent + + private var _lastError: ErrorType? + private let _errorSubject = PublishSubject() + private let _handler: Observable + private let _notifier = PublishSubject() + + init(parent: Parent, observer: O) { + _parent = parent + _handler = parent._notificationHandler(_errorSubject).asObservable() + super.init(observer: observer) + } override func done() { - if let lastError = self.lastError { - observer?.on(.Error(lastError)) - self.lastError = nil + if let lastError = _lastError { + forwardOn(.Error(lastError)) + _lastError = nil } else { - observer?.on(.Completed) + forwardOn(.Completed) } - self.dispose() - } - - override func dispose() { - - super.dispose() + + dispose() } override func extract(observable: Observable) -> S.Generator? { - if let onError = observable as? RetryWhenSequence { - return onError.sources.generate() + if let onError = observable as? RetryWhenSequence { + return onError._sources.generate() } else { return nil } } - + + override func subscribeToNext(source: Observable) -> Disposable { + let iter = RetryWhenSequenceSinkIter(parent: self) + iter.disposable = source.subscribe(iter) + return iter + } + override func run(sources: S.Generator) -> Disposable { - _generators.append(sources) - - scheduleMoveNext() - - return handlerSubscription + let triggerSubscription = _handler.subscribe(_notifier.asObserver()) + let superSubscription = super.run(sources) + return StableCompositeDisposable.create(superSubscription, triggerSubscription) } } -class RetryWhenSequence : Producer { +class RetryWhenSequence : Producer { typealias Element = S.Generator.Element.E - let sources: S - let notificationHandler: Observable -> Observable + private let _sources: S + private let _notificationHandler: Observable -> TriggerObservable - init(sources: S, notificationHandler: Observable -> Observable) { - self.sources = sources - self.notificationHandler = notificationHandler + init(sources: S, notificationHandler: Observable -> TriggerObservable) { + _sources = sources + _notificationHandler = notificationHandler } - override func run(observer: O, cancel: Disposable, setSink: (Disposable) -> Void) -> Disposable { - let sink = RetryWhenSequenceSink(parent: self, observer: observer, cancel: cancel) - setSink(sink) - return sink.run(self.sources.generate()) + override func run(observer: O) -> Disposable { + let sink = RetryWhenSequenceSink(parent: self, observer: observer) + sink.disposable = sink.run(self._sources.generate()) + return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Sink.swift b/RxSwift/Observables/Implementations/Sink.swift index fec16dd0..a8ef5c33 100644 --- a/RxSwift/Observables/Implementations/Sink.swift +++ b/RxSwift/Observables/Implementations/Sink.swift @@ -11,7 +11,7 @@ import Foundation class Sink : SingleAssignmentDisposable { private let _observer: O - func forwardOn(event: Event) { + final func forwardOn(event: Event) { if disposed { return } diff --git a/RxSwift/Observables/Observable+Single.swift b/RxSwift/Observables/Observable+Single.swift index 0b37549a..54df9070 100644 --- a/RxSwift/Observables/Observable+Single.swift +++ b/RxSwift/Observables/Observable+Single.swift @@ -157,7 +157,21 @@ extension ObservableType { - parameter notificationHandler: A handler that is passed an observable sequence of errors raised by the source observable and returns and observable that either continues, completes or errors. This behavior is then applied to the source observable. - returns: An observable sequence producing the elements of the given sequence repeatedly until it terminates successfully or is notified to error or complete. */ - public func retryWhen(notificationHandler: Observable -> Observable) + @warn_unused_result(message="http://git.io/rxs.uo") + public func retryWhen(notificationHandler: Observable -> TriggerObservable) + -> Observable { + return RetryWhenSequence(sources: InfiniteSequence(repeatedValue: self.asObservable()), notificationHandler: notificationHandler) + } + + /** + Repeats the source observable sequence on error when the notifier emits a next value. + If the source observable errors and the notifier completes, it will complete the source sequence. + + - parameter notificationHandler: A handler that is passed an observable sequence of errors raised by the source observable and returns and observable that either continues, completes or errors. This behavior is then applied to the source observable. + - returns: An observable sequence producing the elements of the given sequence repeatedly until it terminates successfully or is notified to error or complete. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func retryWhen(notificationHandler: Observable -> TriggerObservable) -> Observable { return RetryWhenSequence(sources: InfiniteSequence(repeatedValue: self.asObservable()), notificationHandler: notificationHandler) } diff --git a/RxSwift/Observers/TailRecursiveSink.swift b/RxSwift/Observers/TailRecursiveSink.swift index c650b89b..6f4a85f6 100644 --- a/RxSwift/Observers/TailRecursiveSink.swift +++ b/RxSwift/Observers/TailRecursiveSink.swift @@ -8,8 +8,16 @@ import Foundation +enum TailRecursiveSinkCommand { + case MoveNext + case Dispose +} + /// This class is usually used with `Generator` version of the operators. -class TailRecursiveSink : Sink, ObserverType { +class TailRecursiveSink + : Sink + , InvocableWithValueType { + typealias Value = TailRecursiveSinkCommand typealias E = O.E var _generators:[S.Generator] = [] @@ -17,7 +25,7 @@ class TailRecursiveSink>>() override init(observer: O) { super.init(observer: observer) @@ -25,27 +33,24 @@ class TailRecursiveSink Disposable { _generators.append(sources) + + schedule(.MoveNext) - scheduleMoveNext() - - let disposeSinkStack = AnonymousDisposable { - self.schedule { - self.disposePrivate() - } - } - - return StableCompositeDisposable.create(_subscription, disposeSinkStack) + return _subscription } - - func scheduleMoveNext() { - return schedule { - self.moveNext() + + func invoke(command: TailRecursiveSinkCommand) { + switch command { + case .Dispose: + disposeCommand() + case .MoveNext: + moveNextCommand() } } // simple implementation for now - func schedule(action: () -> Void) { - _gate.wait(action) + func schedule(command: TailRecursiveSinkCommand) { + _gate.invoke(InvocableScheduledItem(invocable: self, state: command)) } func done() { @@ -57,13 +62,9 @@ class TailRecursiveSink) { - abstractMethod() - } - // should be done on gate locked - private func moveNext() { + private func moveNextCommand() { var next: Observable? = nil repeat { @@ -101,13 +102,24 @@ class TailRecursiveSink) -> Disposable { + abstractMethod() + } + + func disposeCommand() { _disposed = true _generators.removeAll(keepCapacity: false) } - + + override func dispose() { + super.dispose() + + _subscription.dispose() + + schedule(.Dispose) + } } diff --git a/RxSwift/Schedulers/AnonymousInvocable.swift b/RxSwift/Schedulers/AnonymousInvocable.swift new file mode 100644 index 00000000..8525db45 --- /dev/null +++ b/RxSwift/Schedulers/AnonymousInvocable.swift @@ -0,0 +1,21 @@ +// +// AnonymousInvocable.swift +// Rx +// +// Created by Krunoslav Zaher on 11/7/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +struct AnonymousInvocable : InvocableType { + private let _action: () -> () + + init(_ action: () -> ()) { + _action = action + } + + func invoke() { + _action() + } +} \ No newline at end of file diff --git a/RxSwift/Schedulers/CurrentThreadScheduler.swift b/RxSwift/Schedulers/CurrentThreadScheduler.swift index ada3b894..e2d5b2b4 100644 --- a/RxSwift/Schedulers/CurrentThreadScheduler.swift +++ b/RxSwift/Schedulers/CurrentThreadScheduler.swift @@ -136,7 +136,7 @@ public class CurrentThreadScheduler : ImmediateSchedulerType { CurrentThreadScheduler.queue = queue } - let scheduledItem = ScheduledItem(action: action, state: state, time: 0) + let scheduledItem = ScheduledItem(action: action, state: state) queue.value.enqueue(scheduledItem) return scheduledItem } diff --git a/RxSwift/Schedulers/ImmediateScheduler.swift b/RxSwift/Schedulers/ImmediateScheduler.swift index 53ef92fc..fc54fd51 100644 --- a/RxSwift/Schedulers/ImmediateScheduler.swift +++ b/RxSwift/Schedulers/ImmediateScheduler.swift @@ -13,7 +13,7 @@ Represents an object that schedules units of work to run immediately on the curr */ private class ImmediateScheduler : ImmediateSchedulerType { - private let _asyncLock = AsyncLock() + private let _asyncLock = AsyncLock() /** Schedules an action to be executed immediatelly. @@ -27,12 +27,12 @@ private class ImmediateScheduler : ImmediateSchedulerType { */ func schedule(state: StateType, action: (StateType) -> Disposable) -> Disposable { let disposable = SingleAssignmentDisposable() - _asyncLock.wait { + _asyncLock.invoke(AnonymousInvocable { if disposable.disposed { return } disposable.disposable = action(state) - } + }) return disposable } diff --git a/RxSwift/Schedulers/InvocableScheduledItem.swift b/RxSwift/Schedulers/InvocableScheduledItem.swift new file mode 100644 index 00000000..1a4e3aa4 --- /dev/null +++ b/RxSwift/Schedulers/InvocableScheduledItem.swift @@ -0,0 +1,24 @@ +// +// InvocableScheduledItem.swift +// Rx +// +// Created by Krunoslav Zaher on 11/7/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +struct InvocableScheduledItem : InvocableType { + + let _invocable: I + let _state: I.Value + + init(invocable: I, state: I.Value) { + _invocable = invocable + _state = state + } + + func invoke() { + _invocable.invoke(_state) + } +} \ No newline at end of file diff --git a/RxSwift/Schedulers/InvocableType.swift b/RxSwift/Schedulers/InvocableType.swift new file mode 100644 index 00000000..0f1bcee7 --- /dev/null +++ b/RxSwift/Schedulers/InvocableType.swift @@ -0,0 +1,19 @@ +// +// InvocableType.swift +// Rx +// +// Created by Krunoslav Zaher on 11/7/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +protocol InvocableType { + func invoke() +} + +protocol InvocableWithValueType { + typealias Value + + func invoke(value: Value) +} \ No newline at end of file diff --git a/RxSwift/Schedulers/ScheduledItem.swift b/RxSwift/Schedulers/ScheduledItem.swift index e4b7d45e..d29bef4a 100644 --- a/RxSwift/Schedulers/ScheduledItem.swift +++ b/RxSwift/Schedulers/ScheduledItem.swift @@ -8,40 +8,32 @@ import Foundation -protocol ScheduledItemType : Cancelable { - var time: Int { - get - } - - func invoke() -} - -class ScheduledItem : ScheduledItemType { +struct ScheduledItem + : ScheduledItemType + , InvocableType { typealias Action = T -> Disposable - let action: Action - let state: T - let time: Int - + private let _action: Action + private let _state: T + + private let _disposable = SingleAssignmentDisposable() + var disposed: Bool { get { - return disposable.disposed + return _disposable.disposed } } - var disposable = SingleAssignmentDisposable() - - init(action: Action, state: T, time: Int) { - self.action = action - self.state = state - self.time = time + init(action: Action, state: T) { + _action = action + _state = state } func invoke() { - self.disposable.disposable = action(state) + _disposable.disposable = _action(_state) } func dispose() { - self.disposable.dispose() + _disposable.dispose() } -} +} \ No newline at end of file diff --git a/RxSwift/Schedulers/ScheduledItemType.swift b/RxSwift/Schedulers/ScheduledItemType.swift new file mode 100644 index 00000000..2a1eca27 --- /dev/null +++ b/RxSwift/Schedulers/ScheduledItemType.swift @@ -0,0 +1,15 @@ +// +// ScheduledItemType.swift +// Rx +// +// Created by Krunoslav Zaher on 11/7/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +protocol ScheduledItemType + : Cancelable + , InvocableType { + func invoke() +} diff --git a/RxTests/RxSwiftTests/TestImplementations/Mocks/ColdObservable.swift b/RxTests/RxSwiftTests/TestImplementations/Mocks/ColdObservable.swift index 51888890..3b25e1a6 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Mocks/ColdObservable.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Mocks/ColdObservable.swift @@ -18,36 +18,29 @@ class ColdObservable : ObservableType, ObservableConvertible var subscriptions: [Subscription] var recordedEvents: [Events] - var observers: Bag init(testScheduler: TestScheduler, recordedEvents: [Events]) { self.testScheduler = testScheduler self.recordedEvents = recordedEvents self.subscriptions = [] - self.observers = Bag() } func subscribe(observer: O) -> Disposable { - let key = observers.insert(AnyObserver(observer)) subscriptions.append(Subscription(self.testScheduler.now)) let i = self.subscriptions.count - 1 for recordedEvent in recordedEvents { - testScheduler.scheduleRelative((), dueTime: recordedEvent.time, action: { (Int) in - self.observers.forEach { $0.on(recordedEvent.event) } + testScheduler.scheduleRelative((), dueTime: recordedEvent.time, action: { (_) in + observer.on(recordedEvent.event) return NopDisposable.instance }) } return AnonymousDisposable { - let removed = self.observers.removeKey(key) - assert(removed != nil); - let existing = self.subscriptions[i] self.subscriptions[i] = Subscription(existing.subscribe, self.testScheduler.now) - } } } \ No newline at end of file diff --git a/RxTests/RxSwiftTests/Tests/Observable+SingleTest.swift b/RxTests/RxSwiftTests/Tests/Observable+SingleTest.swift index a9c73511..68dee9cd 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+SingleTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+SingleTest.swift @@ -766,8 +766,39 @@ extension ObservableSingleTest { Subscription(200, 450), ]) } - - + + func testRetry_tailRecursiveOptimizationsTest() { + var count = 1 + let sequenceSendingImmediateError: Observable = create { observer in + observer.on(.Next(0)) + observer.on(.Next(1)) + observer.on(.Next(2)) + if count < 2 { + observer.on(.Error(testError)) + count++ + } + observer.on(.Next(3)) + observer.on(.Next(4)) + observer.on(.Next(5)) + observer.on(.Completed) + + return NopDisposable.instance + } + + _ = sequenceSendingImmediateError + .retry() + .subscribe { _ in + } + } +} + +struct CustomErrorType : ErrorType { + +} + +// retryWhen +extension ObservableSingleTest { + func testRetryWhen_Never() { let scheduler = TestScheduler(initialClock: 0) @@ -783,9 +814,9 @@ extension ObservableSingleTest { ]) let res = scheduler.start(300) { - xs.retryWhen({ (errors: Observable) in - return empty.asObservable() - }) + xs.retryWhen { (errors: Observable) in + return empty + } } let correct: [Recorded] = [ @@ -817,9 +848,9 @@ extension ObservableSingleTest { ]) let res = scheduler.start() { - xs.retryWhen({ (errors: Observable) in - return never.asObservable() - }) + xs.retryWhen { (errors: Observable) in + return never + } } let correct: [Recorded] = [ @@ -832,7 +863,7 @@ extension ObservableSingleTest { XCTAssertEqual(res.messages, correct) XCTAssertEqual(xs.subscriptions, [ - Subscription(200, 1000) + Subscription(200, 250) ]) } @@ -854,9 +885,9 @@ extension ObservableSingleTest { ]) let res = scheduler.start() { - xs.retryWhen({ (errors: Observable) in - return never.asObservable() - }) + xs.retryWhen { (errors: Observable) in + return never + } } let correct: [Recorded] = [ @@ -891,9 +922,9 @@ extension ObservableSingleTest { ]) let res = scheduler.start() { - xs.retryWhen({ (errors: Observable) in - return empty.asObservable() - }) + xs.retryWhen { (errors: Observable) in + return empty + } } let correct: [Recorded] = [ @@ -923,14 +954,14 @@ extension ObservableSingleTest { ]) let res = scheduler.start(300) { - xs.retryWhen({ (errors: Observable) in + xs.retryWhen { (errors: Observable) in return errors.scan(0) { (var a, e) in if ++a == 2 { - throw testError + throw testError1 } return a } - }) + } } let correct: [Recorded] = [ @@ -938,7 +969,7 @@ extension ObservableSingleTest { next(220, 2), next(240, 1), next(250, 2), - error(260, testError) + error(260, testError1) ] XCTAssertEqual(res.messages, correct) @@ -997,13 +1028,13 @@ extension ObservableSingleTest { ]) let res = scheduler.start(300) { - xs.retryWhen({ (errors: Observable) in + xs.retryWhen { (errors: Observable) in return errors.scan(0) { (a, e) in return a + 1 }.takeWhile { (num: Int) -> Bool in return num < 2 } - }) + } } let correct: [Recorded] = [ @@ -1038,9 +1069,9 @@ extension ObservableSingleTest { ]) let res = scheduler.start() { - xs.retryWhen({ (errors: Observable) in - return never.asObservable() - }) + xs.retryWhen { (errors: Observable) in + return never + } } let correct: [Recorded] = [ @@ -1051,11 +1082,11 @@ extension ObservableSingleTest { XCTAssertEqual(res.messages, correct) XCTAssertEqual(xs.subscriptions, [ - Subscription(200, 1000) + Subscription(200, 230) ]) } - /* + func testRetryWhen_Incremental_BackOff() { let scheduler = TestScheduler(initialClock: 0) @@ -1067,35 +1098,91 @@ extension ObservableSingleTest { ]) let res = scheduler.start(800) { - xs.retryWhen({ (errors: Observable) in - return scheduler.createColdObservable([ - next(50, 1), // delay 50 - next(150, 2), // delay 100 - next(300, 3), // delay 150 - completed(500) // delay 200 - ]) - }) + xs.retryWhen { (errors: Observable) in + errors.scan((0, nil)) { (a: (Int, NSError!), e) in + (a.0 + 1, e) + } + .flatMap { (a, e) -> Observable in + if a >= 4 { + return failWith(e) + } + + return timer(a * 50, scheduler) + } + } } let correct: [Recorded] = [ next(205, 1), next(265, 1), - next(365, 1), - next(515, 1), - completed(710) + next(375, 1), + next(535, 1), + error(540, testError) ] XCTAssertEqual(res.messages, correct) XCTAssertEqual(xs.subscriptions, [ - Subscription(200, 260), - Subscription(260, 360), - Subscription(360, 510), - Subscription(510, 710) + Subscription(200, 210), + Subscription(260, 270), + Subscription(370, 380), + Subscription(530, 540) ]) } - */ - + + func testRetryWhen_IgnoresDifferentErrorTypes() { + + let scheduler = TestScheduler(initialClock: 0) + + // just fails + let xs = scheduler.createColdObservable([ + next(5, 1), + error(10, testError) + ]) + + let res = scheduler.start(800) { + xs.retryWhen { (errors: Observable) in + errors + } + } + + let correct: [Recorded] = [ + next(205, 1), + error(210, testError) + ] + + XCTAssertEqual(res.messages, correct) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 210) + ]) + } + + func testRetryWhen_tailRecursiveOptimizationsTest() { + var count = 1 + let sequenceSendingImmediateError: Observable = create { observer in + observer.on(.Next(0)) + observer.on(.Next(1)) + observer.on(.Next(2)) + if count < 2 { + observer.on(.Error(testError)) + count++ + } + observer.on(.Next(3)) + observer.on(.Next(4)) + observer.on(.Next(5)) + observer.on(.Completed) + + return NopDisposable.instance + } + + _ = sequenceSendingImmediateError + .retryWhen { errors in + return errors + } + .subscribe { _ in + } + } } From 2da94a21361156e49256fb2e201be50936227312 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 8 Nov 2015 22:34:16 +0100 Subject: [PATCH 167/210] Performance optimizations for stateful components. --- RxSwift/Concurrency/Lock.swift | 24 +++ .../SynchronizedUnsubscribeType.swift | 11 +- RxSwift/DataStructures/Bag.swift | 23 ++- .../Implementations/ShareReplay1.swift | 29 ++-- RxSwift/Subjects/BehaviorSubject.swift | 14 +- RxSwift/Subjects/PublishSubject.swift | 29 ++-- RxSwift/Subjects/ReplaySubject.swift | 36 +++-- RxTests/PerformanceTests/main.swift | 28 +++- .../Tests/SubjectConcurrencyTest.swift | 149 ++++++++++++++++++ RxTests/RxTests.xcodeproj/project.pbxproj | 8 + RxTests/Tests/PerformanceTools.swift | 31 ++-- 11 files changed, 314 insertions(+), 68 deletions(-) create mode 100644 RxTests/RxSwiftTests/Tests/SubjectConcurrencyTest.swift diff --git a/RxSwift/Concurrency/Lock.swift b/RxSwift/Concurrency/Lock.swift index df4d6f40..d9d2b1fe 100644 --- a/RxSwift/Concurrency/Lock.swift +++ b/RxSwift/Concurrency/Lock.swift @@ -76,4 +76,28 @@ extension NSRecursiveLock : Lock { let result = try action() return result } +} + +let RECURSIVE_MUTEX = _initializeRecursiveMutex() + +func _initializeRecursiveMutex() -> pthread_mutex_t { + var mutex: pthread_mutex_t = pthread_mutex_t() + var mta: pthread_mutexattr_t = pthread_mutexattr_t() + + pthread_mutex_init(&mutex, nil) + pthread_mutexattr_init(&mta) + pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_RECURSIVE) + pthread_mutex_init(&mutex, &mta) + + return mutex +} + +extension pthread_mutex_t { + mutating func lock() { + pthread_mutex_lock(&self) + } + + mutating func unlock() { + pthread_mutex_unlock(&self) + } } \ No newline at end of file diff --git a/RxSwift/Concurrency/SynchronizedUnsubscribeType.swift b/RxSwift/Concurrency/SynchronizedUnsubscribeType.swift index a73f8fca..3bb68495 100644 --- a/RxSwift/Concurrency/SynchronizedUnsubscribeType.swift +++ b/RxSwift/Concurrency/SynchronizedUnsubscribeType.swift @@ -8,15 +8,8 @@ import Foundation -protocol SynchronizedUnsubscribeType : class, Lock { +protocol SynchronizedUnsubscribeType : class { typealias DisposeKey - func _synchronized_unsubscribe(disposeKey: DisposeKey) -} - -extension SynchronizedUnsubscribeType { - func synchronizedUnsubscribe(disposeKey: DisposeKey) { - lock(); defer { unlock() } - _synchronized_unsubscribe(disposeKey) - } + func synchronizedUnsubscribe(disposeKey: DisposeKey) } \ No newline at end of file diff --git a/RxSwift/DataStructures/Bag.swift b/RxSwift/DataStructures/Bag.swift index 3bc66e69..b803d3d8 100644 --- a/RxSwift/DataStructures/Bag.swift +++ b/RxSwift/DataStructures/Bag.swift @@ -90,6 +90,8 @@ public struct Bag : CustomStringConvertible { // last is sparse dictionary private var _dictionary: [BagKey : T]? = nil + private var _onlyFastPath = true + /** Creates new empty `Bag`. */ @@ -130,6 +132,8 @@ public struct Bag : CustomStringConvertible { return key } + _onlyFastPath = false + if _key1 == nil { _key1 = key _value1 = element @@ -221,6 +225,13 @@ extension Bag { - parameter action: Enumeration closure. */ public func forEach(@noescape action: (T) -> Void) { + if _onlyFastPath { + if let value0 = _value0 { + action(value0) + } + return + } + let pairs = _pairs let value0 = _value0 let value1 = _value1 @@ -253,7 +264,12 @@ extension Bag where T: ObserverType { - parameter action: Enumeration closure. */ public func on(event: Event) { - let pairs = self._pairs + if _onlyFastPath { + _value0?.on(event) + return + } + + let pairs = _pairs let value0 = _value0 let value1 = _value1 let dictionary = _dictionary @@ -282,6 +298,11 @@ extension Bag where T: ObserverType { Dispatches `dispose` to all disposables contained inside bag. */ public func disposeAllIn(bag: Bag) { + if bag._onlyFastPath { + bag._value0?.dispose() + return + } + let pairs = bag._pairs let value0 = bag._value0 let value1 = bag._value1 diff --git a/RxSwift/Observables/Implementations/ShareReplay1.swift b/RxSwift/Observables/Implementations/ShareReplay1.swift index 0707b840..21b5b2bc 100644 --- a/RxSwift/Observables/Implementations/ShareReplay1.swift +++ b/RxSwift/Observables/Implementations/ShareReplay1.swift @@ -9,22 +9,20 @@ import Foundation // optimized version of share replay for most common case -class ShareReplay1 +final class ShareReplay1 : Observable , ObserverType - , LockOwnerType - , SynchronizedOnType - , SynchronizedSubscribeType , SynchronizedUnsubscribeType { typealias DisposeKey = Bag>.KeyType private let _source: Observable - let _lock = NSRecursiveLock() + private var _lock = RECURSIVE_MUTEX private var _connection: SingleAssignmentDisposable? private var _element: Element? + private var _stopped = false private var _stopEvent = nil as Event? private var _observers = Bag>() @@ -33,7 +31,8 @@ class ShareReplay1 } override func subscribe(observer: O) -> Disposable { - return synchronizedSubscribe(observer) + _lock.lock(); defer { _lock.unlock() } + return _synchronized_subscribe(observer) } func _synchronized_subscribe(observer: O) -> Disposable { @@ -60,6 +59,11 @@ class ShareReplay1 return SubscriptionDisposable(owner: self, key: disposeKey) } + func synchronizedUnsubscribe(disposeKey: DisposeKey) { + _lock.lock(); defer { _lock.unlock() } + _synchronized_unsubscribe(disposeKey) + } + func _synchronized_unsubscribe(disposeKey: DisposeKey) { // if already unsubscribed, just return if self._observers.removeKey(disposeKey) == nil { @@ -73,20 +77,21 @@ class ShareReplay1 } func on(event: Event) { - synchronizedOn(event) + _lock.lock(); defer { _lock.unlock() } + _synchronized_on(event) } func _synchronized_on(event: Event) { - if _stopEvent != nil { + if _stopped { return } - if case .Next(let element) = event { + switch event { + case .Next(let element): _element = element - } - - if event.isStopEvent { + case .Error, .Completed: _stopEvent = event + _stopped = true _connection?.dispose() _connection = nil } diff --git a/RxSwift/Subjects/BehaviorSubject.swift b/RxSwift/Subjects/BehaviorSubject.swift index 4059ce87..66b14883 100644 --- a/RxSwift/Subjects/BehaviorSubject.swift +++ b/RxSwift/Subjects/BehaviorSubject.swift @@ -17,9 +17,6 @@ public final class BehaviorSubject : Observable , SubjectType , ObserverType - , LockOwnerType - , SynchronizedOnType - , SynchronizedSubscribeType , SynchronizedUnsubscribeType , Disposable { public typealias SubjectObserverType = BehaviorSubject @@ -76,7 +73,8 @@ public final class BehaviorSubject - parameter event: Event to send to the observers. */ public func on(event: Event) { - synchronizedOn(event) + _lock.lock(); defer { _lock.unlock() } + _synchronized_on(event) } func _synchronized_on(event: Event) { @@ -101,7 +99,8 @@ public final class BehaviorSubject - returns: Disposable object that can be used to unsubscribe the observer from the subject. */ public override func subscribe(observer: O) -> Disposable { - return synchronizedSubscribe(observer) + _lock.lock(); defer { _lock.unlock() } + return _synchronized_subscribe(observer) } func _synchronized_subscribe(observer: O) -> Disposable { @@ -121,6 +120,11 @@ public final class BehaviorSubject return SubscriptionDisposable(owner: self, key: key) } + func synchronizedUnsubscribe(disposeKey: DisposeKey) { + _lock.lock(); defer { _lock.unlock() } + _synchronized_unsubscribe(disposeKey) + } + func _synchronized_unsubscribe(disposeKey: DisposeKey) { if _disposed { return diff --git a/RxSwift/Subjects/PublishSubject.swift b/RxSwift/Subjects/PublishSubject.swift index fd1d5712..970808c1 100644 --- a/RxSwift/Subjects/PublishSubject.swift +++ b/RxSwift/Subjects/PublishSubject.swift @@ -13,25 +13,22 @@ Represents an object that is both an observable sequence as well as an observer. Each notification is broadcasted to all subscribed observers. */ -public class PublishSubject +final public class PublishSubject : Observable , SubjectType , Cancelable , ObserverType - , LockOwnerType - , SynchronizedOnType - , SynchronizedSubscribeType - , SynchronizedUnsubscribeType - , SynchronizedDisposeType { + , SynchronizedUnsubscribeType { public typealias SubjectObserverType = PublishSubject typealias DisposeKey = Bag>.KeyType - let _lock = NSRecursiveLock() + private var _lock = RECURSIVE_MUTEX // state private var _disposed = false private var _observers = Bag>() + private var _stopped = false private var _stoppedEvent = nil as Event? /** @@ -56,13 +53,14 @@ public class PublishSubject - parameter event: Event to send to the observers. */ public func on(event: Event) { - synchronizedOn(event) + _lock.lock(); defer { _lock.unlock() } + _synchronized_on(event) } func _synchronized_on(event: Event) { switch event { case .Next(_): - if _disposed || _stoppedEvent != nil { + if _disposed || _stopped { return } @@ -70,6 +68,7 @@ public class PublishSubject case .Completed, .Error: if _stoppedEvent == nil { _stoppedEvent = event + _stopped = true _observers.on(event) _observers.removeAll() } @@ -83,7 +82,8 @@ public class PublishSubject - returns: Disposable object that can be used to unsubscribe the observer from the subject. */ public override func subscribe(observer: O) -> Disposable { - return synchronizedSubscribe(observer) + _lock.lock(); defer { _lock.unlock() } + return _synchronized_subscribe(observer) } func _synchronized_subscribe(observer: O) -> Disposable { @@ -101,6 +101,10 @@ public class PublishSubject return SubscriptionDisposable(owner: self, key: key) } + func synchronizedUnsubscribe(disposeKey: DisposeKey) { + _lock.lock(); defer { _lock.unlock() } + _synchronized_unsubscribe(disposeKey) + } func _synchronized_unsubscribe(disposeKey: DisposeKey) { _ = _observers.removeKey(disposeKey) @@ -117,10 +121,11 @@ public class PublishSubject Unsubscribe all observers and release resources. */ public func dispose() { - synchronizedDispose() + _lock.lock(); defer { _lock.unlock() } + _synchronized_dispose() } - func _synchronized_dispose() { + final func _synchronized_dispose() { _disposed = true _observers.removeAll() _stoppedEvent = nil diff --git a/RxSwift/Subjects/ReplaySubject.swift b/RxSwift/Subjects/ReplaySubject.swift index 3ad58d4f..551a01df 100644 --- a/RxSwift/Subjects/ReplaySubject.swift +++ b/RxSwift/Subjects/ReplaySubject.swift @@ -66,23 +66,15 @@ public class ReplaySubject class ReplayBufferBase : ReplaySubject - , LockOwnerType - , SynchronizedOnType - , SynchronizedSubscribeType - , SynchronizedUnsubscribeType - , SynchronizedDisposeType { - - let _lock = NSRecursiveLock() + , SynchronizedUnsubscribeType { + private var _lock = RECURSIVE_MUTEX + // state private var _disposed = false private var _stoppedEvent = nil as Event? private var _observers = Bag>() - override init() { - - } - func trim() { abstractMethod() } @@ -96,7 +88,8 @@ class ReplayBufferBase } override func on(event: Event) { - synchronizedOn(event) + _lock.lock(); defer { _lock.unlock() } + _synchronized_on(event) } func _synchronized_on(event: Event) { @@ -122,7 +115,8 @@ class ReplayBufferBase } override func subscribe(observer: O) -> Disposable { - return synchronizedSubscribe(observer) + _lock.lock(); defer { _lock.unlock() } + return _synchronized_subscribe(observer) } func _synchronized_subscribe(observer: O) -> Disposable { @@ -144,6 +138,11 @@ class ReplayBufferBase } } + func synchronizedUnsubscribe(disposeKey: DisposeKey) { + _lock.lock(); defer { _lock.unlock() } + _synchronized_unsubscribe(disposeKey) + } + func _synchronized_unsubscribe(disposeKey: DisposeKey) { if _disposed { return @@ -158,6 +157,11 @@ class ReplayBufferBase synchronizedDispose() } + func synchronizedDispose() { + _lock.lock(); defer { _lock.unlock() } + _synchronized_dispose() + } + func _synchronized_dispose() { _disposed = true _stoppedEvent = nil @@ -165,7 +169,7 @@ class ReplayBufferBase } } -class ReplayOne : ReplayBufferBase { +final class ReplayOne : ReplayBufferBase { private var _value: Element? override init() { @@ -215,7 +219,7 @@ class ReplayManyBase : ReplayBufferBase { } } -class ReplayMany : ReplayManyBase { +final class ReplayMany : ReplayManyBase { private let _bufferSize: Int init(bufferSize: Int) { @@ -231,7 +235,7 @@ class ReplayMany : ReplayManyBase { } } -class ReplayAll : ReplayManyBase { +final class ReplayAll : ReplayManyBase { init() { super.init(queueSize: 0) } diff --git a/RxTests/PerformanceTests/main.swift b/RxTests/PerformanceTests/main.swift index f2a0b7f1..d4240578 100644 --- a/RxTests/PerformanceTests/main.swift +++ b/RxTests/PerformanceTests/main.swift @@ -18,18 +18,37 @@ func allocation() { } -compareTwoImplementations(benchmarkTime: true, first: { +repeat { +compareTwoImplementations(benchmarkTime: true, benchmarkMemory: false, first: { let publishSubject = PublishSubject() //let a = just(1) //combineLatest(a, - publishSubject//.asDriver(onErrorJustReturn: -1) + publishSubject //.asDriver(onErrorJustReturn: -1) + /*create { (o: AnyObserver) in + for i in 0..<100 { + o.on(.Next(i)) + } + return NopDisposable.instance + }*/ + //.retryWhen { $0 } .shareReplay(1) + .shareReplay(1) + .shareReplay(1) + .shareReplay(1) + .shareReplay(1) + .shareReplay(1) + .shareReplay(1) + .shareReplay(1) + //.map { $0 } + /*.map { $0 } .map { $0 } - .filter { _ in true }//){ x, _ in x } .map { $0 } - .flatMap { just($0) } + .map { $0 }*/ + /*.filter { _ in true }//){ x, _ in x } + .map { $0 } + .flatMap { just($0) }*/ .subscribeNext { _ in } @@ -42,3 +61,4 @@ compareTwoImplementations(benchmarkTime: true, first: { }, second: { }) +} while true \ No newline at end of file diff --git a/RxTests/RxSwiftTests/Tests/SubjectConcurrencyTest.swift b/RxTests/RxSwiftTests/Tests/SubjectConcurrencyTest.swift new file mode 100644 index 00000000..1478558b --- /dev/null +++ b/RxTests/RxSwiftTests/Tests/SubjectConcurrencyTest.swift @@ -0,0 +1,149 @@ +// +// SubjectTest.swift +// RxTests +// +// Created by Krunoslav Zaher on 11/8/15. +// +// + +import Foundation +import RxSwift +import XCTest + + +class ReplaySubjectConcurrencyTest : SubjectConcurrencyTest { + override func createSubject() -> (Observable, AnyObserver) { + let s = ReplaySubject.create(bufferSize: 1) + return (s.asObservable(), AnyObserver(eventHandler: s.asObserver().on)) + } +} + +class BehaviorSubjectConcurrencyTest : SubjectConcurrencyTest { + override func createSubject() -> (Observable, AnyObserver) { + let s = BehaviorSubject(value: -1) + return (s.asObservable(), AnyObserver(eventHandler: s.asObserver().on)) + } +} + +class SubjectConcurrencyTest : RxTest { + // default test is for publish subject + func createSubject() -> (Observable, AnyObserver) { + let s = PublishSubject() + return (s.asObservable(), AnyObserver(eventHandler: s.asObserver().on)) + } +} + +extension SubjectConcurrencyTest { + func testSubjectIsSynchronized() { + let (observable, _observer) = createSubject() + + let o = RxBox(_observer) + + var allDone = false + + var state = 0 + _ = observable.subscribeNext { [unowned o] n in + if n < 0 { + return + } + + if state == 0 { + state = 1 + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) { + o.value.on(.Next(1)) + } + + // if other thread can't fulfill the condition in 0.5 sek, that means it is synchronized + NSThread.sleepForTimeInterval(0.5) + + XCTAssertEqual(state, 1) + + dispatch_async(dispatch_get_main_queue()) { + o.value.on(.Next(2)) + } + } + else if state == 1 { + XCTAssertTrue(!NSThread.isMainThread()) + state = 2 + } + else if state == 2 { + XCTAssertTrue(NSThread.isMainThread()) + allDone = true + } + } + + _observer.on(.Next(0)) + + // wait for second + for _ in 0 ..< 10 { + NSRunLoop.currentRunLoop().runUntilDate(NSDate().dateByAddingTimeInterval(0.1)) + if allDone { + break + } + } + + XCTAssertTrue(allDone) + } + + func testSubjectIsReentrantForNextAndComplete() { + let (observable, _observer) = createSubject() + + var state = 0 + + let o = RxBox(_observer) + + var ranAll = false + + _ = observable.subscribeNext { [unowned o] n in + if n < 0 { + return + } + + if state == 0 { + state = 1 + + // if isn't reentrant, this will cause deadlock + o.value.on(.Next(1)) + } + else if state == 1 { + // if isn't reentrant, this will cause deadlock + o.value.on(.Completed) + ranAll = true + } + } + + o.value.on(.Next(0)) + XCTAssertTrue(ranAll) + } + + func testSubjectIsReentrantForNextAndError() { + let (observable, _observer) = createSubject() + + var state = 0 + + let o = RxBox(_observer) + + var ranAll = false + + _ = observable.subscribeNext { [unowned o] n in + if n < 0 { + return + } + + if state == 0 { + state = 1 + + // if isn't reentrant, this will cause deadlock + o.value.on(.Next(1)) + } + else if state == 1 { + // if isn't reentrant, this will cause deadlock + o.value.on(.Error(testError)) + ranAll = true + } + } + + o.value.on(.Next(0)) + XCTAssertTrue(ranAll) + } +} diff --git a/RxTests/RxTests.xcodeproj/project.pbxproj b/RxTests/RxTests.xcodeproj/project.pbxproj index 1224c280..ac7ce0d5 100644 --- a/RxTests/RxTests.xcodeproj/project.pbxproj +++ b/RxTests/RxTests.xcodeproj/project.pbxproj @@ -124,6 +124,9 @@ C8EA2D371BD02E1900FB22AC /* EquatableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EA2D361BD02E1900FB22AC /* EquatableArray.swift */; }; C8EA2D381BD02E1900FB22AC /* EquatableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EA2D361BD02E1900FB22AC /* EquatableArray.swift */; }; C8EA2D391BD02E1900FB22AC /* EquatableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EA2D361BD02E1900FB22AC /* EquatableArray.swift */; }; + C8F6A1411BEFE04F007DF367 /* SubjectConcurrencyTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A1401BEFE04F007DF367 /* SubjectConcurrencyTest.swift */; }; + C8F6A1421BEFE04F007DF367 /* SubjectConcurrencyTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A1401BEFE04F007DF367 /* SubjectConcurrencyTest.swift */; }; + C8F6A1431BEFE04F007DF367 /* SubjectConcurrencyTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A1401BEFE04F007DF367 /* SubjectConcurrencyTest.swift */; }; C8FDC5F81B2B5B7E0065F8D9 /* ElementIndexPair.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8FDC5F71B2B5B7E0065F8D9 /* ElementIndexPair.swift */; }; C8FDC5F91B2B5B7E0065F8D9 /* ElementIndexPair.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8FDC5F71B2B5B7E0065F8D9 /* ElementIndexPair.swift */; }; D203C4EB1BB9C22800D02D00 /* NSNotificationCenterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C811082B1AF50E2A001C13E4 /* NSNotificationCenterTests.swift */; }; @@ -246,6 +249,7 @@ C8E9D2BC1BD422D80079D0DB /* Control+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Control+RxTests.swift"; sourceTree = ""; }; C8E9D2C01BD4525B0079D0DB /* Control+RxTests+Cocoa.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Control+RxTests+Cocoa.swift"; sourceTree = ""; }; C8EA2D361BD02E1900FB22AC /* EquatableArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EquatableArray.swift; sourceTree = ""; }; + C8F6A1401BEFE04F007DF367 /* SubjectConcurrencyTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubjectConcurrencyTest.swift; sourceTree = ""; }; C8FDC5F71B2B5B7E0065F8D9 /* ElementIndexPair.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElementIndexPair.swift; sourceTree = ""; }; D2AF91961BD2EBB900A008C1 /* MockDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockDisposable.swift; sourceTree = ""; }; D2EBEB491BB9B7AE003A27DC /* RxTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RxTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -435,6 +439,7 @@ C69B64FF1BA88FAC00A7FA73 /* ObserverTests.swift */, C89CDB951BCDA1F1002063D9 /* Observable+SubscriptionTest.swift */, C80DDEDB1BCE9A03006A1832 /* Driver+Test.swift */, + C8F6A1401BEFE04F007DF367 /* SubjectConcurrencyTest.swift */, ); path = Tests; sourceTree = ""; @@ -622,6 +627,7 @@ C8AF26EF1B499E5C00131C03 /* DelegateProxyTest.swift in Sources */, C8E381281B207D03008CDC33 /* PrimitiveHotObservable.swift in Sources */, C81108661AF50E2A001C13E4 /* Observable+StandardSequenceOperatorsTest.swift in Sources */, + C8F6A1411BEFE04F007DF367 /* SubjectConcurrencyTest.swift in Sources */, C80DDEE01BCEE898006A1832 /* MainThreadPrimitiveHotObservable.swift in Sources */, C81108641AF50E2A001C13E4 /* Observable+MultipleTest.swift in Sources */, C811084F1AF50E2A001C13E4 /* ColdObservable.swift in Sources */, @@ -713,6 +719,7 @@ C69B65011BA8957C00A7FA73 /* ObserverTests.swift in Sources */, C8633AE61B0AA0ED00375D60 /* KVOObservableTests.swift in Sources */, C8EA2D381BD02E1900FB22AC /* EquatableArray.swift in Sources */, + C8F6A1421BEFE04F007DF367 /* SubjectConcurrencyTest.swift in Sources */, C88BB8AC1B07E64B0064D411 /* AssumptionsTest.swift in Sources */, C88BB8AD1B07E64B0064D411 /* TestConnectableObservable.swift in Sources */, C8E3813A1B21B77E008CDC33 /* Observable+Extensions.swift in Sources */, @@ -750,6 +757,7 @@ D2EBEB681BB9B7EF003A27DC /* ConcurrencyTest.swift in Sources */, D2EBEB721BB9B7F6003A27DC /* Observable+MultipleTest+Zip.swift in Sources */, D2EBEB731BB9B7F9003A27DC /* Observable+SingleTest.swift in Sources */, + C8F6A1431BEFE04F007DF367 /* SubjectConcurrencyTest.swift in Sources */, C80DDEE21BCEE898006A1832 /* MainThreadPrimitiveHotObservable.swift in Sources */, D2EBEB6C1BB9B7EF003A27DC /* Observable+BindingTest.swift in Sources */, D2EBEB621BB9B7E5003A27DC /* ElementIndexPair.swift in Sources */, diff --git a/RxTests/Tests/PerformanceTools.swift b/RxTests/Tests/PerformanceTools.swift index 3ccd1f0c..d9587dac 100644 --- a/RxTests/Tests/PerformanceTools.swift +++ b/RxTests/Tests/PerformanceTools.swift @@ -157,10 +157,15 @@ func measureMemoryUsage(@noescape work: () -> ()) -> (bytesAllocated: UInt64, al return (approxValuePerIteration(bytesAfter - bytes), approxValuePerIteration(allocationsAfter - allocations)) } -func compareTwoImplementations(benchmarkTime benchmarkTime: Bool, @noescape first: () -> (), @noescape second: () -> ()) { - print("Fragmenting memory ...") - fragmentMemory() - print("Benchmarking ...") +var fragmentedMemory = false + +func compareTwoImplementations(benchmarkTime benchmarkTime: Bool, benchmarkMemory: Bool, @noescape first: () -> (), @noescape second: () -> ()) { + if !fragmentedMemory { + print("Fragmenting memory ...") + fragmentMemory() + print("Benchmarking ...") + fragmentedMemory = true + } // first warm up to keep it fair @@ -179,14 +184,22 @@ func compareTwoImplementations(benchmarkTime benchmarkTime: Bool, @noescape firs time2 = 0 } - registerMallocHooks() + let memory1: (bytesAllocated: UInt64, allocations: UInt64) + let memory2: (bytesAllocated: UInt64, allocations: UInt64) + if benchmarkMemory { - first() - second() + registerMallocHooks() - let memory1 = measureMemoryUsage(first) - let memory2 = measureMemoryUsage(second) + first() + second() + memory1 = measureMemoryUsage(first) + memory2 = measureMemoryUsage(second) + } + else { + memory1 = (0, 0) + memory2 = (0, 0) + } // this is good enough print(String(format: "#1 implementation %8d bytes %4d allocations %5d useconds", arguments: [ memory1.bytesAllocated, From 9fa53a6bb3c1fb52f215fe04f626e21d73ff9a90 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 9 Nov 2015 00:44:59 +0100 Subject: [PATCH 168/210] Transforms `NSError`s across projects into Swift enums that inherit from `ErrorType`. --- .../Implementations/KVOObservable.swift | 4 +- .../Common/Observables/NSURLSession+Rx.swift | 74 +++++++++++++---- RxCocoa/Common/RxCocoa.swift | 79 ++++++++++--------- RxCocoa/iOS/UICollectionView+Rx.swift | 2 +- RxCocoa/iOS/UITableView+Rx.swift | 2 +- .../GitHubSearchRepositoriesAPI.swift | 6 +- .../GitHubSignup/GitHubAPI/GitHubAPI.swift | 9 +-- RxSwift/Error.swift | 61 ++++++-------- RxSwift/Rx.swift | 4 +- RxSwift/Subjects/BehaviorSubject.swift | 4 +- RxSwift/Subjects/PublishSubject.swift | 2 +- RxSwift/Subjects/ReplaySubject.swift | 2 +- 12 files changed, 138 insertions(+), 111 deletions(-) diff --git a/RxCocoa/Common/Observables/Implementations/KVOObservable.swift b/RxCocoa/Common/Observables/Implementations/KVOObservable.swift index c5a0bac2..74744549 100644 --- a/RxCocoa/Common/Observables/Implementations/KVOObservable.swift +++ b/RxCocoa/Common/Observables/Implementations/KVOObservable.swift @@ -102,7 +102,7 @@ func observeWeaklyKeyPathFor( let property = class_getProperty(object_getClass(target), propertyName); if property == nil { - return failWith(rxError(.KeyPathInvalid, "Object \(target) doesn't have property named `\(propertyName)`")) + return failWith(RxCocoaError.InvalidPropertyName(object: target, propertyName: propertyName)) } let propertyAttributes = property_getAttributes(property); @@ -121,7 +121,7 @@ func observeWeaklyKeyPathFor( let strongTarget: AnyObject? = weakTarget if nextObject == nil { - return failWith(rxError(.KeyPathInvalid, "Observed \(nextTarget) as property `\(propertyName)` on `\(strongTarget)` which is not `NSObject`.")) + return failWith(RxCocoaError.InvalidObjectOnKeyPath(object: nextTarget!, sourceObject: strongTarget ?? NSNull(), propertyName: propertyName)) } // if target is alive, then send change diff --git a/RxCocoa/Common/Observables/NSURLSession+Rx.swift b/RxCocoa/Common/Observables/NSURLSession+Rx.swift index a22476d7..efe4b5d3 100644 --- a/RxCocoa/Common/Observables/NSURLSession+Rx.swift +++ b/RxCocoa/Common/Observables/NSURLSession+Rx.swift @@ -11,6 +11,48 @@ import Foundation import RxSwift #endif +/** +RxCocoa URL errors. +*/ +public enum RxCocoaURLError + : ErrorType + , CustomDebugStringConvertible { + /** + Unknown error occurred. + */ + case Unknown + /** + Response is not NSHTTPURLResponse + */ + case NonHTTPResponse(response: NSURLResponse) + /** + Response is not successful. (not in `200 ..< 300` range) + */ + case HTTPRequestFailed(response: NSHTTPURLResponse, data: NSData?) + /** + Deserialization error. + */ + case DeserializationError(error: ErrorType) +} + +public extension RxCocoaURLError { + /** + A textual representation of `self`, suitable for debugging. + */ + public var debugDescription: String { + switch self { + case .Unknown: + return "Unknown error has occurred." + case let .NonHTTPResponse(response): + return "Response is not NSHTTPURLResponse `\(response)`." + case let .HTTPRequestFailed(response, _): + return "HTTP request failed with `\(response.statusCode)`." + case let .DeserializationError(error): + return "Error during deserialization of the response: \(error)" + } + } +} + func escapeTerminalString(value: String) -> String { return value.stringByReplacingOccurrencesOfString("\"", withString: "\\\"", options:[], range: nil) } @@ -75,7 +117,7 @@ extension NSURLSession { - returns: Observable sequence of URL responses. */ @warn_unused_result(message="http://git.io/rxs.uo") - public func rx_response(request: NSURLRequest) -> Observable<(NSData!, NSURLResponse!)> { + public func rx_response(request: NSURLRequest) -> Observable<(NSData!, NSHTTPURLResponse)> { return create { observer in // smart compiler should be able to optimize this out @@ -93,13 +135,18 @@ extension NSURLSession { print(convertResponseToString(data, response, error, interval)) } - if data == nil || response == nil { - observer.on(.Error(error ?? RxError.UnknownError)) + guard let response = response, data = data else { + observer.on(.Error(error ?? RxCocoaURLError.Unknown)) + return } - else { - observer.on(.Next(data as NSData!, response as NSURLResponse!)) - observer.on(.Completed) + + guard let httpResponse = response as? NSHTTPURLResponse else { + observer.on(.Error(RxCocoaURLError.NonHTTPResponse(response: response))) + return } + + observer.on(.Next(data as NSData!, httpResponse)) + observer.on(.Completed) } @@ -130,18 +177,11 @@ extension NSURLSession { @warn_unused_result(message="http://git.io/rxs.uo") public func rx_data(request: NSURLRequest) -> Observable { return rx_response(request).map { (data, response) -> NSData in - guard let response = response as? NSHTTPURLResponse else { - throw RxError.UnknownError - } - if 200 ..< 300 ~= response.statusCode { return data ?? NSData() } else { - throw rxError(.NetworkError, message: "Server returned failure", userInfo: [ - RxCocoaErrorHTTPResponseKey: response, - RxCocoaErrorHTTPResponseDataKey: data ?? NSData() - ]) + throw RxCocoaURLError.HTTPRequestFailed(response: response, data: data) } } } @@ -166,7 +206,11 @@ extension NSURLSession { @warn_unused_result(message="http://git.io/rxs.uo") public func rx_JSON(request: NSURLRequest) -> Observable { return rx_data(request).map { (data) -> AnyObject! in - return try NSJSONSerialization.JSONObjectWithData(data ?? NSData(), options: []) + do { + return try NSJSONSerialization.JSONObjectWithData(data ?? NSData(), options: []) + } catch let error { + throw RxCocoaURLError.DeserializationError(error: error) + } } } diff --git a/RxCocoa/Common/RxCocoa.swift b/RxCocoa/Common/RxCocoa.swift index 80707902..3fd743f8 100644 --- a/RxCocoa/Common/RxCocoa.swift +++ b/RxCocoa/Common/RxCocoa.swift @@ -14,45 +14,52 @@ import RxSwift import UIKit #endif -public enum RxCocoaError : Int { - case Unknown = 0 - case NetworkError = 1 - case InvalidOperation = 2 - case KeyPathInvalid = 3 +/** +RxCocoa errors. +*/ +public enum RxCocoaError + : ErrorType + , CustomDebugStringConvertible { + /** + Unknown error has occurred. + */ + case Unknown + /** + Invalid operation was attempted. + */ + case InvalidOperation(object: AnyObject) + /** + Items are not yet bound to user interface but have been requested. + */ + case ItemsNotYetBound(object: AnyObject) + /** + Invalid KVO Path. + */ + case InvalidPropertyName(object: AnyObject, propertyName: String) + /** + Invalid object on key path. + */ + case InvalidObjectOnKeyPath(object: AnyObject, sourceObject: AnyObject, propertyName: String) } -/** -Error domain for internal RxCocoa errors. -*/ -public let RxCocoaErrorDomain = "RxCocoaError" - -/** -`userInfo` key for `NSURLResponse` object when `RxCocoaError.NetworkError` happens. -*/ -public let RxCocoaErrorHTTPResponseKey = "RxCocoaErrorHTTPResponseKey" - -/** -`userInfo` key for `NSData` object when `RxCocoaError.NetworkError` happens. -*/ -public let RxCocoaErrorHTTPResponseDataKey = "RxCocoaErrorHTTPResponseDataKey" - -func rxError(errorCode: RxCocoaError, _ message: String) -> NSError { - return NSError(domain: RxCocoaErrorDomain, code: errorCode.rawValue, userInfo: [NSLocalizedDescriptionKey: message]) -} - -#if !RELEASE -public func _rxError(errorCode: RxCocoaError, message: String, userInfo: NSDictionary) -> NSError { - return rxError(errorCode, message: message, userInfo: userInfo) -} -#endif - -func rxError(errorCode: RxCocoaError, message: String, userInfo: NSDictionary) -> NSError { - var resultInfo: [NSObject: AnyObject] = [:] - resultInfo[NSLocalizedDescriptionKey] = message - for k in userInfo.allKeys { - resultInfo[k as! NSObject] = userInfo[k as! NSCopying] +public extension RxCocoaError { + /** + A textual representation of `self`, suitable for debugging. + */ + public var debugDescription: String { + switch self { + case .Unknown: + return "Unknown error occurred" + case let .InvalidOperation(object): + return "Invalid operation was attempted on `\(object)`" + case let .ItemsNotYetBound(object): + return "Data source is set, but items are not yet bound to user interface for `\(object)`" + case let .InvalidPropertyName(object, propertyName): + return "Object `\(object)` dosn't have a property named `\(propertyName)`" + case let .InvalidObjectOnKeyPath(object, sourceObject, propertyName): + return "Unobservable object `\(object)` was observed as `\(propertyName)` of `\(sourceObject)`" + } } - return NSError(domain: RxCocoaErrorDomain, code: Int(errorCode.rawValue), userInfo: resultInfo) } func bindingErrorToInterface(error: ErrorType) { diff --git a/RxCocoa/iOS/UICollectionView+Rx.swift b/RxCocoa/iOS/UICollectionView+Rx.swift index 5024b610..0e729c2f 100644 --- a/RxCocoa/iOS/UICollectionView+Rx.swift +++ b/RxCocoa/iOS/UICollectionView+Rx.swift @@ -161,7 +161,7 @@ extension UICollectionView { let dataSource: RxCollectionViewReactiveArrayDataSource = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_itemsWith*` methods was used.") guard let element = dataSource.modelAtIndex(indexPath.item) else { - throw rxError(.InvalidOperation, "Items not set yet.") + throw RxCocoaError.ItemsNotYetBound(object: self) } return element diff --git a/RxCocoa/iOS/UITableView+Rx.swift b/RxCocoa/iOS/UITableView+Rx.swift index 898300af..fbfa0080 100644 --- a/RxCocoa/iOS/UITableView+Rx.swift +++ b/RxCocoa/iOS/UITableView+Rx.swift @@ -205,7 +205,7 @@ extension UITableView { let dataSource: RxTableViewReactiveArrayDataSource = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_items*` methods was used.") guard let element = dataSource.modelAtIndex(indexPath.item) else { - throw rxError(.InvalidOperation, "Items not set yet.") + throw RxCocoaError.ItemsNotYetBound(object: self) } return element diff --git a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift index 36323cc8..406d408c 100644 --- a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift +++ b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift @@ -219,11 +219,7 @@ class GitHubSearchRepositoriesAPI { .retry(3) .trackActivity(self.activityIndicator) .observeOn(Dependencies.sharedDependencies.backgroundWorkScheduler) - .map { data, response -> SearchRepositoryResponse in - guard let httpResponse = response as? NSHTTPURLResponse else { - throw exampleError("not getting http response") - } - + .map { data, httpResponse -> SearchRepositoryResponse in if httpResponse.statusCode == 403 { return .LimitExceeded } diff --git a/RxExample/RxExample/Examples/GitHubSignup/GitHubAPI/GitHubAPI.swift b/RxExample/RxExample/Examples/GitHubSignup/GitHubAPI/GitHubAPI.swift index fae3424b..12c70554 100644 --- a/RxExample/RxExample/Examples/GitHubSignup/GitHubAPI/GitHubAPI.swift +++ b/RxExample/RxExample/Examples/GitHubSignup/GitHubAPI/GitHubAPI.swift @@ -46,13 +46,8 @@ class GitHubAPI { let URL = NSURL(string: "https://github.com/\(URLEscape(username))")! let request = NSURLRequest(URL: URL) return self.URLSession.rx_response(request) - .map { (maybeData, maybeResponse) in - if let response = maybeResponse as? NSHTTPURLResponse { - return response.statusCode == 404 - } - else { - return false - } + .map { (maybeData, response) in + return response.statusCode == 404 } .observeOn(self.dataScheduler) .catchErrorJustReturn(false) diff --git a/RxSwift/Error.swift b/RxSwift/Error.swift index 74307fe3..63ea3238 100644 --- a/RxSwift/Error.swift +++ b/RxSwift/Error.swift @@ -14,56 +14,41 @@ let RxCompositeFailures = "RxCompositeFailures" /** Generic Rx error codes. */ -public enum RxErrorCode : Int { +public enum RxError + : ErrorType + , CustomDebugStringConvertible { /** - Unknown error occured + Unknown error occured. */ - case Unknown = 0 + case Unknown /** - Casting error + Performing an action on disposed object. */ - case Cast = 2 - /** - Performing an action on disposed object - */ - case Disposed = 3 + case Disposed(object: AnyObject) /** Aritmetic overflow error. */ - case Overflow = 4 + case Overflow /** Argument out of range error. */ - case ArgumentOutOfRange = 5 + case ArgumentOutOfRange } -/** -Singleton instances of RxErrors -*/ -public struct RxError { +public extension RxError { /** - Singleton instance of unknown Error + A textual representation of `self`, suitable for debugging. */ - public static let UnknownError = NSError(domain: RxErrorDomain, code: RxErrorCode.Unknown.rawValue, userInfo: nil) - - /** - Singleton instance of error during casting. - */ - public static let CastError = NSError(domain: RxErrorDomain, code: RxErrorCode.Cast.rawValue, userInfo: nil) - - /** - Singleton instance of doing something on a disposed object error. - */ - public static let DisposedError = NSError(domain: RxErrorDomain, code: RxErrorCode.Disposed.rawValue, userInfo: nil) - - /** - Singleton instance of aritmetic overflow error. - */ - public static let OverflowError = NSError(domain: RxErrorDomain, code: RxErrorCode.Overflow.rawValue, userInfo: nil) - - /** - Singleton instance of argument out of range error. - */ - public static let ArgumentOutOfRange = NSError(domain: RxErrorDomain, code: RxErrorCode.ArgumentOutOfRange.rawValue, userInfo: nil) - + public var debugDescription: String { + switch self { + case .Unknown: + return "Unknown error occured" + case .Disposed(let object): + return "Object `\(object)` was already disposed" + case .Overflow: + return "Arithmetic overflow occured" + case .ArgumentOutOfRange: + return "Argument out of range" + } + } } \ No newline at end of file diff --git a/RxSwift/Rx.swift b/RxSwift/Rx.swift index 4621c980..dccb10ae 100644 --- a/RxSwift/Rx.swift +++ b/RxSwift/Rx.swift @@ -30,7 +30,7 @@ public var resourceCount: Int32 = 0 func incrementChecked(inout i: Int) throws -> Int { if i == Int.max { - throw RxError.OverflowError + throw RxError.Overflow } let result = i i += 1 @@ -39,7 +39,7 @@ func incrementChecked(inout i: Int) throws -> Int { func decrementChecked(inout i: Int) throws -> Int { if i == Int.min { - throw RxError.OverflowError + throw RxError.Overflow } let result = i i -= 1 diff --git a/RxSwift/Subjects/BehaviorSubject.swift b/RxSwift/Subjects/BehaviorSubject.swift index 66b14883..7c6284fc 100644 --- a/RxSwift/Subjects/BehaviorSubject.swift +++ b/RxSwift/Subjects/BehaviorSubject.swift @@ -54,7 +54,7 @@ public final class BehaviorSubject public func value() throws -> Element { _lock.lock(); defer { _lock.unlock() } // { if _disposed { - throw RxError.DisposedError + throw RxError.Disposed(object: self) } if let error = _stoppedEvent?.error { @@ -105,7 +105,7 @@ public final class BehaviorSubject func _synchronized_subscribe(observer: O) -> Disposable { if _disposed { - observer.on(.Error(RxError.DisposedError)) + observer.on(.Error(RxError.Disposed(object: self))) return NopDisposable.instance } diff --git a/RxSwift/Subjects/PublishSubject.swift b/RxSwift/Subjects/PublishSubject.swift index 970808c1..c569b392 100644 --- a/RxSwift/Subjects/PublishSubject.swift +++ b/RxSwift/Subjects/PublishSubject.swift @@ -93,7 +93,7 @@ final public class PublishSubject } if _disposed { - observer.on(.Error(RxError.DisposedError)) + observer.on(.Error(RxError.Disposed(object: self))) return NopDisposable.instance } diff --git a/RxSwift/Subjects/ReplaySubject.swift b/RxSwift/Subjects/ReplaySubject.swift index 551a01df..307ebde7 100644 --- a/RxSwift/Subjects/ReplaySubject.swift +++ b/RxSwift/Subjects/ReplaySubject.swift @@ -121,7 +121,7 @@ class ReplayBufferBase func _synchronized_subscribe(observer: O) -> Disposable { if _disposed { - observer.on(.Error(RxError.DisposedError)) + observer.on(.Error(RxError.Disposed(object: self))) return NopDisposable.instance } From 046024d7fdcc4057795d85ec05b9a94143afe989 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 9 Nov 2015 01:05:51 +0100 Subject: [PATCH 169/210] Changes `CustomStringConvertible` to `CustomDebugStringConvertible`. --- RxDataSourceStarterKit/Changeset.swift | 8 +++---- RxDataSourceStarterKit/Differentiator.swift | 24 ++++++++++++------- .../GitHubSearchRepositoriesAPI.swift | 8 ++++--- .../RxExample/Examples/TableView/User.swift | 8 ++++--- .../WikipediaAPI/WikipediaSearchResult.swift | 8 ++++++- RxSwift/DataStructures/Bag.swift | 23 ++++++++++-------- RxSwift/Event.swift | 8 ++++--- RxSwift/RxBox.swift | 16 ++++++++----- RxTests/RxCocoaTests/KVOObservableTests.swift | 3 ++- RxTests/RxCocoaTests/RxCocoaTests.swift | 8 ------- .../TestImplementations/Recorded.swift | 8 ++++--- .../Schedulers/VirtualTimeSchedulerBase.swift | 19 +++++++++------ .../TestImplementations/Subscription.swift | 12 +++++++--- RxTests/RxTest.swift | 2 +- 14 files changed, 93 insertions(+), 62 deletions(-) diff --git a/RxDataSourceStarterKit/Changeset.swift b/RxDataSourceStarterKit/Changeset.swift index 161bfa98..976b7f17 100644 --- a/RxDataSourceStarterKit/Changeset.swift +++ b/RxDataSourceStarterKit/Changeset.swift @@ -13,18 +13,18 @@ import RxSwift import RxCocoa #endif -struct ItemPath : CustomStringConvertible { +struct ItemPath : CustomDebugStringConvertible { let sectionIndex: Int let itemIndex: Int - var description : String { + var debugDescription : String { get { return "(\(sectionIndex), \(itemIndex))" } } } -public struct Changeset : CustomStringConvertible { +public struct Changeset : CustomDebugStringConvertible { typealias I = S.Item var finalSections: [S] = [] @@ -46,7 +46,7 @@ public struct Changeset : CustomStringConvertible { return initialValue } - public var description : String { + public var debugDescription : String { get { let serializedSections = "[\n" + finalSections.map { "\($0)" }.joinWithSeparator(",\n") + "\n]\n" return " >> Final sections" diff --git a/RxDataSourceStarterKit/Differentiator.swift b/RxDataSourceStarterKit/Differentiator.swift index a7789de4..95a00068 100644 --- a/RxDataSourceStarterKit/Differentiator.swift +++ b/RxDataSourceStarterKit/Differentiator.swift @@ -8,14 +8,16 @@ import Foundation -enum EditEvent : CustomStringConvertible { +enum EditEvent : CustomDebugStringConvertible { case Inserted // can't be found in old sections case Deleted // Was in old, not in new, in it's place is something "not new" :(, otherwise it's Updated case Moved // same item, but was on different index, and needs explicit move case MovedAutomatically // don't need to specify any changes for those rows case Untouched - - var description: String { +} + +extension EditEvent { + var debugDescription: String { get { switch self { case .Inserted: @@ -33,22 +35,26 @@ enum EditEvent : CustomStringConvertible { } } -struct SectionAdditionalInfo : CustomStringConvertible { +struct SectionAdditionalInfo : CustomDebugStringConvertible { var event: EditEvent var indexAfterDelete: Int? - - var description: String { +} + +extension SectionAdditionalInfo { + var debugDescription: String { get { return "\(event), \(indexAfterDelete)" } } } -struct ItemAdditionalInfo : CustomStringConvertible { +struct ItemAdditionalInfo : CustomDebugStringConvertible { var event: EditEvent var indexAfterDelete: Int? - - var description: String { +} + +extension ItemAdditionalInfo { + var debugDescription: String { get { return "\(event) \(indexAfterDelete)" } diff --git a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift index 406d408c..115a3b95 100644 --- a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift +++ b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift @@ -14,7 +14,7 @@ import RxSwift /** Parsed GitHub respository. */ -struct Repository: CustomStringConvertible { +struct Repository: CustomDebugStringConvertible { var name: String var url: String @@ -22,8 +22,10 @@ struct Repository: CustomStringConvertible { self.name = name self.url = url } - - var description: String { +} + +extension Repository { + var debugDescription: String { return "\(name) | \(url)" } } diff --git a/RxExample/RxExample/Examples/TableView/User.swift b/RxExample/RxExample/Examples/TableView/User.swift index 62b452ce..f05b664d 100755 --- a/RxExample/RxExample/Examples/TableView/User.swift +++ b/RxExample/RxExample/Examples/TableView/User.swift @@ -1,7 +1,7 @@ import Foundation -struct User: Equatable, CustomStringConvertible { +struct User: Equatable, CustomDebugStringConvertible { var firstName: String var lastName: String @@ -12,8 +12,10 @@ struct User: Equatable, CustomStringConvertible { self.lastName = lastName self.imageURL = imageURL } - - var description: String { +} + +extension User { + var debugDescription: String { get { return firstName + " " + lastName } diff --git a/RxExample/RxExample/Examples/WikipediaImageSearch/WikipediaAPI/WikipediaSearchResult.swift b/RxExample/RxExample/Examples/WikipediaImageSearch/WikipediaAPI/WikipediaSearchResult.swift index a0dd0e1e..8ce20e80 100644 --- a/RxExample/RxExample/Examples/WikipediaImageSearch/WikipediaAPI/WikipediaSearchResult.swift +++ b/RxExample/RxExample/Examples/WikipediaImageSearch/WikipediaAPI/WikipediaSearchResult.swift @@ -11,7 +11,7 @@ import Foundation import RxSwift #endif -struct WikipediaSearchResult: CustomStringConvertible { +struct WikipediaSearchResult: CustomDebugStringConvertible { let title: String let description: String let URL: NSURL @@ -52,3 +52,9 @@ struct WikipediaSearchResult: CustomStringConvertible { return searchResults } } + +extension WikipediaSearchResult { + var debugDescription: String { + return "[\(title)](\(URL))" + } +} \ No newline at end of file diff --git a/RxSwift/DataStructures/Bag.swift b/RxSwift/DataStructures/Bag.swift index b803d3d8..5953ebde 100644 --- a/RxSwift/DataStructures/Bag.swift +++ b/RxSwift/DataStructures/Bag.swift @@ -62,7 +62,7 @@ Time and space complexity of insertion an deletion is O(n). It is suitable for storing small number of elements. */ -public struct Bag : CustomStringConvertible { +public struct Bag : CustomDebugStringConvertible { /** Type of identifier for inserted elements. */ @@ -98,15 +98,6 @@ public struct Bag : CustomStringConvertible { public init() { } - /** - - returns: Bag description. - */ - public var description : String { - get { - return "\(self.count) elements in Bag" - } - } - /** Inserts `value` into bag. @@ -216,6 +207,18 @@ public struct Bag : CustomStringConvertible { } } +extension Bag { + /** + A textual representation of `self`, suitable for debugging. + */ + public var debugDescription : String { + get { + return "\(self.count) elements in Bag" + } + } +} + + // MARK: forEach extension Bag { diff --git a/RxSwift/Event.swift b/RxSwift/Event.swift index e97f28fb..2e233016 100644 --- a/RxSwift/Event.swift +++ b/RxSwift/Event.swift @@ -15,7 +15,7 @@ Represents sequence event Sequence grammar: Next\* (Error | Completed) */ -public enum Event : CustomStringConvertible { +public enum Event : CustomDebugStringConvertible { /** Next element is produced */ @@ -30,11 +30,13 @@ public enum Event : CustomStringConvertible { Sequence completes sucessfully */ case Completed - +} + +extension Event { /** - returns: Description of event */ - public var description: String { + public var debugDescription: String { get { switch self { case .Next(let value): diff --git a/RxSwift/RxBox.swift b/RxSwift/RxBox.swift index b688c1f4..7141e9b9 100644 --- a/RxSwift/RxBox.swift +++ b/RxSwift/RxBox.swift @@ -11,7 +11,7 @@ import Foundation /** Creates immutable reference wrapper for any type. */ -public class RxBox : CustomStringConvertible { +public class RxBox : CustomDebugStringConvertible { /** Wrapped value */ @@ -25,11 +25,13 @@ public class RxBox : CustomStringConvertible { public init (_ value: T) { self.value = value } - +} + +extension RxBox { /** - returns: Box description. */ - public var description: String { + public var debugDescription: String { get { return "Box(\(self.value))" } @@ -39,7 +41,7 @@ public class RxBox : CustomStringConvertible { /** Creates mutable reference wrapper for any type. */ -public class RxMutableBox : CustomStringConvertible { +public class RxMutableBox : CustomDebugStringConvertible { /** Wrapped value */ @@ -53,11 +55,13 @@ public class RxMutableBox : CustomStringConvertible { public init (_ value: T) { self.value = value } - +} + +extension RxMutableBox { /** - returns: Box description. */ - public var description: String { + public var debugDescription: String { get { return "MutatingBox(\(self.value))" } diff --git a/RxTests/RxCocoaTests/KVOObservableTests.swift b/RxTests/RxCocoaTests/KVOObservableTests.swift index 4ee76757..6c02d071 100644 --- a/RxTests/RxCocoaTests/KVOObservableTests.swift +++ b/RxTests/RxCocoaTests/KVOObservableTests.swift @@ -960,7 +960,8 @@ extension KVOObservableTests { } XCTAssertTrue(lastError != nil) - + lastError = nil + var rootDeallocated = false _ = root diff --git a/RxTests/RxCocoaTests/RxCocoaTests.swift b/RxTests/RxCocoaTests/RxCocoaTests.swift index 6a4a87a6..80a2a1f5 100644 --- a/RxTests/RxCocoaTests/RxCocoaTests.swift +++ b/RxTests/RxCocoaTests/RxCocoaTests.swift @@ -12,12 +12,4 @@ import RxSwift import RxCocoa class RxCocoaTest : RxTest { -#if !RELEASE - func testRxError() { - let result = _rxError(RxCocoaError.NetworkError, message: "my bad", userInfo: ["a": 1]) - - let dUserInfo = NSDictionary(dictionary: result.userInfo) - XCTAssertTrue(dUserInfo.isEqualToDictionary([NSLocalizedDescriptionKey: "my bad", "a" : 1])) - } -#endif } \ No newline at end of file diff --git a/RxTests/RxSwiftTests/TestImplementations/Recorded.swift b/RxTests/RxSwiftTests/TestImplementations/Recorded.swift index 606f3a2e..a3d9a74b 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Recorded.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Recorded.swift @@ -10,7 +10,7 @@ import Foundation import RxSwift import Swift -struct Recorded : CustomStringConvertible, Equatable { +struct Recorded : CustomDebugStringConvertible, Equatable { let time: Time let event: Event @@ -31,8 +31,10 @@ struct Recorded : CustomStringConvertible, Equatable { } } } - - var description: String { +} + +extension Recorded { + var debugDescription: String { get { return "\(event) @ \(time)" } diff --git a/RxTests/RxSwiftTests/TestImplementations/Schedulers/VirtualTimeSchedulerBase.swift b/RxTests/RxSwiftTests/TestImplementations/Schedulers/VirtualTimeSchedulerBase.swift index 213edac1..97481e86 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Schedulers/VirtualTimeSchedulerBase.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Schedulers/VirtualTimeSchedulerBase.swift @@ -48,7 +48,10 @@ class ScheduledItem : ScheduledItemProtocol { } -class VirtualTimeSchedulerBase : SchedulerType, CustomStringConvertible { +class VirtualTimeSchedulerBase + : SchedulerType + , CustomDebugStringConvertible { + typealias TimeInterval = Int typealias Time = Int @@ -61,12 +64,6 @@ class VirtualTimeSchedulerBase : SchedulerType, CustomStringConvertible { } } - var description: String { - get { - return self.schedulerQueue.description - } - } - private var schedulerQueue : [ScheduledItemProtocol] = [] init(initialClock: Time) { @@ -149,4 +146,12 @@ class VirtualTimeSchedulerBase : SchedulerType, CustomStringConvertible { return minElement } +} + +extension VirtualTimeSchedulerBase { + var debugDescription: String { + get { + return self.schedulerQueue.description + } + } } \ No newline at end of file diff --git a/RxTests/RxSwiftTests/TestImplementations/Subscription.swift b/RxTests/RxSwiftTests/TestImplementations/Subscription.swift index a9a7e7fe..21f36cad 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Subscription.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Subscription.swift @@ -8,7 +8,11 @@ import Foundation -struct Subscription : Equatable, Hashable, CustomStringConvertible { +struct Subscription + : Equatable + , Hashable + , CustomDebugStringConvertible { + let subscribe : Time let unsubscribe : Time @@ -27,8 +31,10 @@ struct Subscription : Equatable, Hashable, CustomStringConvertible { return subscribe.hashValue ^ unsubscribe.hashValue } } - - var description : String { +} + +extension Subscription { + var debugDescription : String { get { let infiniteText = "Infinity" return "(\(subscribe) : \(unsubscribe != Time.max ? String(unsubscribe) : infiniteText))" diff --git a/RxTests/RxTest.swift b/RxTests/RxTest.swift index a15f9629..008437a7 100644 --- a/RxTests/RxTest.swift +++ b/RxTests/RxTest.swift @@ -58,7 +58,7 @@ func completed(time: Time) -> Recorded { return Recorded(time: time, event: .Completed) } -func error(time: Time, _ error: NSError) -> Recorded { +func error(time: Time, _ error: ErrorType) -> Recorded { return Recorded(time: time, event: .Error(error)) } From a9d445c2cdf7375ce7427b9745f884497b724312 Mon Sep 17 00:00:00 2001 From: Lars Lockefeer Date: Sun, 8 Nov 2015 15:29:49 +0100 Subject: [PATCH 170/210] Added tvOS specific extension for UIButton This extension registers for the `PrimaryActionTriggered` event, rather than the `TouchUpInside` event, to ensure `rx_tap` works as expected. --- Rx.xcodeproj/project.pbxproj | 4 ++-- RxCocoa/iOS/UIButton+Rx.swift | 22 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index 99c1d709..a84bc9e2 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 9D71C4D21BF08191006E8F59 /* UIButton+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88254061B8A752B00B02D69 /* UIButton+Rx.swift */; }; B1B7C3BD1BDD39DB0076934E /* TakeLast.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B7C3BC1BDD39DB0076934E /* TakeLast.swift */; }; B1B7C3BE1BDD39DB0076934E /* TakeLast.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B7C3BC1BDD39DB0076934E /* TakeLast.swift */; }; B1B7C3BF1BDD39DB0076934E /* TakeLast.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B7C3BC1BDD39DB0076934E /* TakeLast.swift */; }; @@ -581,7 +582,6 @@ D203C5011BB9C53E00D02D00 /* UIActionSheet+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88254031B8A752B00B02D69 /* UIActionSheet+Rx.swift */; }; D203C5021BB9C53E00D02D00 /* UIAlertView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88254041B8A752B00B02D69 /* UIAlertView+Rx.swift */; }; D203C5031BB9C53E00D02D00 /* UIBarButtonItem+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88254051B8A752B00B02D69 /* UIBarButtonItem+Rx.swift */; }; - D203C5041BB9C53E00D02D00 /* UIButton+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88254061B8A752B00B02D69 /* UIButton+Rx.swift */; }; D203C5051BB9C53E00D02D00 /* UICollectionView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88254071B8A752B00B02D69 /* UICollectionView+Rx.swift */; }; D203C5061BB9C53E00D02D00 /* UIControl+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88254081B8A752B00B02D69 /* UIControl+Rx.swift */; }; D203C5071BB9C53E00D02D00 /* UIDatePicker+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88254091B8A752B00B02D69 /* UIDatePicker+Rx.swift */; }; @@ -2632,7 +2632,6 @@ D203C5031BB9C53E00D02D00 /* UIBarButtonItem+Rx.swift in Sources */, D203C4FC1BB9C53700D02D00 /* RxScrollViewDelegateProxy.swift in Sources */, D2138C8E1BB9BED600339B5C /* ControlTarget.swift in Sources */, - D203C5041BB9C53E00D02D00 /* UIButton+Rx.swift in Sources */, C80DDEA91BCE69BA006A1832 /* ObservableConvertibleType+Driver.swift in Sources */, C80DDEA11BCE69BA006A1832 /* Driver+Subscription.swift in Sources */, D2138C891BB9BEBE00339B5C /* DelegateProxyType.swift in Sources */, @@ -2663,6 +2662,7 @@ D2138C871BB9BEBE00339B5C /* CLLocationManager+Rx.swift in Sources */, D203C4FF1BB9C53700D02D00 /* RxTableViewDelegateProxy.swift in Sources */, D2138C811BB9BEBE00339B5C /* _RXDelegateProxy.m in Sources */, + 9D71C4D21BF08191006E8F59 /* UIButton+Rx.swift in Sources */, D203C4FD1BB9C53700D02D00 /* RxSearchBarDelegateProxy.swift in Sources */, D2138C8A1BB9BEBE00339B5C /* Logging.swift in Sources */, D2138C851BB9BEBE00339B5C /* _RXSwizzling.m in Sources */, diff --git a/RxCocoa/iOS/UIButton+Rx.swift b/RxCocoa/iOS/UIButton+Rx.swift index 9621acb2..8945af61 100644 --- a/RxCocoa/iOS/UIButton+Rx.swift +++ b/RxCocoa/iOS/UIButton+Rx.swift @@ -6,7 +6,7 @@ // Copyright (c) 2015 Krunoslav Zaher. All rights reserved. // -#if os(iOS) || os(tvOS) +#if os(iOS) import Foundation #if !RX_NO_MODULE @@ -26,3 +26,23 @@ extension UIButton { } #endif + +#if os(tvOS) + +import Foundation +#if !RX_NO_MODULE + import RxSwift +#endif +import UIKit + +extension UIButton { + + /** + Reactive wrapper for `PrimaryActionTriggered` control event. + */ + public var rx_tap: ControlEvent { + return rx_controlEvents(.PrimaryActionTriggered) + } +} + +#endif From 15ad7ead1dffcdde956fbe29ad37a84584fbc62f Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 9 Nov 2015 12:15:24 +0100 Subject: [PATCH 171/210] Deprecates KVO wrappers that don't accept type as first operator and adds overloads that do. --- .../NSObject+Rx+CoreGraphics.swift | 158 +++++++++++++++++- RxCocoa/Common/Observables/NSObject+Rx.swift | 89 +++++++--- RxTests/RxCocoaTests/KVOObservableTests.swift | 82 ++++----- 3 files changed, 266 insertions(+), 63 deletions(-) diff --git a/RxCocoa/Common/Observables/NSObject+Rx+CoreGraphics.swift b/RxCocoa/Common/Observables/NSObject+Rx+CoreGraphics.swift index 765a6e38..dc22a2fe 100644 --- a/RxCocoa/Common/Observables/NSObject+Rx+CoreGraphics.swift +++ b/RxCocoa/Common/Observables/NSObject+Rx+CoreGraphics.swift @@ -22,7 +22,157 @@ let CGSizeType = "{CGSize=ff}" let CGPointType = "{CGPoint=ff}" #endif -// rx_observe + CoreGraphics +extension NSObject { + /** + Specialization of generic `rx_observe` method. + + For more information take a look at `rx_observe` method. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func rx_observe(type: CGRect.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { + return rx_observe(keyPath, options: options, retainSelf: retainSelf) + .map { (value: NSValue?) in + if let value = value { + if strcmp(value.objCType, CGRectType) != 0 { + return nil + } + var typedValue = CGRect(x: 0, y: 0, width: 0, height: 0) + value.getValue(&typedValue) + return typedValue + } + else { + return nil + } + } + } + + /** + Specialization of generic `rx_observe` method. + + For more information take a look at `rx_observe` method. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func rx_observe(type: CGSize.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { + return rx_observe(keyPath, options: options, retainSelf: retainSelf) + .map { (value: NSValue?) in + if let value = value { + if strcmp(value.objCType, CGSizeType) != 0 { + return nil + } + var typedValue = CGSize(width: 0, height: 0) + value.getValue(&typedValue) + return typedValue + } + else { + return nil + } + } + } + + /** + Specialization of generic `rx_observe` method. + + For more information take a look at `rx_observe` method. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func rx_observe(type: CGPoint.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { + return rx_observe(keyPath, options: options, retainSelf: retainSelf) + .map { (value: NSValue?) in + if let value = value { + if strcmp(value.objCType, CGPointType) != 0 { + return nil + } + var typedValue = CGPoint(x: 0, y: 0) + value.getValue(&typedValue) + return typedValue + } + else { + return nil + } + } + } +} + +#if !DISABLE_SWIZZLING + + // rx_observeWeakly + CoreGraphics + extension NSObject { + + /** + Specialization of generic `rx_observeWeakly` method. + + For more information take a look at `rx_observeWeakly` method. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func rx_observeWeakly(type: CGRect.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { + return rx_observeWeakly(keyPath, options: options) + .map { (value: NSValue?) in + if let value = value { + if strcmp(value.objCType, CGRectType) != 0 { + return nil + } + var typedValue = CGRect(x: 0, y: 0, width: 0, height: 0) + value.getValue(&typedValue) + return typedValue + } + else { + return nil + } + } + } + + /** + Specialization of generic `rx_observeWeakly` method. + + For more information take a look at `rx_observeWeakly` method. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func rx_observeWeakly(type: CGSize.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { + return rx_observeWeakly(keyPath, options: options) + .map { (value: NSValue?) in + if let value = value { + if strcmp(value.objCType, CGSizeType) != 0 { + return nil + } + var typedValue = CGSize(width: 0, height: 0) + value.getValue(&typedValue) + return typedValue + } + else { + return nil + } + } + } + + /** + Specialization of generic `rx_observeWeakly` method. + + For more information take a look at `rx_observeWeakly` method. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func rx_observeWeakly(type: CGPoint.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { + return rx_observeWeakly(keyPath, options: options) + .map { (value: NSValue?) in + if let value = value { + if strcmp(value.objCType, CGPointType) != 0 { + return nil + } + var typedValue = CGPoint(x: 0, y: 0) + value.getValue(&typedValue) + return typedValue + } + else { + return nil + } + } + } + } + +#endif + + +// MARK: Deprecated + extension NSObject { /** Specialization of generic `rx_observe` method. @@ -30,6 +180,7 @@ extension NSObject { For more information take a look at `rx_observe` method. */ @warn_unused_result(message="http://git.io/rxs.uo") + @available(*, deprecated=2.0.0, message="Please use version that takes type as first argument.") public func rx_observe(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { return rx_observe(keyPath, options: options, retainSelf: retainSelf) .map { (value: NSValue?) in @@ -53,6 +204,7 @@ extension NSObject { For more information take a look at `rx_observe` method. */ @warn_unused_result(message="http://git.io/rxs.uo") + @available(*, deprecated=2.0.0, message="Please use version that takes type as first argument.") public func rx_observe(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { return rx_observe(keyPath, options: options, retainSelf: retainSelf) .map { (value: NSValue?) in @@ -76,6 +228,7 @@ extension NSObject { For more information take a look at `rx_observe` method. */ @warn_unused_result(message="http://git.io/rxs.uo") + @available(*, deprecated=2.0.0, message="Please use version that takes type as first argument.") public func rx_observe(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { return rx_observe(keyPath, options: options, retainSelf: retainSelf) .map { (value: NSValue?) in @@ -105,6 +258,7 @@ extension NSObject { For more information take a look at `rx_observeWeakly` method. */ @warn_unused_result(message="http://git.io/rxs.uo") + @available(*, deprecated=2.0.0, message="Please use version that takes type as first argument.") public func rx_observeWeakly(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { return rx_observeWeakly(keyPath, options: options) .map { (value: NSValue?) in @@ -128,6 +282,7 @@ extension NSObject { For more information take a look at `rx_observeWeakly` method. */ @warn_unused_result(message="http://git.io/rxs.uo") + @available(*, deprecated=2.0.0, message="Please use version that takes type as first argument.") public func rx_observeWeakly(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { return rx_observeWeakly(keyPath, options: options) .map { (value: NSValue?) in @@ -151,6 +306,7 @@ extension NSObject { For more information take a look at `rx_observeWeakly` method. */ @warn_unused_result(message="http://git.io/rxs.uo") + @available(*, deprecated=2.0.0, message="Please use version that takes type as first argument.") public func rx_observeWeakly(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { return rx_observeWeakly(keyPath, options: options) .map { (value: NSValue?) in diff --git a/RxCocoa/Common/Observables/NSObject+Rx.swift b/RxCocoa/Common/Observables/NSObject+Rx.swift index 4f3acd33..c274f1b0 100644 --- a/RxCocoa/Common/Observables/NSObject+Rx.swift +++ b/RxCocoa/Common/Observables/NSObject+Rx.swift @@ -18,29 +18,51 @@ var deallocatingSubjectContext: UInt8 = 0 var deallocatedSubjectTriggerContext: UInt8 = 0 var deallocatedSubjectContext: UInt8 = 0 -// KVO is a tricky mechanism. -// -// When observing child in a ownership hierarchy, usually retaining observing target is wanted behavior. -// When observing parent in a ownership hierarchy, usually retaining target isn't wanter behavior. -// -// KVO with weak references is especially tricky. For it to work, some kind of swizzling is required. -// That can be done by -// * replacing object class dynamically (like KVO does) -// * by swizzling `dealloc` method on all instances for a class. -// * some third method ... -// -// Both approaches can fail in certain scenarios: -// * problems arise when swizzlers return original object class (like KVO does when nobody is observing) -// * Problems can arise because replacing dealloc method isn't atomic operation (get implementation, -// set implementation). -// -// Second approach is chosen. It can fail in case there are multiple libraries dynamically trying -// to replace dealloc method. In case that isn't the case, it should be ok. -// +/** +KVO is a tricky mechanism. -// KVO +When observing child in a ownership hierarchy, usually retaining observing target is wanted behavior. +When observing parent in a ownership hierarchy, usually retaining target isn't wanter behavior. + +KVO with weak references is especially tricky. For it to work, some kind of swizzling is required. +That can be done by + * replacing object class dynamically (like KVO does) + * by swizzling `dealloc` method on all instances for a class. + * some third method ... + +Both approaches can fail in certain scenarios: + * problems arise when swizzlers return original object class (like KVO does when nobody is observing) + * Problems can arise because replacing dealloc method isn't atomic operation (get implementation, + set implementation). + +Second approach is chosen. It can fail in case there are multiple libraries dynamically trying +to replace dealloc method. In case that isn't the case, it should be ok. +*/ extension NSObject { + + /** + Observes values on `keyPath` starting from `self` with `options` and retains `self` if `retainSelf` is set. + + `rx_observe` is just a simple and performant wrapper around KVO mechanism. + + * it can be used to observe paths starting from `self` or from ancestors in ownership graph (`retainSelf = false`) + * it can be used to observe paths starting from descendants in ownership graph (`retainSelf = true`) + * the paths have to consist only of `strong` properties, otherwise you are risking crashing the system by not unregistering KVO observer before dealloc. + + If support for weak properties is needed or observing arbitrary or unknown relationships in the + ownership tree, `rx_observeWeakly` is the preferred option. + + - parameter keyPath: Key path of property names to observe. + - parameter options: KVO mechanism notification options. + - parameter retainSelf: Retains self during observation if set `true`. + - returns: Observable sequence of objects on `keyPath`. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func rx_observe(type: Element.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { + return KVOObservable(object: self, keyPath: keyPath, options: options, retainTarget: retainSelf).asObservable() + } + /** Observes values on `keyPath` starting from `self` with `options` and retains `self` if `retainSelf` is set. @@ -58,6 +80,8 @@ extension NSObject { - parameter retainSelf: Retains self during observation if set `true`. - returns: Observable sequence of objects on `keyPath`. */ + @warn_unused_result(message="http://git.io/rxs.uo") + @available(*, deprecated=2.0.0, message="Please use version that takes type as first argument `rx_observe(type: Element.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable`") public func rx_observe(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { return KVOObservable(object: self, keyPath: keyPath, options: options, retainTarget: retainSelf).asObservable() } @@ -67,7 +91,28 @@ extension NSObject { #if !DISABLE_SWIZZLING // KVO extension NSObject { - + /** + Observes values on `keyPath` starting from `self` with `options` and doesn't retain `self`. + + It can be used in all cases where `rx_observe` can be used and additionally + + * because it won't retain observed target, it can be used to observe arbitrary object graph whose ownership relation is unknown + * it can be used to observe `weak` properties + + **Since it needs to intercept object deallocation process it needs to perform swizzling of `dealloc` method on observed object.** + + - parameter keyPath: Key path of property names to observe. + - parameter options: KVO mechanism notification options. + - returns: Observable sequence of objects on `keyPath`. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func rx_observeWeakly(type: Element.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { + return observeWeaklyKeyPathFor(self, keyPath: keyPath, options: options) + .map { n in + return n as? Element + } + } + /** Observes values on `keyPath` starting from `self` with `options` and doesn't retain `self`. @@ -82,6 +127,8 @@ extension NSObject { - parameter options: KVO mechanism notification options. - returns: Observable sequence of objects on `keyPath`. */ + @available(*, deprecated=2.0.0, message="Please use version that takes type as first argument `rx_observeWeakly(type: Element.Type, keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable`") + @warn_unused_result(message="http://git.io/rxs.uo") public func rx_observeWeakly(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { return observeWeaklyKeyPathFor(self, keyPath: keyPath, options: options) .map { n in diff --git a/RxTests/RxCocoaTests/KVOObservableTests.swift b/RxTests/RxCocoaTests/KVOObservableTests.swift index 6c02d071..2273094b 100644 --- a/RxTests/RxCocoaTests/KVOObservableTests.swift +++ b/RxTests/RxCocoaTests/KVOObservableTests.swift @@ -32,7 +32,7 @@ class Parent : NSObject { init(callback: String? -> Void) { super.init() - self.rx_observe("val", options: [.Initial, .New], retainSelf: false) + self.rx_observe(String.self, "val", options: [.Initial, .New], retainSelf: false) .subscribeNext(callback) .addDisposableTo(disposeBag) } @@ -47,7 +47,7 @@ class Child : NSObject { init(parent: ParentWithChild, callback: String? -> Void) { super.init() - parent.rx_observe("val", options: [.Initial, .New], retainSelf: false) + parent.rx_observe(String.self, "val", options: [.Initial, .New], retainSelf: false) .subscribeNext(callback) .addDisposableTo(disposeBag) } @@ -101,7 +101,7 @@ extension KVOObservableTests { func test_New() { let testClass = TestClass() - let os: Observable = testClass.rx_observe("pr", options: .New) + let os = testClass.rx_observe(String.self, "pr", options: .New) var latest: String? @@ -135,7 +135,7 @@ extension KVOObservableTests { func test_New_And_Initial() { let testClass = TestClass() - let os: Observable = testClass.rx_observe("pr", options: NSKeyValueObservingOptions(rawValue: NSKeyValueObservingOptions.Initial.rawValue | NSKeyValueObservingOptions.New.rawValue)) + let os = testClass.rx_observe(String.self, "pr", options: NSKeyValueObservingOptions(rawValue: NSKeyValueObservingOptions.Initial.rawValue | NSKeyValueObservingOptions.New.rawValue)) var latest: String? @@ -169,7 +169,7 @@ extension KVOObservableTests { func test_Default() { let testClass = TestClass() - let os: Observable = testClass.rx_observe("pr") + let os = testClass.rx_observe(String.self, "pr") var latest: String? @@ -266,8 +266,8 @@ extension KVOObservableTests { var root: HasStrongProperty! = HasStrongProperty() - _ = root.rx_observeWeakly("property") - .subscribeNext { (n: String?) in + _ = root.rx_observeWeakly(String.self, "property") + .subscribeNext { n in latest = n } @@ -296,8 +296,8 @@ extension KVOObservableTests { var root: HasWeakProperty! = HasWeakProperty() - _ = root.rx_observeWeakly("property") - .subscribeNext { (n: String?) in + _ = root.rx_observeWeakly(String.self, "property") + .subscribeNext { n in latest = n } @@ -330,8 +330,8 @@ extension KVOObservableTests { var root: HasWeakProperty! = HasWeakProperty() - _ = root.rx_observeWeakly("property.property") - .subscribeNext { (n: String?) in + _ = root.rx_observeWeakly(String.self, "property.property") + .subscribeNext { n in latest = n } @@ -379,8 +379,8 @@ extension KVOObservableTests { XCTAssertTrue(latest == nil) XCTAssertTrue(disposed == false) - _ = root.rx_observeWeakly("property.property") - .subscribeNext { (n: String?) in + _ = root.rx_observeWeakly(String.self, "property.property") + .subscribeNext { n in latest = n } @@ -407,8 +407,8 @@ extension KVOObservableTests { var root: HasStrongProperty! = HasStrongProperty() - _ = root.rx_observeWeakly("property.property") - .subscribeNext { (n: String?) in + _ = root.rx_observeWeakly(String.self, "property.property") + .subscribeNext { n in latest = n } @@ -456,8 +456,8 @@ extension KVOObservableTests { XCTAssertTrue(latest == nil) XCTAssertTrue(disposed == false) - _ = root.rx_observeWeakly("property.property") - .subscribeNext { (n: String?) in + _ = root.rx_observeWeakly(String.self, "property.property") + .subscribeNext { n in latest = n } @@ -495,7 +495,7 @@ extension KVOObservableTests { XCTAssertTrue(latest.value == nil) - let observable: Observable = root.rx_observeWeakly("property.property") + let observable = root.rx_observeWeakly(NSObject.self, "property.property") _ = observable .subscribeNext { n in latest?.value = n @@ -538,7 +538,7 @@ extension KVOObservableTests { XCTAssertTrue(latest.value == nil) - let observable: Observable = root.rx_observeWeakly("property.property.property") + let observable = root.rx_observeWeakly(NSObject.self, "property.property.property") _ = observable .subscribeNext { n in latest?.value = n @@ -582,8 +582,8 @@ extension KVOObservableTests { XCTAssertTrue(latest.value == nil) _ = root - .rx_observeWeakly("property") - .subscribeNext { (n: String?) in + .rx_observeWeakly(String.self, "property") + .subscribeNext { n in latest.value = n } @@ -613,8 +613,8 @@ extension KVOObservableTests { XCTAssertTrue(latest.value == nil) _ = root - .rx_observeWeakly("property", options: .New) - .subscribeNext { (n: String?) in + .rx_observeWeakly(String.self, "property", options: .New) + .subscribeNext { n in latest.value = n } @@ -683,8 +683,8 @@ extension KVOObservableTests { XCTAssertTrue(latest.value == nil) - let d = root.rx_observe("frame") - .subscribeNext { (n: CGSize?) in + let d = root.rx_observe(CGSize.self, "frame") + .subscribeNext { n in latest.value = n } @@ -719,8 +719,8 @@ extension KVOObservableTests { XCTAssertTrue(latest.value == nil) - let d = root.rx_observe("frame") - .subscribeNext { (n: CGRect?) in + let d = root.rx_observe(CGRect.self, "frame") + .subscribeNext { n in latest.value = n } @@ -755,8 +755,8 @@ extension KVOObservableTests { XCTAssertTrue(latest.value == nil) - let d = root.rx_observe("size") - .subscribeNext { (n: CGSize?) in + let d = root.rx_observe(CGSize.self, "size") + .subscribeNext { n in latest.value = n } @@ -791,8 +791,8 @@ extension KVOObservableTests { XCTAssertTrue(latest.value == nil) - let d = root.rx_observe("point") - .subscribeNext { (n: CGPoint?) in + let d = root.rx_observe(CGPoint.self, "point") + .subscribeNext { n in latest.value = n } defer { @@ -828,8 +828,8 @@ extension KVOObservableTests { XCTAssertTrue(latest.value == nil) _ = root - .rx_observeWeakly("frame") - .subscribeNext { (n: CGRect?) in + .rx_observeWeakly(CGRect.self, "frame") + .subscribeNext { n in latest.value = n } XCTAssertTrue(latest.value == root.frame) @@ -860,8 +860,8 @@ extension KVOObservableTests { XCTAssertTrue(latest.value == nil) _ = root - .rx_observeWeakly("size") - .subscribeNext { (n: CGSize?) in + .rx_observeWeakly(CGSize.self, "size") + .subscribeNext { n in latest.value = n } XCTAssertTrue(latest.value == root.size) @@ -892,8 +892,8 @@ extension KVOObservableTests { XCTAssertTrue(latest.value == nil) _ = root - .rx_observeWeakly("point") - .subscribeNext { (n: CGPoint?) in + .rx_observeWeakly(CGPoint.self, "point") + .subscribeNext { n in latest.value = n } @@ -925,8 +925,8 @@ extension KVOObservableTests { XCTAssertTrue(latest.value == nil) _ = root - .rx_observeWeakly("integer") - .subscribeNext { (n: NSNumber?) in + .rx_observeWeakly(NSNumber.self, "integer") + .subscribeNext { n in latest.value = n?.integerValue } XCTAssertTrue(latest.value == root.integer) @@ -954,7 +954,7 @@ extension KVOObservableTests { var lastError: ErrorType? = nil - _ = (root.rx_observeWeakly("notExist") as Observable) + _ = root.rx_observeWeakly(NSNumber.self, "notExist") .subscribeError { error in lastError = error } @@ -980,7 +980,7 @@ extension KVOObservableTests { var lastError: ErrorType? = nil - _ = (root.rx_observeWeakly("property.notExist") as Observable) + _ = root.rx_observeWeakly(NSNumber.self, "property.notExist") .subscribeError { error in lastError = error } From c79cea523f775c9ec672cf619b486a43b74e7284 Mon Sep 17 00:00:00 2001 From: Tomi Koskinen Date: Mon, 9 Nov 2015 21:19:56 +0200 Subject: [PATCH 172/210] Blocking version of single operator --- RxBlocking/BlockingObservable+Operators.swift | 55 +++++++++++++++++- RxSwift/Error.swift | 12 ++++ .../Tests/Observable+BlockingTest.swift | 58 +++++++++++++++++++ 3 files changed, 124 insertions(+), 1 deletion(-) diff --git a/RxBlocking/BlockingObservable+Operators.swift b/RxBlocking/BlockingObservable+Operators.swift index 3f05801c..3e1ca201 100644 --- a/RxBlocking/BlockingObservable+Operators.swift +++ b/RxBlocking/BlockingObservable+Operators.swift @@ -144,4 +144,57 @@ extension BlockingObservable { return element } -} \ No newline at end of file +} + +extension BlockingObservable { + /** + Blocks current thread until sequence terminates. + + If sequence terminates with error before producing first element, terminating error will be thrown. + + - returns: Returns the only element of an sequence, and reports an exception if there is not exactly one element in the observable sequence. + */ + public func single() throws -> E? { + var element: E? + + var error: ErrorType? + + let d = SingleAssignmentDisposable() + + let lock = RunLoopLock() + + lock.dispatch { + d.disposable = self.source.subscribe { e in + switch e { + case .Next(let e): + if element == nil { + element = e + } else { + error = RxError.MoreThanOneElement + } + return + case .Error(let e): + error = e + break + case .Completed: + if element == nil { + error = RxError.NoElements + } + break + } + + lock.stop() + } + } + + lock.run() + + d.dispose() + + if let error = error { + throw error + } + + return element + } +} diff --git a/RxSwift/Error.swift b/RxSwift/Error.swift index 63ea3238..30798839 100644 --- a/RxSwift/Error.swift +++ b/RxSwift/Error.swift @@ -33,6 +33,14 @@ public enum RxError Argument out of range error. */ case ArgumentOutOfRange + /** + No elements sent to a sequence requiring at least one. + */ + case NoElements + /** + More elements sent to a sequence expecting only one. + */ + case MoreThanOneElement } public extension RxError { @@ -49,6 +57,10 @@ public extension RxError { return "Arithmetic overflow occured" case .ArgumentOutOfRange: return "Argument out of range" + case .NoElements: + return "No element sent to a sequence requiring at least one" + case .MoreThanOneElement: + return "More elements sent to a sequence expecting only one" } } } \ No newline at end of file diff --git a/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift b/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift index 00b4c410..76cd26e8 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift @@ -126,3 +126,61 @@ extension ObservableBlockingTest { } +// single + +extension ObservableBlockingTest { + func testSingle_empty() { + do { + try (empty() as Observable).toBlocking().single() + XCTFail() + } + catch let e { + XCTAssertTrue((e as! RxError)._code == RxError.NoElements._code) + } + } + + func testSingle_return() { + XCTAssert(try! just(42).toBlocking().single() == 42) + } + + func testSingle_two() { + do { + try (sequenceOf(42, 43) as Observable).toBlocking().single() + XCTFail() + } + catch let e { + XCTAssertTrue((e as! RxError)._code == RxError.MoreThanOneElement._code) + } + } + + func testSingle_someData() { + do { + try (sequenceOf(42, 43, 44, 45) as Observable).toBlocking().single() + XCTFail() + } + catch let e { + XCTAssertTrue((e as! RxError)._code == RxError.MoreThanOneElement._code) + } + } + + func testSingle_fail() { + do { + try (failWith(testError) as Observable).toBlocking().single() + XCTFail() + } + catch let e { + XCTAssertTrue(e as NSError === testError) + } + } + + func testSingle_withRealScheduler() { + let scheduler = ConcurrentDispatchQueueScheduler(globalConcurrentQueuePriority: .Default) + + let array = try! interval(0.001, scheduler) + .take(1) + .toBlocking() + .single() + + XCTAssert(array == 0) + } +} From ce5b31b8a96351a08116cb6f0a9371fe9c2e5512 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 9 Nov 2015 23:06:37 +0100 Subject: [PATCH 173/210] Renames `rx_tap` to `rx_primaryAction` on `UIButton` for `tvOS` and adds it to unit tests. --- RxCocoa/iOS/UIButton+Rx.swift | 2 +- .../RxCocoaTests/Control+RxTests+UIKit.swift | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/RxCocoa/iOS/UIButton+Rx.swift b/RxCocoa/iOS/UIButton+Rx.swift index 8945af61..6c16240c 100644 --- a/RxCocoa/iOS/UIButton+Rx.swift +++ b/RxCocoa/iOS/UIButton+Rx.swift @@ -40,7 +40,7 @@ extension UIButton { /** Reactive wrapper for `PrimaryActionTriggered` control event. */ - public var rx_tap: ControlEvent { + public var rx_primaryAction: ControlEvent { return rx_controlEvents(.PrimaryActionTriggered) } } diff --git a/RxTests/RxCocoaTests/Control+RxTests+UIKit.swift b/RxTests/RxCocoaTests/Control+RxTests+UIKit.swift index ceb6dff7..e7e963ec 100644 --- a/RxTests/RxCocoaTests/Control+RxTests+UIKit.swift +++ b/RxTests/RxCocoaTests/Control+RxTests+UIKit.swift @@ -361,4 +361,23 @@ extension ControlTests { } } + +// UIButton +extension ControlTests { + func testButton_tapDeallocates() { + let createView: () -> UIButton = { UIButton(frame: CGRectMake(0, 0, 1, 1)) } + ensureEventDeallocated(createView) { (view: UIButton) in view.rx_tap } + } +} + +#elseif os(tvOS) + +// UIButton +extension ControlTests { + func testButton_tapDeallocates() { + let createView: () -> UIButton = { UIButton(frame: CGRectMake(0, 0, 1, 1)) } + ensureEventDeallocated(createView) { (view: UIButton) in view.rx_primaryAction } + } +} + #endif From edeaf53b276024344e8efb756d70c7e0478f0ed3 Mon Sep 17 00:00:00 2001 From: Tomi Koskinen Date: Tue, 10 Nov 2015 20:25:52 +0200 Subject: [PATCH 174/210] Blocking single operator with predicate --- RxBlocking/BlockingObservable+Operators.swift | 60 ++++++++++++ .../Tests/Observable+BlockingTest.swift | 97 +++++++++++++++++++ 2 files changed, 157 insertions(+) diff --git a/RxBlocking/BlockingObservable+Operators.swift b/RxBlocking/BlockingObservable+Operators.swift index 3e1ca201..4cceaa8e 100644 --- a/RxBlocking/BlockingObservable+Operators.swift +++ b/RxBlocking/BlockingObservable+Operators.swift @@ -197,4 +197,64 @@ extension BlockingObservable { return element } + + /** + Blocks current thread until sequence terminates. + + If sequence terminates with error before producing first element, terminating error will be thrown. + + - parameter predicate: A function to test each source element for a condition. + - returns: Returns the only element of an sequence that satisfies the condition in the predicate, and reports an error if there is not exactly one element in the sequence. + */ + public func single(predicate: (E) throws -> Bool) throws -> E? { + var element: E? + + var error: ErrorType? + + let d = SingleAssignmentDisposable() + + let lock = RunLoopLock() + + lock.dispatch { + d.disposable = self.source.subscribe { e in + switch e { + case .Next(let e): + do { + if try error == nil && predicate(e) { + if element == nil { + element = e + } else { + error = RxError.MoreThanOneElement + } + } + } catch (let err) { + error = err + } + return + case .Error(let e): + error = e + lock.stop() + case .Completed: + if error != nil { + break + } else if element == nil { + error = RxError.NoElements + } + break + } + + lock.stop() + } + } + + lock.run() + + d.dispose() + + if let error = error { + throw error + } + + return element + } } diff --git a/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift b/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift index 76cd26e8..55876d5a 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift @@ -183,4 +183,101 @@ extension ObservableBlockingTest { XCTAssert(array == 0) } + + + func testSingle_predicate_empty() { + do { + try (empty() as Observable).toBlocking().single { _ in true } + XCTFail() + } + catch let e { + XCTAssertTrue((e as! RxError)._code == RxError.NoElements._code) + } + } + + func testSingle_predicate_return() { + XCTAssert(try! just(42).toBlocking().single( { _ in true } ) == 42) + } + + func testSingle_predicate_someData_one_match() { + var predicateVals = [Int]() + do { + try (sequenceOf(42, 43, 44, 45) as Observable).toBlocking().single( { e in + predicateVals.append(e) + return e == 44 + } ) + } + catch _ { + XCTFail() + } + XCTAssertEqual(predicateVals, [42, 43, 44, 45]) + } + + func testSingle_predicate_someData_two_match() { + var predicateVals = [Int]() + do { + try (sequenceOf(42, 43, 44, 45) as Observable).toBlocking().single( { e in + predicateVals.append(e) + return e >= 43 + } ) + XCTFail() + } + catch let e { + XCTAssertTrue((e as! RxError)._code == RxError.MoreThanOneElement._code) + } + XCTAssertEqual(predicateVals, [42, 43, 44]) + } + + + func testSingle_predicate_none() { + var predicateVals = [Int]() + do { + try (sequenceOf(42, 43, 44, 45) as Observable).toBlocking().single( { e in + predicateVals.append(e) + return e > 50 + } ) + XCTFail() + } + catch let e { + XCTAssertTrue((e as! RxError)._code == RxError.NoElements._code) + } + XCTAssertEqual(predicateVals, [42, 43, 44, 45]) + } + + func testSingle_predicate_throws() { + var predicateVals = [Int]() + do { + try (sequenceOf(42, 43, 44, 45) as Observable).toBlocking().single( { e in + predicateVals.append(e) + if e < 43 { return false } + throw testError + } ) + XCTFail() + } + catch let e { + XCTAssertTrue(e as NSError === testError) + } + XCTAssertEqual(predicateVals, [42, 43]) + } + + func testSingle_predicate_fail() { + do { + try (failWith(testError) as Observable).toBlocking().single( { _ in true } ) + XCTFail() + } + catch let e { + XCTAssertTrue(e as NSError === testError) + } + } + + func testSingle_predicate_withRealScheduler() { + let scheduler = ConcurrentDispatchQueueScheduler(globalConcurrentQueuePriority: .Default) + + let array = try! interval(0.001, scheduler) + .take(4) + .toBlocking() + .single( { $0 == 3 } ) + + XCTAssert(array == 3) + } } From 45d1aa60471785d9f6bd17cff418ad02b3a820e7 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Wed, 11 Nov 2015 10:12:11 +0100 Subject: [PATCH 175/210] Changes some texts. --- RxBlocking/BlockingObservable+Operators.swift | 2 +- RxSwift/Error.swift | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/RxBlocking/BlockingObservable+Operators.swift b/RxBlocking/BlockingObservable+Operators.swift index 3e1ca201..2d8b8202 100644 --- a/RxBlocking/BlockingObservable+Operators.swift +++ b/RxBlocking/BlockingObservable+Operators.swift @@ -152,7 +152,7 @@ extension BlockingObservable { If sequence terminates with error before producing first element, terminating error will be thrown. - - returns: Returns the only element of an sequence, and reports an exception if there is not exactly one element in the observable sequence. + - returns: Returns the only element of an sequence, and reports an error if there is not exactly one element in the observable sequence. */ public func single() throws -> E? { var element: E? diff --git a/RxSwift/Error.swift b/RxSwift/Error.swift index 30798839..e6a94387 100644 --- a/RxSwift/Error.swift +++ b/RxSwift/Error.swift @@ -34,11 +34,11 @@ public enum RxError */ case ArgumentOutOfRange /** - No elements sent to a sequence requiring at least one. + Sequence doesn't contain any element. */ case NoElements /** - More elements sent to a sequence expecting only one. + Sequence contains more then one element. */ case MoreThanOneElement } @@ -50,17 +50,17 @@ public extension RxError { public var debugDescription: String { switch self { case .Unknown: - return "Unknown error occured" + return "Unknown error occured." case .Disposed(let object): - return "Object `\(object)` was already disposed" + return "Object `\(object)` was already disposed." case .Overflow: - return "Arithmetic overflow occured" + return "Arithmetic overflow occured." case .ArgumentOutOfRange: - return "Argument out of range" + return "Argument out of range." case .NoElements: - return "No element sent to a sequence requiring at least one" + return "Sequence doesn't contain any element." case .MoreThanOneElement: - return "More elements sent to a sequence expecting only one" + return "Sequence contains more then one element." } } } \ No newline at end of file From 7e92fb72481dce7ca480effea97553f0843116f4 Mon Sep 17 00:00:00 2001 From: Junior B Date: Mon, 9 Nov 2015 17:29:29 +0100 Subject: [PATCH 176/210] Add `single` non-blocking operator --- Rx.xcodeproj/project.pbxproj | 10 +++ .../Implementations/SingleAsync.swift | 80 +++++++++++++++++++ ...Observable+StandardSequenceOperators.swift | 33 +++++++- 3 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 RxSwift/Observables/Implementations/SingleAsync.swift diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index fdb94d15..693caf24 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -585,6 +585,10 @@ CB255BD81BC46A9C00798A4C /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB255BD61BC46A9C00798A4C /* RetryWhen.swift */; }; CB255BD91BC46A9C00798A4C /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB255BD61BC46A9C00798A4C /* RetryWhen.swift */; }; CB255BDA1BC46A9C00798A4C /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB255BD61BC46A9C00798A4C /* RetryWhen.swift */; }; + CB30D9E91BF0E3500084C1C0 /* SingleAsync.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30D9E81BF0E3500084C1C0 /* SingleAsync.swift */; }; + CB30D9EA1BF0E3500084C1C0 /* SingleAsync.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30D9E81BF0E3500084C1C0 /* SingleAsync.swift */; }; + CB30D9EB1BF0E3500084C1C0 /* SingleAsync.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30D9E81BF0E3500084C1C0 /* SingleAsync.swift */; }; + CB30D9EC1BF0E3500084C1C0 /* SingleAsync.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30D9E81BF0E3500084C1C0 /* SingleAsync.swift */; }; CB883B3B1BE24355000AC2EE /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B3A1BE24355000AC2EE /* Window.swift */; }; CB883B3C1BE24355000AC2EE /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B3A1BE24355000AC2EE /* Window.swift */; }; CB883B3D1BE24355000AC2EE /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B3A1BE24355000AC2EE /* Window.swift */; }; @@ -1063,6 +1067,7 @@ C8F6A0F81BEE33C1007DF367 /* InvocableScheduledItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InvocableScheduledItem.swift; sourceTree = ""; }; C8F6A0FD1BEE42DD007DF367 /* AnonymousInvocable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnonymousInvocable.swift; sourceTree = ""; }; CB255BD61BC46A9C00798A4C /* RetryWhen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RetryWhen.swift; sourceTree = ""; }; + CB30D9E81BF0E3500084C1C0 /* SingleAsync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SingleAsync.swift; sourceTree = ""; }; CB883B3A1BE24355000AC2EE /* Window.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = ""; }; CB883B3F1BE24C15000AC2EE /* RefCountDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefCountDisposable.swift; sourceTree = ""; }; CB883B441BE256D4000AC2EE /* BooleanDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BooleanDisposable.swift; sourceTree = ""; }; @@ -1302,6 +1307,7 @@ C8093C861B8A72BE0088E94D /* Sample.swift */, C8093C871B8A72BE0088E94D /* Scan.swift */, C89CDB351BCB0DD7002063D9 /* ShareReplay1.swift */, + CB30D9E81BF0E3500084C1C0 /* SingleAsync.swift */, C8093C881B8A72BE0088E94D /* Sink.swift */, C8093C891B8A72BE0088E94D /* Skip.swift */, D285BAC31BC0231000B3F602 /* SkipUntil.swift */, @@ -2279,6 +2285,7 @@ C8093CDE1B8A72BE0088E94D /* DisposeBag.swift in Sources */, C8093D981B8A72BE0088E94D /* RecursiveScheduler.swift in Sources */, C8093D381B8A72BE0088E94D /* Scan.swift in Sources */, + CB30D9EA1BF0E3500084C1C0 /* SingleAsync.swift in Sources */, C8093CD21B8A72BE0088E94D /* Queue.swift in Sources */, C8C3DA131B93A3EA004D233E /* AnonymousObservable.swift in Sources */, C8093D201B8A72BE0088E94D /* FlatMap.swift in Sources */, @@ -2416,6 +2423,7 @@ C8093CDD1B8A72BE0088E94D /* DisposeBag.swift in Sources */, C8093D971B8A72BE0088E94D /* RecursiveScheduler.swift in Sources */, C8093D371B8A72BE0088E94D /* Scan.swift in Sources */, + CB30D9E91BF0E3500084C1C0 /* SingleAsync.swift in Sources */, C8093CD11B8A72BE0088E94D /* Queue.swift in Sources */, C8C3DA121B93A3EA004D233E /* AnonymousObservable.swift in Sources */, C8093D1F1B8A72BE0088E94D /* FlatMap.swift in Sources */, @@ -2553,6 +2561,7 @@ C8F0BFC91BBBFB8B001B112F /* DisposeBag.swift in Sources */, C8F0BFCA1BBBFB8B001B112F /* RecursiveScheduler.swift in Sources */, C8F0BFCB1BBBFB8B001B112F /* Scan.swift in Sources */, + CB30D9EC1BF0E3500084C1C0 /* SingleAsync.swift in Sources */, C8F0BFCC1BBBFB8B001B112F /* Queue.swift in Sources */, C8F0BFCD1BBBFB8B001B112F /* AnonymousObservable.swift in Sources */, C8F0BFCE1BBBFB8B001B112F /* FlatMap.swift in Sources */, @@ -2841,6 +2850,7 @@ D2EBEAEC1BB9B69E003A27DC /* Lock.swift in Sources */, D2EBEB3C1BB9B6D8003A27DC /* RecursiveScheduler.swift in Sources */, D2EBEAF61BB9B6B2003A27DC /* NopDisposable.swift in Sources */, + CB30D9EB1BF0E3500084C1C0 /* SingleAsync.swift in Sources */, D2EBEAFF1BB9B6BA003A27DC /* Buffer.swift in Sources */, D2EBEAF51BB9B6AE003A27DC /* NAryDisposable.swift in Sources */, D2EBEB1D1BB9B6C1003A27DC /* Scan.swift in Sources */, diff --git a/RxSwift/Observables/Implementations/SingleAsync.swift b/RxSwift/Observables/Implementations/SingleAsync.swift new file mode 100644 index 00000000..12135c17 --- /dev/null +++ b/RxSwift/Observables/Implementations/SingleAsync.swift @@ -0,0 +1,80 @@ +// +// SingleAsync.swift +// Rx +// +// Created by Junior B. on 09/11/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +class SingleAsyncSink : Sink, ObserverType { + typealias Parent = SingleAsync + typealias E = ElementType + + private let _parent: Parent + private var _seenValue: Bool = false + + init(parent: Parent, observer: O) { + _parent = parent + super.init(observer: observer) + } + + func on(event: Event) { + switch event { + case .Next(let value): + + if let predicate = _parent._predicate { + do { + print("val: \(value)") + let forward = try predicate(value) + if forward && _seenValue == false { + forwardOn(.Next(value)) + _seenValue = true + } else if forward && _seenValue { + forwardOn(.Error(RxError.MoreThanOneElement)) + dispose() + } + } catch (let error) { + forwardOn(.Error(error as ErrorType)) + dispose() + } + } else if _seenValue == false { + forwardOn(.Next(value)) + _seenValue = true + } else { + forwardOn(.Error(RxError.MoreThanOneElement)) + dispose() + } + + case .Error: + forwardOn(event) + dispose() + case .Completed: + if (!_seenValue) { + forwardOn(.Error(RxError.NoElements)) + } else { + forwardOn(.Completed) + } + dispose() + } + } +} + +class SingleAsync: Producer { + typealias Predicate = (Element) throws -> Bool + + private let _source: Observable + private let _predicate: Predicate? + + init(source: Observable, predicate: Predicate? = nil) { + _source = source + _predicate = predicate + } + + override func run(observer: O) -> Disposable { + let sink = SingleAsyncSink(parent: self, observer: observer) + sink.disposable = _source.subscribe(sink) + return sink + } +} \ No newline at end of file diff --git a/RxSwift/Observables/Observable+StandardSequenceOperators.swift b/RxSwift/Observables/Observable+StandardSequenceOperators.swift index 3dbed145..31dad0bb 100644 --- a/RxSwift/Observables/Observable+StandardSequenceOperators.swift +++ b/RxSwift/Observables/Observable+StandardSequenceOperators.swift @@ -218,7 +218,7 @@ extension ObservableType { } } -// elementAt +// MARK: elementAt extension ObservableType { @@ -233,4 +233,35 @@ extension ObservableType { -> Observable { return ElementAt(source: self.asObservable(), index: index, throwOnEmpty: true) } +} + +// MARK: single + +extension ObservableType { + + /** + The single operator is similar to first, but throws a `RxError.NoElements` or `RxError.MoreThanOneElement` + if the source Observable does not emit exactly one item before successfully completing. + + - returns: An observable sequence that emits a single item or throws an exception if more (or none) of them are emitted. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func single() + -> Observable { + return SingleAsync(source: self.asObservable()) + } + + /** + The single operator is similar to first, but throws a `RxError.NoElements` or `RxError.MoreThanOneElement` + if the source Observable does not emit exactly one item before successfully completing. + + - parameter predicate: A function to test each source element for a condition. + - returns: An observable sequence that emits a single item or throws an exception if more (or none) of them are emitted. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func single(predicate: (E) throws -> Bool) + -> Observable { + return SingleAsync(source: self.asObservable(), predicate: predicate) + } + } \ No newline at end of file From 9663469b011c50f11198c993f0bd1d6ce1b9ce13 Mon Sep 17 00:00:00 2001 From: Junior B Date: Mon, 9 Nov 2015 17:29:48 +0100 Subject: [PATCH 177/210] Add tests for `single` non-blocking operator --- RxExample/RxExample.xcodeproj/project.pbxproj | 4 + ...rvable+StandardSequenceOperatorsTest.swift | 227 ++++++++++++++++++ 2 files changed, 231 insertions(+) diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index 3bcbc508..bd9b237f 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -348,6 +348,7 @@ C8F6A1341BEF9DA3007DF367 /* SchedulerServices+Emulation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464951BC6C2B00055219D /* SchedulerServices+Emulation.swift */; }; C8F6A1351BEF9DA3007DF367 /* SerialDispatchQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464961BC6C2B00055219D /* SerialDispatchQueueScheduler.swift */; }; C8F6A1381BEF9DF6007DF367 /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A1361BEF9DD4007DF367 /* RetryWhen.swift */; }; + CB30D9EE1BF106260084C1C0 /* SingleAsync.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30D9ED1BF106260084C1C0 /* SingleAsync.swift */; }; CB883B501BE3AC54000AC2EE /* BooleanDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B4E1BE3AC54000AC2EE /* BooleanDisposable.swift */; }; CB883B511BE3AC54000AC2EE /* RefCountDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B4F1BE3AC54000AC2EE /* RefCountDisposable.swift */; }; CB883B601BE3AC72000AC2EE /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB883B5E1BE3AC72000AC2EE /* Window.swift */; }; @@ -747,6 +748,7 @@ C8F6A1061BEF9D83007DF367 /* InvocableType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InvocableType.swift; sourceTree = ""; }; C8F6A1071BEF9D83007DF367 /* ScheduledItemType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduledItemType.swift; sourceTree = ""; }; C8F6A1361BEF9DD4007DF367 /* RetryWhen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RetryWhen.swift; sourceTree = ""; }; + CB30D9ED1BF106260084C1C0 /* SingleAsync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SingleAsync.swift; sourceTree = ""; }; CB883B4E1BE3AC54000AC2EE /* BooleanDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BooleanDisposable.swift; sourceTree = ""; }; CB883B4F1BE3AC54000AC2EE /* RefCountDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefCountDisposable.swift; sourceTree = ""; }; CB883B5E1BE3AC72000AC2EE /* Window.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = ""; }; @@ -1198,6 +1200,7 @@ C894646A1BC6C2B00055219D /* Sample.swift */, C894646B1BC6C2B00055219D /* Scan.swift */, C89CDB611BCC45DC002063D9 /* ShareReplay1.swift */, + CB30D9ED1BF106260084C1C0 /* SingleAsync.swift */, C894646C1BC6C2B00055219D /* Sink.swift */, C894646D1BC6C2B00055219D /* Skip.swift */, C89CDB621BCC45DC002063D9 /* SkipUntil.swift */, @@ -1825,6 +1828,7 @@ C89465701BC6C2BC0055219D /* Logging.swift in Sources */, C89464A31BC6C2B00055219D /* InfiniteSequence.swift in Sources */, C89465611BC6C2BC0055219D /* _RX.m in Sources */, + CB30D9EE1BF106260084C1C0 /* SingleAsync.swift in Sources */, C80DDED41BCE9046006A1832 /* Driver+Operators+arity.swift in Sources */, C89465841BC6C2BC0055219D /* RxActionSheetDelegateProxy.swift in Sources */, C89464F51BC6C2B00055219D /* AnyObserver.swift in Sources */, diff --git a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift index e6d5e060..db342e69 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift @@ -4703,3 +4703,230 @@ extension ObservableStandardSequenceOperatorsTest { ]) } } + + +// MARK: single +extension ObservableStandardSequenceOperatorsTest { + + func testSingle_Empty() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + completed(250) + ]) + + let res = scheduler.start { + xs.single() + } + + XCTAssertEqual(res.messages, [ + error(250, RxError.NoElements) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 250) + ]) + } + + func testSingle_One() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + completed(250) + ]) + + let res = scheduler.start { + xs.single() + } + + XCTAssertEqual(res.messages, [ + next(210, 2), + completed(250) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 250) + ]) + } + + func testSingle_Many() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + next(220, 3), + completed(250) + ]) + + let res = scheduler.start { + xs.single() + } + + XCTAssertEqual(res.messages, [ + next(210, 2), + error(220, RxError.MoreThanOneElement) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 220) + ]) + } + + func testSingle_Error() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + error(210, testError) + ]) + + let res = scheduler.start { + xs.single() + } + + XCTAssertEqual(res.messages, [ + error(210, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 210) + ]) + } + + func testSinglePredicate_Empty() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + completed(250) + ]) + + let res = scheduler.start { + xs.single { e in + return e % 2 == 1 + } + } + + XCTAssertEqual(res.messages, [ + error(250, RxError.NoElements) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 250) + ]) + } + + func testSinglePredicate_One() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5), + completed(250) + ]) + + let res = scheduler.start { + xs.single() { e in + return e == 4 + } + } + + XCTAssertEqual(res.messages, [ + next(230, 4), + completed(250) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 250) + ]) + } + + func testSinglePredicate_Many() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5), + completed(250) + ]) + + let res = scheduler.start { + xs.single() { e in + return (e % 2) == 1 + } + } + + XCTAssertEqual(res.messages, [ + next(220, 3), + error(240, RxError.MoreThanOneElement) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 240) + ]) + } + + func testSinglePredicate_Error() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + error(210, testError) + ]) + + let res = scheduler.start { + xs.single() { e in + return e % 2 == 1 + } + } + + XCTAssertEqual(res.messages, [ + error(210, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 210) + ]) + } + + func testSinglePredicate_Throws() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5), + completed(250) + ]) + + let res = scheduler.start { + xs.single() { (e: Int) -> Bool in + guard e < 4 else { + throw testError + } + return false + } + } + + XCTAssertEqual(res.messages, [ + error(230, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 230) + ]) + } + +} From df0ede1bf21f75ee5d1c0ac99899546345f7c6b1 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Wed, 11 Nov 2015 11:29:30 +0100 Subject: [PATCH 178/210] Removes debugging code. --- RxSwift/Observables/Implementations/SingleAsync.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/RxSwift/Observables/Implementations/SingleAsync.swift b/RxSwift/Observables/Implementations/SingleAsync.swift index 12135c17..5285f4bd 100644 --- a/RxSwift/Observables/Implementations/SingleAsync.swift +++ b/RxSwift/Observables/Implementations/SingleAsync.swift @@ -26,7 +26,6 @@ class SingleAsyncSink : S if let predicate = _parent._predicate { do { - print("val: \(value)") let forward = try predicate(value) if forward && _seenValue == false { forwardOn(.Next(value)) From c2ca7e6b04c530c170cbad75f092a49ff0eab399 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Wed, 11 Nov 2015 11:40:01 +0100 Subject: [PATCH 179/210] Adds early return. --- .../Implementations/SingleAsync.swift | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/RxSwift/Observables/Implementations/SingleAsync.swift b/RxSwift/Observables/Implementations/SingleAsync.swift index 5285f4bd..38938144 100644 --- a/RxSwift/Observables/Implementations/SingleAsync.swift +++ b/RxSwift/Observables/Implementations/SingleAsync.swift @@ -23,22 +23,19 @@ class SingleAsyncSink : S func on(event: Event) { switch event { case .Next(let value): - - if let predicate = _parent._predicate { - do { - let forward = try predicate(value) - if forward && _seenValue == false { - forwardOn(.Next(value)) - _seenValue = true - } else if forward && _seenValue { - forwardOn(.Error(RxError.MoreThanOneElement)) - dispose() - } - } catch (let error) { - forwardOn(.Error(error as ErrorType)) - dispose() + do { + let forward = try _parent._predicate?(value) ?? true + if !forward { + return } - } else if _seenValue == false { + } + catch let error { + forwardOn(.Error(error as ErrorType)) + dispose() + return + } + + if _seenValue == false { forwardOn(.Next(value)) _seenValue = true } else { From e6f79b510f9acbb2ce0ea8e51acc30845076bf56 Mon Sep 17 00:00:00 2001 From: Daniel Tartaglia Date: Wed, 11 Nov 2015 20:39:58 -0500 Subject: [PATCH 180/210] Update UIImageView+Rx.swift --- RxCocoa/iOS/UIImageView+Rx.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RxCocoa/iOS/UIImageView+Rx.swift b/RxCocoa/iOS/UIImageView+Rx.swift index a3e62a36..e14a688f 100644 --- a/RxCocoa/iOS/UIImageView+Rx.swift +++ b/RxCocoa/iOS/UIImageView+Rx.swift @@ -19,7 +19,7 @@ extension UIImageView { /** Bindable sink for `image` property. */ - public var rx_image: AnyObserver { + public var rx_image: AnyObserver { return self.rx_imageAnimated(nil) } @@ -28,7 +28,7 @@ extension UIImageView { - parameter transitionType: Optional transition type while setting the image (kCATransitionFade, kCATransitionMoveIn, ...) */ - public func rx_imageAnimated(transitionType: String?) -> AnyObserver { + public func rx_imageAnimated(transitionType: String?) -> AnyObserver { return AnyObserver { [weak self] event in MainScheduler.ensureExecutingOnScheduler() From 5499c10f9a61a2ab815f33066727b7215fcecc95 Mon Sep 17 00:00:00 2001 From: Tomi Koskinen Date: Wed, 11 Nov 2015 18:43:22 +0200 Subject: [PATCH 181/210] remove duplicate blocking single code --- RxBlocking/BlockingObservable+Operators.swift | 42 +------------------ 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/RxBlocking/BlockingObservable+Operators.swift b/RxBlocking/BlockingObservable+Operators.swift index 4cceaa8e..d40f3a0d 100644 --- a/RxBlocking/BlockingObservable+Operators.swift +++ b/RxBlocking/BlockingObservable+Operators.swift @@ -155,47 +155,7 @@ extension BlockingObservable { - returns: Returns the only element of an sequence, and reports an exception if there is not exactly one element in the observable sequence. */ public func single() throws -> E? { - var element: E? - - var error: ErrorType? - - let d = SingleAssignmentDisposable() - - let lock = RunLoopLock() - - lock.dispatch { - d.disposable = self.source.subscribe { e in - switch e { - case .Next(let e): - if element == nil { - element = e - } else { - error = RxError.MoreThanOneElement - } - return - case .Error(let e): - error = e - break - case .Completed: - if element == nil { - error = RxError.NoElements - } - break - } - - lock.stop() - } - } - - lock.run() - - d.dispose() - - if let error = error { - throw error - } - - return element + return try single { _ in true } } /** From 14affd773c5cf785e6c27da2d37c253f1bc2c3da Mon Sep 17 00:00:00 2001 From: Tomi Koskinen Date: Wed, 11 Nov 2015 18:41:39 +0200 Subject: [PATCH 182/210] exit loop and dispose subscription immediately --- RxBlocking/BlockingObservable+Operators.swift | 19 ++++++++++--------- .../Tests/Observable+BlockingTest.swift | 4 ++-- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/RxBlocking/BlockingObservable+Operators.swift b/RxBlocking/BlockingObservable+Operators.swift index d40f3a0d..b0a778e4 100644 --- a/RxBlocking/BlockingObservable+Operators.swift +++ b/RxBlocking/BlockingObservable+Operators.swift @@ -180,24 +180,25 @@ extension BlockingObservable { switch e { case .Next(let e): do { - if try error == nil && predicate(e) { - if element == nil { - element = e - } else { - error = RxError.MoreThanOneElement - } + if try !predicate(e) { + return + } + if element == nil { + element = e + } else { + throw RxError.MoreThanOneElement } } catch (let err) { error = err + d.dispose() + lock.stop() } return case .Error(let e): error = e lock.stop() case .Completed: - if error != nil { - break - } else if element == nil { + if error == nil && element == nil { error = RxError.NoElements } break diff --git a/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift b/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift index 55876d5a..0fd88c2c 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift @@ -225,7 +225,7 @@ extension ObservableBlockingTest { catch let e { XCTAssertTrue((e as! RxError)._code == RxError.MoreThanOneElement._code) } - XCTAssertEqual(predicateVals, [42, 43, 44]) + XCTAssertEqual(predicateVals, [42, 43, 44, 45]) } @@ -257,7 +257,7 @@ extension ObservableBlockingTest { catch let e { XCTAssertTrue(e as NSError === testError) } - XCTAssertEqual(predicateVals, [42, 43]) + XCTAssertEqual(predicateVals, [42, 43, 44, 45]) } func testSingle_predicate_fail() { From 80d6302fda996dca172f3ef9341b6cfe0b40a3f8 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sat, 14 Nov 2015 15:33:10 +0100 Subject: [PATCH 183/210] Updates `CONTRIBUTING.md`. --- CONTRIBUTING.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1dbfb11b..5e47ef6f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ There are multiple ways you can contribute to this project. -The easiest way to contribute is to report possible bugs, request features, [discuss ideas](mailto:krunoslav.zaher@gmail.com?subject=[RxSwift] I have an idea) and share excitement about this project. +The easiest way to contribute is to report possible bugs, request features, [discuss ideas](https://github.com/ReactiveX/RxSwift/issues) and share excitement about this project. You can also make pull requests. @@ -10,10 +10,8 @@ There are some best practices that will be followed during the development of th So what does this mean in practice: -* If you notice a bug in **documentation** only, that could be considered non risky hotfix, so please make a PR to **master** branch -* If you notice a bug in **source code** please make a PR to **develop** branch because otherwise it could get a lot worse :) If needed, hotfix will be created from commit with the fix and applied to master branch after the PR has been merged into develop and tested. -* If you want to make a small contribution (dozen lines of code) that is not a bug fix please make a pull request to **develop** branch. -* If you want to make a big contribution to the project, please [discuss it](mailto:krunoslav.zaher@gmail.com?subject=[RxSwift] I have an idea) first with me so we can make sure project is going in the right direction. All pull requests with **source code** contributions should be targeted to **develop** branch. +* Please target your PR to **develop** branch +* If you want to make a bigger contribution to the project, please [open an issue first](https://github.com/ReactiveX/RxSwift/issues/new) so we can plan that work, discuss the architecture and brainstorm around that idea first. ## Developer's Certificate of Origin 1.1 From a269a42a38b185c6678561415e3ae418e30e0001 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sat, 14 Nov 2015 15:23:36 +0100 Subject: [PATCH 184/210] KVO revamp. --- Rx.xcodeproj/project.pbxproj | 50 ++ .../KVORepresentable+CoreGraphics.swift | 71 ++ RxCocoa/Common/KVORepresentable+Swift.swift | 93 ++ RxCocoa/Common/KVORepresentable.swift | 35 + .../NSObject+Rx+CoreGraphics.swift | 259 +----- .../NSObject+Rx+KVORepresentable.swift | 45 + .../NSObject+Rx+RawRepresentable.swift | 51 ++ RxCocoa/Common/Observables/NSObject+Rx.swift | 9 +- RxExample/RxExample.xcodeproj/project.pbxproj | 20 + RxTests/RxCocoaTests/KVOObservableTests.swift | 796 +++++++++++++++++- 10 files changed, 1176 insertions(+), 253 deletions(-) create mode 100644 RxCocoa/Common/KVORepresentable+CoreGraphics.swift create mode 100644 RxCocoa/Common/KVORepresentable+Swift.swift create mode 100644 RxCocoa/Common/KVORepresentable.swift create mode 100644 RxCocoa/Common/Observables/NSObject+Rx+KVORepresentable.swift create mode 100644 RxCocoa/Common/Observables/NSObject+Rx+RawRepresentable.swift diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index 693caf24..159af3c8 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -399,6 +399,22 @@ C8C3DA101B939767004D233E /* CurrentThreadScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C3DA0E1B939767004D233E /* CurrentThreadScheduler.swift */; }; C8C3DA121B93A3EA004D233E /* AnonymousObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C3DA111B93A3EA004D233E /* AnonymousObservable.swift */; }; C8C3DA131B93A3EA004D233E /* AnonymousObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C3DA111B93A3EA004D233E /* AnonymousObservable.swift */; }; + C8DB967E1BF7496C0084BD53 /* KVORepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB967D1BF7496C0084BD53 /* KVORepresentable.swift */; }; + C8DB967F1BF7496C0084BD53 /* KVORepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB967D1BF7496C0084BD53 /* KVORepresentable.swift */; }; + C8DB96801BF7496C0084BD53 /* KVORepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB967D1BF7496C0084BD53 /* KVORepresentable.swift */; }; + C8DB96811BF7496C0084BD53 /* KVORepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB967D1BF7496C0084BD53 /* KVORepresentable.swift */; }; + C8DB96831BF754C80084BD53 /* NSObject+Rx+KVORepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB96821BF754C80084BD53 /* NSObject+Rx+KVORepresentable.swift */; }; + C8DB96841BF754C80084BD53 /* NSObject+Rx+KVORepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB96821BF754C80084BD53 /* NSObject+Rx+KVORepresentable.swift */; }; + C8DB96851BF754C80084BD53 /* NSObject+Rx+KVORepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB96821BF754C80084BD53 /* NSObject+Rx+KVORepresentable.swift */; }; + C8DB96861BF754C80084BD53 /* NSObject+Rx+KVORepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB96821BF754C80084BD53 /* NSObject+Rx+KVORepresentable.swift */; }; + C8DB96881BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB96871BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift */; }; + C8DB96891BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB96871BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift */; }; + C8DB968A1BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB96871BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift */; }; + C8DB968B1BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB96871BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift */; }; + C8DB968D1BF7595D0084BD53 /* KVORepresentable+Swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB968C1BF7595D0084BD53 /* KVORepresentable+Swift.swift */; }; + C8DB968E1BF7595D0084BD53 /* KVORepresentable+Swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB968C1BF7595D0084BD53 /* KVORepresentable+Swift.swift */; }; + C8DB968F1BF7595D0084BD53 /* KVORepresentable+Swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB968C1BF7595D0084BD53 /* KVORepresentable+Swift.swift */; }; + C8DB96901BF7595D0084BD53 /* KVORepresentable+Swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB968C1BF7595D0084BD53 /* KVORepresentable+Swift.swift */; }; C8F0BF921BBBFB8B001B112F /* Observable+Creation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C981B8A72BE0088E94D /* Observable+Creation.swift */; }; C8F0BF931BBBFB8B001B112F /* ConnectableObservableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C4D1B8A72BE0088E94D /* ConnectableObservableType.swift */; }; C8F0BF941BBBFB8B001B112F /* Just.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C3DA021B9390C4004D233E /* Just.swift */; }; @@ -581,6 +597,10 @@ C8F6A0FF1BEE42DD007DF367 /* AnonymousInvocable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A0FD1BEE42DD007DF367 /* AnonymousInvocable.swift */; }; C8F6A1001BEE42DD007DF367 /* AnonymousInvocable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A0FD1BEE42DD007DF367 /* AnonymousInvocable.swift */; }; C8F6A1011BEE42DD007DF367 /* AnonymousInvocable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A0FD1BEE42DD007DF367 /* AnonymousInvocable.swift */; }; + C8F6A1451BF0B9B1007DF367 /* NSObject+Rx+RawRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A1441BF0B9B1007DF367 /* NSObject+Rx+RawRepresentable.swift */; }; + C8F6A1461BF0B9B2007DF367 /* NSObject+Rx+RawRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A1441BF0B9B1007DF367 /* NSObject+Rx+RawRepresentable.swift */; }; + C8F6A1471BF0B9B2007DF367 /* NSObject+Rx+RawRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A1441BF0B9B1007DF367 /* NSObject+Rx+RawRepresentable.swift */; }; + C8F6A1481BF0B9B2007DF367 /* NSObject+Rx+RawRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F6A1441BF0B9B1007DF367 /* NSObject+Rx+RawRepresentable.swift */; }; CB255BD71BC46A9C00798A4C /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB255BD61BC46A9C00798A4C /* RetryWhen.swift */; }; CB255BD81BC46A9C00798A4C /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB255BD61BC46A9C00798A4C /* RetryWhen.swift */; }; CB255BD91BC46A9C00798A4C /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB255BD61BC46A9C00798A4C /* RetryWhen.swift */; }; @@ -1059,6 +1079,10 @@ C8C3DA0B1B93959F004D233E /* Never.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Never.swift; sourceTree = ""; }; C8C3DA0E1B939767004D233E /* CurrentThreadScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurrentThreadScheduler.swift; sourceTree = ""; }; C8C3DA111B93A3EA004D233E /* AnonymousObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AnonymousObservable.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + C8DB967D1BF7496C0084BD53 /* KVORepresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVORepresentable.swift; sourceTree = ""; }; + C8DB96821BF754C80084BD53 /* NSObject+Rx+KVORepresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Rx+KVORepresentable.swift"; sourceTree = ""; }; + C8DB96871BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "KVORepresentable+CoreGraphics.swift"; sourceTree = ""; }; + C8DB968C1BF7595D0084BD53 /* KVORepresentable+Swift.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "KVORepresentable+Swift.swift"; sourceTree = ""; }; C8F0C0021BBBFB8B001B112F /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8F0C04B1BBBFBB9001B112F /* RxCocoa.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxCocoa.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8F0C0581BBBFBCE001B112F /* RxBlocking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxBlocking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1066,6 +1090,7 @@ C8F6A0F31BEE3395007DF367 /* InvocableType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InvocableType.swift; sourceTree = ""; }; C8F6A0F81BEE33C1007DF367 /* InvocableScheduledItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InvocableScheduledItem.swift; sourceTree = ""; }; C8F6A0FD1BEE42DD007DF367 /* AnonymousInvocable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnonymousInvocable.swift; sourceTree = ""; }; + C8F6A1441BF0B9B1007DF367 /* NSObject+Rx+RawRepresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Rx+RawRepresentable.swift"; sourceTree = ""; }; CB255BD61BC46A9C00798A4C /* RetryWhen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RetryWhen.swift; sourceTree = ""; }; CB30D9E81BF0E3500084C1C0 /* SingleAsync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SingleAsync.swift; sourceTree = ""; }; CB883B3A1BE24355000AC2EE /* Window.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = ""; }; @@ -1410,6 +1435,9 @@ C8093E991B8A732E0088E94D /* Proxies */, C8093E9B1B8A732E0088E94D /* RxCocoa.swift */, C8093E9C1B8A732E0088E94D /* RxTarget.swift */, + C8DB967D1BF7496C0084BD53 /* KVORepresentable.swift */, + C8DB96871BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift */, + C8DB968C1BF7595D0084BD53 /* KVORepresentable+Swift.swift */, ); path = Common; sourceTree = ""; @@ -1421,6 +1449,8 @@ C8093E951B8A732E0088E94D /* NSNotificationCenter+Rx.swift */, C8093E961B8A732E0088E94D /* NSObject+Rx+CoreGraphics.swift */, C8093E971B8A732E0088E94D /* NSObject+Rx.swift */, + C8F6A1441BF0B9B1007DF367 /* NSObject+Rx+RawRepresentable.swift */, + C8DB96821BF754C80084BD53 /* NSObject+Rx+KVORepresentable.swift */, C8093E981B8A732E0088E94D /* NSURLSession+Rx.swift */, ); path = Observables; @@ -2094,6 +2124,8 @@ C80D33981B922FB00014629D /* ControlEvent.swift in Sources */, C8093EF31B8A732E0088E94D /* NSObject+Rx+CoreGraphics.swift in Sources */, C882542A1B8A752B00B02D69 /* UIControl+Rx.swift in Sources */, + C8F6A1451BF0B9B1007DF367 /* NSObject+Rx+RawRepresentable.swift in Sources */, + C8DB967E1BF7496C0084BD53 /* KVORepresentable.swift in Sources */, C88254341B8A752B00B02D69 /* UITableView+Rx.swift in Sources */, C88254161B8A752B00B02D69 /* RxCollectionViewReactiveArrayDataSource.swift in Sources */, C8093EEF1B8A732E0088E94D /* KVOObserver.swift in Sources */, @@ -2108,6 +2140,7 @@ C80D338F1B91EF9E0014629D /* Observable+Bind.swift in Sources */, C88254311B8A752B00B02D69 /* UISegmentedControl+Rx.swift in Sources */, C8093EED1B8A732E0088E94D /* KVOObservable.swift in Sources */, + C8DB968D1BF7595D0084BD53 /* KVORepresentable+Swift.swift in Sources */, C80DDEB11BCE8CA3006A1832 /* Driver+Operators+arity.swift in Sources */, C88254281B8A752B00B02D69 /* UIButton+Rx.swift in Sources */, C8093EDF1B8A732E0088E94D /* CLLocationManager+Rx.swift in Sources */, @@ -2128,6 +2161,7 @@ C8093EE11B8A732E0088E94D /* DelegateProxy.swift in Sources */, C8093EF91B8A732E0088E94D /* RxCLLocationManagerDelegateProxy.swift in Sources */, C88254331B8A752B00B02D69 /* UISwitch+Rx.swift in Sources */, + C8DB96831BF754C80084BD53 /* NSObject+Rx+KVORepresentable.swift in Sources */, C8093EE51B8A732E0088E94D /* Logging.swift in Sources */, C88254291B8A752B00B02D69 /* UICollectionView+Rx.swift in Sources */, C882541A1B8A752B00B02D69 /* RxCollectionViewDataSourceType.swift in Sources */, @@ -2137,6 +2171,7 @@ C8093EE71B8A732E0088E94D /* ControlTarget.swift in Sources */, C88254301B8A752B00B02D69 /* UISearchBar+Rx.swift in Sources */, C88254181B8A752B00B02D69 /* ItemEvents.swift in Sources */, + C8DB96881BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift in Sources */, C882541B1B8A752B00B02D69 /* RxTableViewDataSourceType.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2147,21 +2182,26 @@ files = ( C80DDEA41BCE69BA006A1832 /* Driver.swift in Sources */, C8093F4A1B8A732E0088E94D /* NSImageView+Rx.swift in Sources */, + C8DB96891BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift in Sources */, C8093EDA1B8A732E0088E94D /* _RXKVOObserver.m in Sources */, C8093EE41B8A732E0088E94D /* DelegateProxyType.swift in Sources */, C8093F481B8A732E0088E94D /* NSControl+Rx.swift in Sources */, C8093F4E1B8A732E0088E94D /* NSTextField+Rx.swift in Sources */, + C8DB967F1BF7496C0084BD53 /* KVORepresentable.swift in Sources */, C8093EFE1B8A732E0088E94D /* RxTarget.swift in Sources */, C8093ED21B8A732E0088E94D /* _RX.m in Sources */, C8093EFC1B8A732E0088E94D /* RxCocoa.swift in Sources */, + C8DB968E1BF7595D0084BD53 /* KVORepresentable+Swift.swift in Sources */, C80D33991B922FB00014629D /* ControlEvent.swift in Sources */, C80DDEA81BCE69BA006A1832 /* ObservableConvertibleType+Driver.swift in Sources */, C80D339B1B922FB00014629D /* ControlProperty.swift in Sources */, C8093EF41B8A732E0088E94D /* NSObject+Rx+CoreGraphics.swift in Sources */, C8093EF01B8A732E0088E94D /* KVOObserver.swift in Sources */, C8093EEE1B8A732E0088E94D /* KVOObservable.swift in Sources */, + C8F6A1461BF0B9B2007DF367 /* NSObject+Rx+RawRepresentable.swift in Sources */, C8093EE01B8A732E0088E94D /* CLLocationManager+Rx.swift in Sources */, C8093EEC1B8A732E0088E94D /* DeinitAction.swift in Sources */, + C8DB96841BF754C80084BD53 /* NSObject+Rx+KVORepresentable.swift in Sources */, C8093F461B8A732E0088E94D /* NSButton+Rx.swift in Sources */, C80DDEA01BCE69BA006A1832 /* Driver+Subscription.swift in Sources */, C8093ED61B8A732E0088E94D /* _RXDelegateProxy.m in Sources */, @@ -2642,6 +2682,8 @@ C8F0C0131BBBFBB9001B112F /* ControlEvent.swift in Sources */, C8F0C0141BBBFBB9001B112F /* NSObject+Rx+CoreGraphics.swift in Sources */, C8F0C0151BBBFBB9001B112F /* UIControl+Rx.swift in Sources */, + C8F6A1481BF0B9B2007DF367 /* NSObject+Rx+RawRepresentable.swift in Sources */, + C8DB96811BF7496C0084BD53 /* KVORepresentable.swift in Sources */, C8F0C0161BBBFBB9001B112F /* UITableView+Rx.swift in Sources */, C8F0C0171BBBFBB9001B112F /* RxCollectionViewReactiveArrayDataSource.swift in Sources */, C8F0C0181BBBFBB9001B112F /* KVOObserver.swift in Sources */, @@ -2656,6 +2698,7 @@ C8F0C01F1BBBFBB9001B112F /* Observable+Bind.swift in Sources */, C8F0C0201BBBFBB9001B112F /* UISegmentedControl+Rx.swift in Sources */, C8F0C0211BBBFBB9001B112F /* KVOObservable.swift in Sources */, + C8DB96901BF7595D0084BD53 /* KVORepresentable+Swift.swift in Sources */, C80DDEB41BCE8CA3006A1832 /* Driver+Operators+arity.swift in Sources */, C8F0C0221BBBFBB9001B112F /* UIButton+Rx.swift in Sources */, C8F0C0231BBBFBB9001B112F /* CLLocationManager+Rx.swift in Sources */, @@ -2676,6 +2719,7 @@ C8F0C0311BBBFBB9001B112F /* DelegateProxy.swift in Sources */, C8F0C0321BBBFBB9001B112F /* RxCLLocationManagerDelegateProxy.swift in Sources */, C8F0C0331BBBFBB9001B112F /* UISwitch+Rx.swift in Sources */, + C8DB96861BF754C80084BD53 /* NSObject+Rx+KVORepresentable.swift in Sources */, C8F0C0341BBBFBB9001B112F /* Logging.swift in Sources */, C8F0C0351BBBFBB9001B112F /* UICollectionView+Rx.swift in Sources */, C8F0C0361BBBFBB9001B112F /* RxCollectionViewDataSourceType.swift in Sources */, @@ -2685,6 +2729,7 @@ C8F0C03A1BBBFBB9001B112F /* ControlTarget.swift in Sources */, C8F0C03B1BBBFBB9001B112F /* UISearchBar+Rx.swift in Sources */, C8F0C03C1BBBFBB9001B112F /* ItemEvents.swift in Sources */, + C8DB968B1BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift in Sources */, C8F0C03D1BBBFBB9001B112F /* RxTableViewDataSourceType.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2723,6 +2768,8 @@ D203C5101BB9C53E00D02D00 /* UISwitch+Rx.swift in Sources */, D203C5121BB9C53E00D02D00 /* UITextField+Rx.swift in Sources */, D203C4F91BB9C53700D02D00 /* RxAlertViewDelegateProxy.swift in Sources */, + C8F6A1471BF0B9B2007DF367 /* NSObject+Rx+RawRepresentable.swift in Sources */, + C8DB96801BF7496C0084BD53 /* KVORepresentable.swift in Sources */, D203C4F31BB9C4CA00D02D00 /* RxCollectionViewReactiveArrayDataSource.swift in Sources */, D203C50B1BB9C53E00D02D00 /* UIScrollView+Rx.swift in Sources */, D203C50C1BB9C53E00D02D00 /* UISearchBar+Rx.swift in Sources */, @@ -2737,6 +2784,7 @@ D2138C921BB9BED600339B5C /* KVOObserver.swift in Sources */, D2138C831BB9BEBE00339B5C /* _RXKVOObserver.m in Sources */, C80DDEB31BCE8CA3006A1832 /* Driver+Operators+arity.swift in Sources */, + C8DB968F1BF7595D0084BD53 /* KVORepresentable+Swift.swift in Sources */, D203C5061BB9C53E00D02D00 /* UIControl+Rx.swift in Sources */, D203C5111BB9C53E00D02D00 /* UITableView+Rx.swift in Sources */, C80DDEA51BCE69BA006A1832 /* Driver.swift in Sources */, @@ -2757,6 +2805,7 @@ D2138C7F1BB9BEBE00339B5C /* _RX.m in Sources */, D203C4FE1BB9C53700D02D00 /* RxTableViewDataSourceProxy.swift in Sources */, D203C5001BB9C53700D02D00 /* RxTextViewDelegateProxy.swift in Sources */, + C8DB96851BF754C80084BD53 /* NSObject+Rx+KVORepresentable.swift in Sources */, D203C5091BB9C53E00D02D00 /* UIImageView+Rx.swift in Sources */, D2138C871BB9BEBE00339B5C /* CLLocationManager+Rx.swift in Sources */, D203C4FF1BB9C53700D02D00 /* RxTableViewDelegateProxy.swift in Sources */, @@ -2766,6 +2815,7 @@ D2138C8A1BB9BEBE00339B5C /* Logging.swift in Sources */, D2138C851BB9BEBE00339B5C /* _RXSwizzling.m in Sources */, D203C5011BB9C53E00D02D00 /* UIActionSheet+Rx.swift in Sources */, + C8DB968A1BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift in Sources */, D203C50F1BB9C53E00D02D00 /* UIStepper+Rx.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/RxCocoa/Common/KVORepresentable+CoreGraphics.swift b/RxCocoa/Common/KVORepresentable+CoreGraphics.swift new file mode 100644 index 00000000..19abf65b --- /dev/null +++ b/RxCocoa/Common/KVORepresentable+CoreGraphics.swift @@ -0,0 +1,71 @@ +// +// KVORepresentable+CoreGraphics.swift +// Rx +// +// Created by Krunoslav Zaher on 11/14/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE + import RxSwift +#endif +import CoreGraphics + +#if arch(x86_64) || arch(arm64) +let CGRectType = "{CGRect={CGPoint=dd}{CGSize=dd}}" +let CGSizeType = "{CGSize=dd}" +let CGPointType = "{CGPoint=dd}" +#elseif arch(i386) || arch(arm) +let CGRectType = "{CGRect={CGPoint=ff}{CGSize=ff}}" +let CGSizeType = "{CGSize=ff}" +let CGPointType = "{CGPoint=ff}" +#endif + +extension CGRect : KVORepresentable { + public typealias KVOType = NSValue + + /** + Constructs self from `NSValue`. + */ + public init?(KVOValue: KVOType) { + if strcmp(KVOValue.objCType, CGRectType) != 0 { + return nil + } + var typedValue = CGRect(x: 0, y: 0, width: 0, height: 0) + KVOValue.getValue(&typedValue) + self = typedValue + } +} + +extension CGPoint : KVORepresentable { + public typealias KVOType = NSValue + + /** + Constructs self from `NSValue`. + */ + public init?(KVOValue: KVOType) { + if strcmp(KVOValue.objCType, CGPointType) != 0 { + return nil + } + var typedValue = CGPoint(x: 0, y: 0) + KVOValue.getValue(&typedValue) + self = typedValue + } +} + +extension CGSize : KVORepresentable { + public typealias KVOType = NSValue + + /** + Constructs self from `NSValue`. + */ + public init?(KVOValue: KVOType) { + if strcmp(KVOValue.objCType, CGSizeType) != 0 { + return nil + } + var typedValue = CGSize(width: 0, height: 0) + KVOValue.getValue(&typedValue) + self = typedValue + } +} \ No newline at end of file diff --git a/RxCocoa/Common/KVORepresentable+Swift.swift b/RxCocoa/Common/KVORepresentable+Swift.swift new file mode 100644 index 00000000..a118bc4b --- /dev/null +++ b/RxCocoa/Common/KVORepresentable+Swift.swift @@ -0,0 +1,93 @@ +// +// KVORepresentable+Swift.swift +// Rx +// +// Created by Krunoslav Zaher on 11/14/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +extension Int : KVORepresentable { + public typealias KVOType = NSNumber + + /** + Constructs `Self` using KVO value. + */ + public init?(KVOValue: KVOType) { + self.init(KVOValue.integerValue) + } +} + +extension Int32 : KVORepresentable { + public typealias KVOType = NSNumber + + /** + Constructs `Self` using KVO value. + */ + public init?(KVOValue: KVOType) { + self.init(KVOValue.intValue) + } +} + +extension Int64 : KVORepresentable { + public typealias KVOType = NSNumber + + /** + Constructs `Self` using KVO value. + */ + public init?(KVOValue: KVOType) { + self.init(KVOValue.longLongValue) + } +} + +extension UInt : KVORepresentable { + public typealias KVOType = NSNumber + + /** + Constructs `Self` using KVO value. + */ + public init?(KVOValue: KVOType) { + self.init(KVOValue.unsignedLongValue) + } +} + +extension UInt32 : KVORepresentable { + public typealias KVOType = NSNumber + + /** + Constructs `Self` using KVO value. + */ + public init?(KVOValue: KVOType) { + self.init(KVOValue.unsignedIntValue) + } +} + +extension UInt64 : KVORepresentable { + public typealias KVOType = NSNumber + + /** + Constructs `Self` using KVO value. + */ + public init?(KVOValue: KVOType) { + self.init(KVOValue.unsignedLongLongValue) + } +} + + +extension RawRepresentable where RawValue: KVORepresentable { + /** + Constructs `Self` using optional KVO value. + */ + init?(KVOValue: RawValue.KVOType?) { + guard let KVOValue = KVOValue else { + return nil + } + + guard let rawValue = RawValue(KVOValue: KVOValue) else { + return nil + } + + self.init(rawValue: rawValue) + } +} \ No newline at end of file diff --git a/RxCocoa/Common/KVORepresentable.swift b/RxCocoa/Common/KVORepresentable.swift new file mode 100644 index 00000000..481b06ee --- /dev/null +++ b/RxCocoa/Common/KVORepresentable.swift @@ -0,0 +1,35 @@ +// +// KVORepresentable.swift +// Rx +// +// Created by Krunoslav Zaher on 11/14/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +/** +Type that is KVO representable (KVO mechanism can be used to observe it). +*/ +public protocol KVORepresentable { + /** + Associated KVO type. + */ + typealias KVOType + + /** + Constructs `Self` using KVO value. + */ + init?(KVOValue: KVOType) +} + +extension KVORepresentable { + init?(KVOValue: KVOType?) { + guard let KVOValue = KVOValue else { + return nil + } + + self.init(KVOValue: KVOValue) + } +} + diff --git a/RxCocoa/Common/Observables/NSObject+Rx+CoreGraphics.swift b/RxCocoa/Common/Observables/NSObject+Rx+CoreGraphics.swift index dc22a2fe..435d11e4 100644 --- a/RxCocoa/Common/Observables/NSObject+Rx+CoreGraphics.swift +++ b/RxCocoa/Common/Observables/NSObject+Rx+CoreGraphics.swift @@ -12,190 +12,19 @@ import RxSwift #endif import CoreGraphics -#if arch(x86_64) || arch(arm64) -let CGRectType = "{CGRect={CGPoint=dd}{CGSize=dd}}" -let CGSizeType = "{CGSize=dd}" -let CGPointType = "{CGPoint=dd}" -#elseif arch(i386) || arch(arm) -let CGRectType = "{CGRect={CGPoint=ff}{CGSize=ff}}" -let CGSizeType = "{CGSize=ff}" -let CGPointType = "{CGPoint=ff}" -#endif - -extension NSObject { - /** - Specialization of generic `rx_observe` method. - - For more information take a look at `rx_observe` method. - */ - @warn_unused_result(message="http://git.io/rxs.uo") - public func rx_observe(type: CGRect.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { - return rx_observe(keyPath, options: options, retainSelf: retainSelf) - .map { (value: NSValue?) in - if let value = value { - if strcmp(value.objCType, CGRectType) != 0 { - return nil - } - var typedValue = CGRect(x: 0, y: 0, width: 0, height: 0) - value.getValue(&typedValue) - return typedValue - } - else { - return nil - } - } - } - - /** - Specialization of generic `rx_observe` method. - - For more information take a look at `rx_observe` method. - */ - @warn_unused_result(message="http://git.io/rxs.uo") - public func rx_observe(type: CGSize.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { - return rx_observe(keyPath, options: options, retainSelf: retainSelf) - .map { (value: NSValue?) in - if let value = value { - if strcmp(value.objCType, CGSizeType) != 0 { - return nil - } - var typedValue = CGSize(width: 0, height: 0) - value.getValue(&typedValue) - return typedValue - } - else { - return nil - } - } - } - - /** - Specialization of generic `rx_observe` method. - - For more information take a look at `rx_observe` method. - */ - @warn_unused_result(message="http://git.io/rxs.uo") - public func rx_observe(type: CGPoint.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { - return rx_observe(keyPath, options: options, retainSelf: retainSelf) - .map { (value: NSValue?) in - if let value = value { - if strcmp(value.objCType, CGPointType) != 0 { - return nil - } - var typedValue = CGPoint(x: 0, y: 0) - value.getValue(&typedValue) - return typedValue - } - else { - return nil - } - } - } -} - -#if !DISABLE_SWIZZLING - - // rx_observeWeakly + CoreGraphics - extension NSObject { - - /** - Specialization of generic `rx_observeWeakly` method. - - For more information take a look at `rx_observeWeakly` method. - */ - @warn_unused_result(message="http://git.io/rxs.uo") - public func rx_observeWeakly(type: CGRect.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { - return rx_observeWeakly(keyPath, options: options) - .map { (value: NSValue?) in - if let value = value { - if strcmp(value.objCType, CGRectType) != 0 { - return nil - } - var typedValue = CGRect(x: 0, y: 0, width: 0, height: 0) - value.getValue(&typedValue) - return typedValue - } - else { - return nil - } - } - } - - /** - Specialization of generic `rx_observeWeakly` method. - - For more information take a look at `rx_observeWeakly` method. - */ - @warn_unused_result(message="http://git.io/rxs.uo") - public func rx_observeWeakly(type: CGSize.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { - return rx_observeWeakly(keyPath, options: options) - .map { (value: NSValue?) in - if let value = value { - if strcmp(value.objCType, CGSizeType) != 0 { - return nil - } - var typedValue = CGSize(width: 0, height: 0) - value.getValue(&typedValue) - return typedValue - } - else { - return nil - } - } - } - - /** - Specialization of generic `rx_observeWeakly` method. - - For more information take a look at `rx_observeWeakly` method. - */ - @warn_unused_result(message="http://git.io/rxs.uo") - public func rx_observeWeakly(type: CGPoint.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { - return rx_observeWeakly(keyPath, options: options) - .map { (value: NSValue?) in - if let value = value { - if strcmp(value.objCType, CGPointType) != 0 { - return nil - } - var typedValue = CGPoint(x: 0, y: 0) - value.getValue(&typedValue) - return typedValue - } - else { - return nil - } - } - } - } - -#endif - - -// MARK: Deprecated +// MARK: Deprecated, CGPoint, CGRect, CGSize are now KVORepresentable extension NSObject { /** Specialization of generic `rx_observe` method. - + For more information take a look at `rx_observe` method. */ @warn_unused_result(message="http://git.io/rxs.uo") @available(*, deprecated=2.0.0, message="Please use version that takes type as first argument.") public func rx_observe(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { - return rx_observe(keyPath, options: options, retainSelf: retainSelf) - .map { (value: NSValue?) in - if let value = value { - if strcmp(value.objCType, CGRectType) != 0 { - return nil - } - var typedValue = CGRect(x: 0, y: 0, width: 0, height: 0) - value.getValue(&typedValue) - return typedValue - } - else { - return nil - } - } + return rx_observe(NSValue.self, keyPath, options: options, retainSelf: retainSelf) + .map(CGRect.init) } /** @@ -206,20 +35,8 @@ extension NSObject { @warn_unused_result(message="http://git.io/rxs.uo") @available(*, deprecated=2.0.0, message="Please use version that takes type as first argument.") public func rx_observe(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { - return rx_observe(keyPath, options: options, retainSelf: retainSelf) - .map { (value: NSValue?) in - if let value = value { - if strcmp(value.objCType, CGSizeType) != 0 { - return nil - } - var typedValue = CGSize(width: 0, height: 0) - value.getValue(&typedValue) - return typedValue - } - else { - return nil - } - } + return rx_observe(NSValue.self, keyPath, options: options, retainSelf: retainSelf) + .map(CGSize.init) } /** @@ -230,20 +47,8 @@ extension NSObject { @warn_unused_result(message="http://git.io/rxs.uo") @available(*, deprecated=2.0.0, message="Please use version that takes type as first argument.") public func rx_observe(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { - return rx_observe(keyPath, options: options, retainSelf: retainSelf) - .map { (value: NSValue?) in - if let value = value { - if strcmp(value.objCType, CGPointType) != 0 { - return nil - } - var typedValue = CGPoint(x: 0, y: 0) - value.getValue(&typedValue) - return typedValue - } - else { - return nil - } - } + return rx_observe(NSValue.self, keyPath, options: options, retainSelf: retainSelf) + .map(CGPoint.init) } } @@ -260,20 +65,8 @@ extension NSObject { @warn_unused_result(message="http://git.io/rxs.uo") @available(*, deprecated=2.0.0, message="Please use version that takes type as first argument.") public func rx_observeWeakly(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { - return rx_observeWeakly(keyPath, options: options) - .map { (value: NSValue?) in - if let value = value { - if strcmp(value.objCType, CGRectType) != 0 { - return nil - } - var typedValue = CGRect(x: 0, y: 0, width: 0, height: 0) - value.getValue(&typedValue) - return typedValue - } - else { - return nil - } - } + return rx_observeWeakly(NSValue.self, keyPath, options: options) + .map(CGRect.init) } /** @@ -284,20 +77,8 @@ extension NSObject { @warn_unused_result(message="http://git.io/rxs.uo") @available(*, deprecated=2.0.0, message="Please use version that takes type as first argument.") public func rx_observeWeakly(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { - return rx_observeWeakly(keyPath, options: options) - .map { (value: NSValue?) in - if let value = value { - if strcmp(value.objCType, CGSizeType) != 0 { - return nil - } - var typedValue = CGSize(width: 0, height: 0) - value.getValue(&typedValue) - return typedValue - } - else { - return nil - } - } + return rx_observeWeakly(NSValue.self, keyPath, options: options) + .map(CGSize.init) } /** @@ -308,20 +89,8 @@ extension NSObject { @warn_unused_result(message="http://git.io/rxs.uo") @available(*, deprecated=2.0.0, message="Please use version that takes type as first argument.") public func rx_observeWeakly(keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { - return rx_observeWeakly(keyPath, options: options) - .map { (value: NSValue?) in - if let value = value { - if strcmp(value.objCType, CGPointType) != 0 { - return nil - } - var typedValue = CGPoint(x: 0, y: 0) - value.getValue(&typedValue) - return typedValue - } - else { - return nil - } - } + return rx_observeWeakly(NSValue.self, keyPath, options: options) + .map(CGPoint.init) } } diff --git a/RxCocoa/Common/Observables/NSObject+Rx+KVORepresentable.swift b/RxCocoa/Common/Observables/NSObject+Rx+KVORepresentable.swift new file mode 100644 index 00000000..91ac2894 --- /dev/null +++ b/RxCocoa/Common/Observables/NSObject+Rx+KVORepresentable.swift @@ -0,0 +1,45 @@ +// +// NSObject+Rx+KVORepresentable.swift +// Rx +// +// Created by Krunoslav Zaher on 11/14/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE + import RxSwift +#endif + +extension NSObject { + + /** + Specialization of generic `rx_observe` method. + + This is a special overload because to observe values of some type (for example `Int`), first values of KVO type + need to be observed (`NSNumber`), and then converted to result type. + + For more information take a look at `rx_observe` method. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func rx_observe(type: E.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { + return rx_observe(E.KVOType.self, keyPath, options: options, retainSelf: retainSelf) + .map(E.init) + } +} + +#if !DISABLE_SWIZZLING + // KVO + extension NSObject { + /** + Specialization of generic `rx_observeWeakly` method. + + For more information take a look at `rx_observeWeakly` method. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func rx_observeWeakly(type: E.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { + return rx_observeWeakly(E.KVOType.self, keyPath, options: options) + .map(E.init) + } + } +#endif diff --git a/RxCocoa/Common/Observables/NSObject+Rx+RawRepresentable.swift b/RxCocoa/Common/Observables/NSObject+Rx+RawRepresentable.swift new file mode 100644 index 00000000..7ff37f45 --- /dev/null +++ b/RxCocoa/Common/Observables/NSObject+Rx+RawRepresentable.swift @@ -0,0 +1,51 @@ +// +// NSObject+Rx+RawRepresentable.swift +// Rx +// +// Created by Krunoslav Zaher on 11/9/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE + import RxSwift +#endif + +extension NSObject { + /** + Specialization of generic `rx_observe` method. + + This specialization first observes `KVORepresentable` value and then converts it to `RawRepresentable` value. + + It is useful for observing bridged ObjC enum values. + + For more information take a look at `rx_observe` method. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func rx_observe(type: E.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { + return rx_observe(E.RawValue.KVOType.self, keyPath, options: options, retainSelf: retainSelf) + .map(E.init) + } +} + +#if !DISABLE_SWIZZLING + + // rx_observeWeakly + RawRepresentable + extension NSObject { + + /** + Specialization of generic `rx_observeWeakly` method. + + This specialization first observes `KVORepresentable` value and then converts it to `RawRepresentable` value. + + It is useful for observing bridged ObjC enum values. + + For more information take a look at `rx_observeWeakly` method. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func rx_observeWeakly(type: E.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { + return rx_observeWeakly(E.RawValue.KVOType.self, keyPath, options: options) + .map(E.init) + } + } +#endif diff --git a/RxCocoa/Common/Observables/NSObject+Rx.swift b/RxCocoa/Common/Observables/NSObject+Rx.swift index c274f1b0..8629066a 100644 --- a/RxCocoa/Common/Observables/NSObject+Rx.swift +++ b/RxCocoa/Common/Observables/NSObject+Rx.swift @@ -59,10 +59,11 @@ extension NSObject { - returns: Observable sequence of objects on `keyPath`. */ @warn_unused_result(message="http://git.io/rxs.uo") - public func rx_observe(type: Element.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { + public func rx_observe(type: E.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial], retainSelf: Bool = true) -> Observable { return KVOObservable(object: self, keyPath: keyPath, options: options, retainTarget: retainSelf).asObservable() } + /** Observes values on `keyPath` starting from `self` with `options` and retains `self` if `retainSelf` is set. @@ -106,11 +107,11 @@ extension NSObject { - returns: Observable sequence of objects on `keyPath`. */ @warn_unused_result(message="http://git.io/rxs.uo") - public func rx_observeWeakly(type: Element.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { + public func rx_observeWeakly(type: E.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { return observeWeaklyKeyPathFor(self, keyPath: keyPath, options: options) .map { n in - return n as? Element - } + return n as? E + } } /** diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index bd9b237f..446e8cc4 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -106,6 +106,11 @@ C83367231AD029AE00C668A7 /* Example.swift in Sources */ = {isa = PBXBuildFile; fileRef = C833670F1AD029AE00C668A7 /* Example.swift */; }; C83367241AD029AE00C668A7 /* HtmlParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83367111AD029AE00C668A7 /* HtmlParsing.swift */; }; C83367251AD029AE00C668A7 /* ImageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83367121AD029AE00C668A7 /* ImageService.swift */; }; + C83974121BF77406004F02CC /* KVORepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C839740F1BF77406004F02CC /* KVORepresentable.swift */; }; + C83974131BF77406004F02CC /* KVORepresentable+CoreGraphics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83974101BF77406004F02CC /* KVORepresentable+CoreGraphics.swift */; }; + C83974141BF77406004F02CC /* KVORepresentable+Swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83974111BF77406004F02CC /* KVORepresentable+Swift.swift */; }; + C83974231BF77413004F02CC /* NSObject+Rx+KVORepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83974211BF77413004F02CC /* NSObject+Rx+KVORepresentable.swift */; }; + C83974241BF77413004F02CC /* NSObject+Rx+RawRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83974221BF77413004F02CC /* NSObject+Rx+RawRepresentable.swift */; }; C84B91381B8A282000C9CCCF /* RxTableViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88C78631B3EB0A00061C5AB /* RxTableViewSectionedAnimatedDataSource.swift */; }; C84B91391B8A282000C9CCCF /* RxTableViewSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88C78641B3EB0A00061C5AB /* RxTableViewSectionedDataSource.swift */; }; C84B913A1B8A282000C9CCCF /* RxTableViewSectionedReloadDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88C78651B3EB0A00061C5AB /* RxTableViewSectionedReloadDataSource.swift */; }; @@ -516,6 +521,11 @@ C833670F1AD029AE00C668A7 /* Example.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Example.swift; sourceTree = ""; }; C83367111AD029AE00C668A7 /* HtmlParsing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HtmlParsing.swift; sourceTree = ""; }; C83367121AD029AE00C668A7 /* ImageService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageService.swift; sourceTree = ""; }; + C839740F1BF77406004F02CC /* KVORepresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVORepresentable.swift; sourceTree = ""; }; + C83974101BF77406004F02CC /* KVORepresentable+CoreGraphics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "KVORepresentable+CoreGraphics.swift"; sourceTree = ""; }; + C83974111BF77406004F02CC /* KVORepresentable+Swift.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "KVORepresentable+Swift.swift"; sourceTree = ""; }; + C83974211BF77413004F02CC /* NSObject+Rx+KVORepresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Rx+KVORepresentable.swift"; sourceTree = ""; }; + C83974221BF77413004F02CC /* NSObject+Rx+RawRepresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Rx+RawRepresentable.swift"; sourceTree = ""; }; C84CC52D1BDC344100E06A64 /* ElementAt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElementAt.swift; sourceTree = ""; }; C84CC56B1BDD08F500E06A64 /* LockOwnerType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockOwnerType.swift; sourceTree = ""; }; C84CC56C1BDD08F500E06A64 /* SynchronizedDisposeType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizedDisposeType.swift; sourceTree = ""; }; @@ -1285,6 +1295,9 @@ C894650D1BC6C2BC0055219D /* Common */ = { isa = PBXGroup; children = ( + C839740F1BF77406004F02CC /* KVORepresentable.swift */, + C83974101BF77406004F02CC /* KVORepresentable+CoreGraphics.swift */, + C83974111BF77406004F02CC /* KVORepresentable+Swift.swift */, C894650E1BC6C2BC0055219D /* _RX.h */, C894650F1BC6C2BC0055219D /* _RX.m */, C89465101BC6C2BC0055219D /* _RXDelegateProxy.h */, @@ -1320,6 +1333,8 @@ C89465241BC6C2BC0055219D /* Observables */ = { isa = PBXGroup; children = ( + C83974211BF77413004F02CC /* NSObject+Rx+KVORepresentable.swift */, + C83974221BF77413004F02CC /* NSObject+Rx+RawRepresentable.swift */, C89465251BC6C2BC0055219D /* Implementations */, C894652B1BC6C2BC0055219D /* NSNotificationCenter+Rx.swift */, C894652C1BC6C2BC0055219D /* NSObject+Rx+CoreGraphics.swift */, @@ -1713,6 +1728,7 @@ C89464A51BC6C2B00055219D /* Disposable.swift in Sources */, C89464F91BC6C2B00055219D /* ObserverType+Extensions.swift in Sources */, C84CC58D1BDD486300E06A64 /* SynchronizedOnType.swift in Sources */, + C83974121BF77406004F02CC /* KVORepresentable.swift in Sources */, C89464DC1BC6C2B00055219D /* Scan.swift in Sources */, C89464B21BC6C2B00055219D /* StableCompositeDisposable.swift in Sources */, C89464AE1BC6C2B00055219D /* ScheduledDisposable.swift in Sources */, @@ -1722,6 +1738,7 @@ C894658D1BC6C2BC0055219D /* UIActionSheet+Rx.swift in Sources */, C89464EA1BC6C2B00055219D /* Zip.swift in Sources */, C89464E51BC6C2B00055219D /* Throttle.swift in Sources */, + C83974241BF77413004F02CC /* NSObject+Rx+RawRepresentable.swift in Sources */, C89465831BC6C2BC0055219D /* RxTableViewDataSourceType.swift in Sources */, C80DDE881BCDAA0F006A1832 /* SkipWhile.swift in Sources */, C89464ED1BC6C2B00055219D /* Observable+Concurrency.swift in Sources */, @@ -1766,6 +1783,7 @@ C89464B71BC6C2B00055219D /* Observable+Extensions.swift in Sources */, C89464A01BC6C2B00055219D /* Lock.swift in Sources */, CB883B601BE3AC72000AC2EE /* Window.swift in Sources */, + C83974141BF77406004F02CC /* KVORepresentable+Swift.swift in Sources */, C89464C91BC6C2B00055219D /* Do.swift in Sources */, C89464A41BC6C2B00055219D /* Queue.swift in Sources */, C89464B91BC6C2B00055219D /* ObservableConvertibleType.swift in Sources */, @@ -1809,6 +1827,7 @@ C809E97E1BE697100058D948 /* UIImage+Extensions.swift in Sources */, C89464E61BC6C2B00055219D /* Timer.swift in Sources */, C8297E3E1B6CF905000589EA /* DetailViewController.swift in Sources */, + C83974131BF77406004F02CC /* KVORepresentable+CoreGraphics.swift in Sources */, C8297E3F1B6CF905000589EA /* SectionModelType.swift in Sources */, C8297E401B6CF905000589EA /* ImageService.swift in Sources */, C89464AD1BC6C2B00055219D /* NopDisposable.swift in Sources */, @@ -1842,6 +1861,7 @@ C8297E451B6CF905000589EA /* SectionedViewType.swift in Sources */, C89464D51BC6C2B00055219D /* ObserveOnSerialDispatchQueue.swift in Sources */, C894658E1BC6C2BC0055219D /* UIAlertView+Rx.swift in Sources */, + C83974231BF77413004F02CC /* NSObject+Rx+KVORepresentable.swift in Sources */, C8297E461B6CF905000589EA /* Example.swift in Sources */, C89465081BC6C2B00055219D /* PublishSubject.swift in Sources */, C89464FC1BC6C2B00055219D /* RxBox.swift in Sources */, diff --git a/RxTests/RxCocoaTests/KVOObservableTests.swift b/RxTests/RxCocoaTests/KVOObservableTests.swift index 2273094b..353ce0a6 100644 --- a/RxTests/RxCocoaTests/KVOObservableTests.swift +++ b/RxTests/RxCocoaTests/KVOObservableTests.swift @@ -28,7 +28,7 @@ class Parent : NSObject { var disposeBag: DisposeBag! = DisposeBag() dynamic var val: String = "" - + init(callback: String? -> Void) { super.init() @@ -68,13 +68,51 @@ class ParentWithChild : NSObject { } } +@objc enum IntEnum: Int { + typealias RawValue = Int + case One + case Two +} + +@objc enum UIntEnum: UInt { + case One + case Two +} + +@objc enum Int32Enum: Int32 { + case One + case Two +} + +@objc enum UInt32Enum: UInt32 { + case One + case Two +} + +@objc enum Int64Enum: Int64 { + case One + case Two +} + +@objc enum UInt64Enum: UInt64 { + case One + case Two +} + class HasStrongProperty : NSObject { dynamic var property: NSObject? = nil dynamic var frame: CGRect dynamic var point: CGPoint dynamic var size: CGSize + dynamic var intEnum: IntEnum = .One + dynamic var uintEnum: UIntEnum = .One + dynamic var int32Enum: Int32Enum = .One + dynamic var uint32Enum: UInt32Enum = .One + dynamic var int64Enum: Int64Enum = .One + dynamic var uint64Enum: UInt64Enum = .One dynamic var integer: Int + dynamic var uinteger: UInt override init() { self.frame = CGRect(x: 0, y: 0, width: 100, height: 100) @@ -82,6 +120,7 @@ class HasStrongProperty : NSObject { self.size = CGSizeMake(1, 2) self.integer = 1 + self.uinteger = 1 super.init() } } @@ -647,8 +686,8 @@ extension KVOObservableTests { XCTAssertTrue(latest.value == nil) - let disposable = root.rx_observe("frame") - .subscribeNext { (n: NSRect?) in + let disposable = root.rx_observe(NSRect.self, "frame") + .subscribeNext { n in latest.value = n } XCTAssertTrue(latest.value == root.frame) @@ -948,7 +987,7 @@ extension KVOObservableTests { XCTAssertTrue(latest.value == nil) XCTAssertTrue(rootDeallocated) } - + func testObserveWeak_PropertyDoesntExist() { var root: HasStrongProperty! = HasStrongProperty() @@ -1005,3 +1044,752 @@ extension KVOObservableTests { } } #endif + +// MARK: KVORepresentable + +extension KVOObservableTests { + func testObserve_ObserveIntegerRepresentable() { + var root: HasStrongProperty! = HasStrongProperty() + + var latest: Int? + + XCTAssertTrue(latest == nil) + + let disposable = root.rx_observe(Int.self, "integer") + .subscribeNext { n in + latest = n + } + XCTAssertTrue(latest == 1) + + root.integer = 2 + + XCTAssertTrue(latest == 2) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest == 2) + XCTAssertTrue(!rootDeallocated) + + disposable.dispose() + } + + func testObserve_ObserveUIntegerRepresentable() { + var root: HasStrongProperty! = HasStrongProperty() + + var latest: UInt? + + XCTAssertTrue(latest == nil) + + let disposable = root.rx_observe(UInt.self, "uinteger") + .subscribeNext { n in + latest = n + } + XCTAssertTrue(latest == 1) + + root.uinteger = 2 + + XCTAssertTrue(latest == 2) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest == 2) + XCTAssertTrue(!rootDeallocated) + + disposable.dispose() + } +} + +#if !DISABLE_SWIZZLING + extension KVOObservableTests { + func testObserveWeak_ObserveIntegerRepresentable() { + var root: HasStrongProperty! = HasStrongProperty() + + var latest: Int? + + XCTAssertTrue(latest == nil) + + _ = root + .rx_observeWeakly(Int.self, "integer") + .subscribeNext { n in + latest = n + } + + XCTAssertTrue(latest == 1) + + root.integer = 2 + + XCTAssertTrue(latest == 2) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest == nil) + XCTAssertTrue(rootDeallocated) + } + + func testObserveWeak_ObserveUIntegerRepresentable() { + var root: HasStrongProperty! = HasStrongProperty() + + var latest: UInt? + + XCTAssertTrue(latest == nil) + + _ = root + .rx_observeWeakly(UInt.self, "uinteger") + .subscribeNext { n in + latest = n + } + + XCTAssertTrue(latest == 1) + + root.uinteger = 2 + + XCTAssertTrue(latest == 2) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest == nil) + XCTAssertTrue(rootDeallocated) + } + } +#endif + +// MARK: RawRepresentable +extension KVOObservableTests { + func testObserve_ObserveIntEnum() { + var root: HasStrongProperty! = HasStrongProperty() + + var latest: IntEnum? + + XCTAssertTrue(latest == nil) + + let disposable = root.rx_observe(IntEnum.self, "intEnum") + .subscribeNext { n in + latest = n + } + XCTAssertTrue(latest == .One) + + root.intEnum = .Two + + XCTAssertTrue(latest == .Two) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest == .Two) + XCTAssertTrue(!rootDeallocated) + + disposable.dispose() + } + + func testObserve_ObserveInt32Enum() { + var root: HasStrongProperty! = HasStrongProperty() + + var latest: Int32Enum? + + XCTAssertTrue(latest == nil) + + let disposable = root.rx_observe(Int32Enum.self, "int32Enum") + .subscribeNext { n in + latest = n + } + XCTAssertTrue(latest == .One) + + root.int32Enum = .Two + + XCTAssertTrue(latest == .Two) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest == .Two) + XCTAssertTrue(!rootDeallocated) + + disposable.dispose() + } + + func testObserve_ObserveInt64Enum() { + var root: HasStrongProperty! = HasStrongProperty() + + var latest: Int64Enum? + + XCTAssertTrue(latest == nil) + + let disposable = root.rx_observe(Int64Enum.self, "int64Enum") + .subscribeNext { n in + latest = n + } + XCTAssertTrue(latest == .One) + + root.int64Enum = .Two + + XCTAssertTrue(latest == .Two) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest == .Two) + XCTAssertTrue(!rootDeallocated) + + disposable.dispose() + } + + + func testObserve_ObserveUIntEnum() { + var root: HasStrongProperty! = HasStrongProperty() + + var latest: UIntEnum? + + XCTAssertTrue(latest == nil) + + let disposable = root.rx_observe(UIntEnum.self, "uintEnum") + .subscribeNext { n in + latest = n + } + XCTAssertTrue(latest == .One) + + root.uintEnum = .Two + + XCTAssertTrue(latest == .Two) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest == .Two) + XCTAssertTrue(!rootDeallocated) + + disposable.dispose() + } + + func testObserve_ObserveUInt32Enum() { + var root: HasStrongProperty! = HasStrongProperty() + + var latest: UInt32Enum? + + XCTAssertTrue(latest == nil) + + let disposable = root.rx_observe(UInt32Enum.self, "uint32Enum") + .subscribeNext { n in + latest = n + } + XCTAssertTrue(latest == .One) + + root.uint32Enum = .Two + + XCTAssertTrue(latest == .Two) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest == .Two) + XCTAssertTrue(!rootDeallocated) + + disposable.dispose() + } + + func testObserve_ObserveUInt64Enum() { + var root: HasStrongProperty! = HasStrongProperty() + + var latest: UInt64Enum? + + XCTAssertTrue(latest == nil) + + let disposable = root.rx_observe(UInt64Enum.self, "uint64Enum") + .subscribeNext { n in + latest = n + } + XCTAssertTrue(latest == .One) + + root.uint64Enum = .Two + + XCTAssertTrue(latest == .Two) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest == .Two) + XCTAssertTrue(!rootDeallocated) + + disposable.dispose() + } +} + +#if !DISABLE_SWIZZLING +extension KVOObservableTests { + func testObserveWeak_ObserveIntEnum() { + var root: HasStrongProperty! = HasStrongProperty() + + var latest: IntEnum? + + XCTAssertTrue(latest == nil) + + _ = root + .rx_observeWeakly(IntEnum.self, "intEnum") + .subscribeNext { n in + latest = n + } + XCTAssertTrue(latest == .One) + + root.intEnum = .Two + + XCTAssertTrue(latest == .Two) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest == nil) + XCTAssertTrue(rootDeallocated) + } + + func testObserveWeak_ObserveInt32Enum() { + var root: HasStrongProperty! = HasStrongProperty() + + var latest: Int32Enum? + + XCTAssertTrue(latest == nil) + + _ = root + .rx_observeWeakly(Int32Enum.self, "int32Enum") + .subscribeNext { n in + latest = n + } + XCTAssertTrue(latest == .One) + + root.int32Enum = .Two + + XCTAssertTrue(latest == .Two) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest == nil) + XCTAssertTrue(rootDeallocated) + } + + func testObserveWeak_ObserveInt64Enum() { + var root: HasStrongProperty! = HasStrongProperty() + + var latest: Int64Enum? + + XCTAssertTrue(latest == nil) + + _ = root + .rx_observeWeakly(Int64Enum.self, "int64Enum") + .subscribeNext { n in + latest = n + } + XCTAssertTrue(latest == .One) + + root.int64Enum = .Two + + XCTAssertTrue(latest == .Two) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest == nil) + XCTAssertTrue(rootDeallocated) + } + + func testObserveWeak_ObserveUIntEnum() { + var root: HasStrongProperty! = HasStrongProperty() + + var latest: UIntEnum? + + XCTAssertTrue(latest == nil) + + _ = root + .rx_observeWeakly(UIntEnum.self, "uintEnum") + .subscribeNext { n in + latest = n + } + XCTAssertTrue(latest == .One) + + root.uintEnum = .Two + + XCTAssertTrue(latest == .Two) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest == nil) + XCTAssertTrue(rootDeallocated) + } + + func testObserveWeak_ObserveUInt32Enum() { + var root: HasStrongProperty! = HasStrongProperty() + + var latest: UInt32Enum? + + XCTAssertTrue(latest == nil) + + _ = root + .rx_observeWeakly(UInt32Enum.self, "uint32Enum") + .subscribeNext { n in + latest = n + } + XCTAssertTrue(latest == .One) + + root.uint32Enum = .Two + + XCTAssertTrue(latest == .Two) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest == nil) + XCTAssertTrue(rootDeallocated) + } + + func testObserveWeak_ObserveUInt64Enum() { + var root: HasStrongProperty! = HasStrongProperty() + + var latest: UInt32Enum? + + XCTAssertTrue(latest == nil) + + _ = root + .rx_observeWeakly(UInt32Enum.self, "uint64Enum") + .subscribeNext { n in + latest = n + } + XCTAssertTrue(latest == .One) + + root.uint64Enum = .Two + + XCTAssertTrue(latest == .Two) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest == nil) + XCTAssertTrue(rootDeallocated) + } +} +#endif + +// MARK: Deprecated +extension KVOObservableTests { + func testObserve_deprecated_ObserveCGRect() { + var root: HasStrongProperty! = HasStrongProperty() + + let latest = RxMutableBox(nil) + + XCTAssertTrue(latest.value == nil) + + let d = root.rx_observe("frame") + .subscribeNext { (n: CGRect?) in + latest.value = n + } + + defer { + d.dispose() + } + + XCTAssertTrue(latest.value == root.frame) + + root.frame = CGRectMake(-2, 0, 0, 1) + + XCTAssertTrue(latest.value == CGRectMake(-2, 0, 0, 1)) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest.value == CGRectMake(-2, 0, 0, 1)) + XCTAssertTrue(!rootDeallocated) + } + + func testObserve_deprecated_ObserveCGSize() { + var root: HasStrongProperty! = HasStrongProperty() + + let latest = RxMutableBox(nil) + + XCTAssertTrue(latest.value == nil) + + let d = root.rx_observe("size") + .subscribeNext { (n: CGSize?) in + latest.value = n + } + + defer { + d.dispose() + } + + XCTAssertTrue(latest.value == root.size) + + root.size = CGSizeMake(56, 1) + + XCTAssertTrue(latest.value == CGSizeMake(56, 1)) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest.value == CGSizeMake(56, 1)) + XCTAssertTrue(!rootDeallocated) + } + + func testObserve_deprecated_ObserveCGPoint() { + var root: HasStrongProperty! = HasStrongProperty() + + let latest = RxMutableBox(nil) + + XCTAssertTrue(latest.value == nil) + + let d = root.rx_observe("point") + .subscribeNext { (n: CGPoint?) in + latest.value = n + } + defer { + d.dispose() + } + + XCTAssertTrue(latest.value == root.point) + + root.point = CGPoint(x: -100, y: 1) + + XCTAssertTrue(latest.value == CGPoint(x: -100, y: 1)) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest.value == CGPoint(x: -100, y: 1)) + XCTAssertTrue(!rootDeallocated) + } +} + +#if !DISABLE_SWIZZLING +extension KVOObservableTests { + func testObserveWeak_deprecated_ObserveCGRect() { + var root: HasStrongProperty! = HasStrongProperty() + + let latest = RxMutableBox(nil) + + XCTAssertTrue(latest.value == nil) + + _ = root + .rx_observeWeakly("frame") + .subscribeNext { (n: CGRect?) in + latest.value = n + } + XCTAssertTrue(latest.value == root.frame) + + root.frame = CGRectMake(-2, 0, 0, 1) + + XCTAssertTrue(latest.value == CGRectMake(-2, 0, 0, 1)) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest.value == nil) + XCTAssertTrue(rootDeallocated) + } + + func testObserveWeak_deprecated_ObserveCGSize() { + var root: HasStrongProperty! = HasStrongProperty() + + let latest = RxMutableBox(nil) + + XCTAssertTrue(latest.value == nil) + + _ = root + .rx_observeWeakly("size") + .subscribeNext { (n: CGSize?) in + latest.value = n + } + XCTAssertTrue(latest.value == root.size) + + root.size = CGSizeMake(56, 1) + + XCTAssertTrue(latest.value == CGSizeMake(56, 1)) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest.value == nil) + XCTAssertTrue(rootDeallocated) + } + + func testObserveWeak_deprecated_ObserveCGPoint() { + var root: HasStrongProperty! = HasStrongProperty() + + let latest = RxMutableBox(nil) + + XCTAssertTrue(latest.value == nil) + + _ = root + .rx_observeWeakly("point") + .subscribeNext { (n: CGPoint?) in + latest.value = n + } + + XCTAssertTrue(latest.value == root.point) + + root.point = CGPoint(x: -100, y: 1) + + XCTAssertTrue(latest.value == CGPoint(x: -100, y: 1)) + + var rootDeallocated = false + + _ = root + .rx_deallocated + .subscribeCompleted { + rootDeallocated = true + } + + root = nil + + XCTAssertTrue(latest.value == nil) + XCTAssertTrue(rootDeallocated) + } +} +#endif From a35b06b066ef4733e6db838f71984f0fca883cfc Mon Sep 17 00:00:00 2001 From: Alexsander Akers Date: Sat, 14 Nov 2015 11:37:02 +0000 Subject: [PATCH 185/210] Move parameter line to correct function --- RxSwift/Observables/Observable+Multiple.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RxSwift/Observables/Observable+Multiple.swift b/RxSwift/Observables/Observable+Multiple.swift index d6b4dd6c..abc3179b 100644 --- a/RxSwift/Observables/Observable+Multiple.swift +++ b/RxSwift/Observables/Observable+Multiple.swift @@ -109,7 +109,6 @@ extension ObservableType where E : ObservableConvertibleType { /** Merges elements from all observable sequences in the given enumerable sequence into a single observable sequence. - - parameter maxConcurrent: Maximum number of inner observable sequences being subscribed to concurrently. - returns: The observable sequence that merges the elements of the observable sequences. */ @warn_unused_result(message="http://git.io/rxs.uo") @@ -120,6 +119,7 @@ extension ObservableType where E : ObservableConvertibleType { /** Merges elements from all inner observable sequences into a single observable sequence, limiting the number of concurrent subscriptions to inner sequences. + - parameter maxConcurrent: Maximum number of inner observable sequences being subscribed to concurrently. - returns: The observable sequence that merges the elements of the inner sequences. */ @warn_unused_result(message="http://git.io/rxs.uo") From 5cc185743ad8fca4210f6debb7c02f0a05b94981 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sat, 14 Nov 2015 21:22:01 +0100 Subject: [PATCH 186/210] Polish for animated table view updates. --- RxDataSourceStarterKit/Changeset.swift | 4 ++ ...TableViewSectionedAnimatedDataSource.swift | 7 ++- RxDataSourceStarterKit/Differentiator.swift | 46 ++++++++++++---- ...rvableConvertibleType+Differentiator.swift | 44 +++++++++++++++ ...ctionedViewType+RxAnimatedDataSource.swift | 54 +++++++++++++++++++ RxExample/RxExample.xcodeproj/project.pbxproj | 22 ++++++-- .../PartialUpdatesViewController.swift | 14 +---- 7 files changed, 162 insertions(+), 29 deletions(-) create mode 100644 RxDataSourceStarterKit/ObservableConvertibleType+Differentiator.swift create mode 100644 RxDataSourceStarterKit/UISectionedViewType+RxAnimatedDataSource.swift diff --git a/RxDataSourceStarterKit/Changeset.swift b/RxDataSourceStarterKit/Changeset.swift index 976b7f17..f7a3949c 100644 --- a/RxDataSourceStarterKit/Changeset.swift +++ b/RxDataSourceStarterKit/Changeset.swift @@ -27,6 +27,8 @@ struct ItemPath : CustomDebugStringConvertible { public struct Changeset : CustomDebugStringConvertible { typealias I = S.Item + var reloadData: Bool = false + var finalSections: [S] = [] var insertedSections: [Int] = [] @@ -43,6 +45,8 @@ public struct Changeset : CustomDebugStringConvertible { var initialValue = Changeset() initialValue.insertedSections = Array(0 ..< sections.count) initialValue.finalSections = sections + initialValue.reloadData = true + return initialValue } diff --git a/RxDataSourceStarterKit/DataSources/RxTableViewSectionedAnimatedDataSource.swift b/RxDataSourceStarterKit/DataSources/RxTableViewSectionedAnimatedDataSource.swift index 05956a87..b70d9901 100644 --- a/RxDataSourceStarterKit/DataSources/RxTableViewSectionedAnimatedDataSource.swift +++ b/RxDataSourceStarterKit/DataSources/RxTableViewSectionedAnimatedDataSource.swift @@ -23,7 +23,12 @@ class RxTableViewSectionedAnimatedDataSource : RxTableViewS for c in element { //print("Animating ==============================\n\(c)\n===============================\n") setSections(c.finalSections) - tableView.performBatchUpdates(c) + if c.reloadData { + tableView.reloadData() + } + else { + tableView.performBatchUpdates(c) + } } case .Error(let error): bindingErrorToInterface(error) diff --git a/RxDataSourceStarterKit/Differentiator.swift b/RxDataSourceStarterKit/Differentiator.swift index 95a00068..995333bf 100644 --- a/RxDataSourceStarterKit/Differentiator.swift +++ b/RxDataSourceStarterKit/Differentiator.swift @@ -8,6 +8,21 @@ import Foundation +public enum DifferentiatorError + : ErrorType + , CustomDebugStringConvertible { + case DuplicateItem(item: Any) +} + +extension DifferentiatorError { + public var debugDescription: String { + switch self { + case let .DuplicateItem(item): + return "Duplicate item \(item)" + } + } +} + enum EditEvent : CustomDebugStringConvertible { case Inserted // can't be found in old sections case Deleted // Was in old, not in new, in it's place is something "not new" :(, otherwise it's Updated @@ -61,17 +76,22 @@ extension ItemAdditionalInfo { } } -func indexSections(sections: [S]) -> [S : Int] { +func indexSections(sections: [S]) throws -> [S : Int] { var indexedSections: [S : Int] = [:] for (i, section) in sections.enumerate() { - precondition(indexedSections[section] == nil, "Section \(section) has already been indexed at \(indexedSections[section]!)") + guard indexedSections[section] == nil else { + #if DEBUG + precondition(indexedSections[section] == nil, "Section \(section) has already been indexed at \(indexedSections[section]!)") + #endif + throw DifferentiatorError.DuplicateItem(item: section) + } indexedSections[section] = i } return indexedSections } -func indexSectionItems(sections: [S]) -> [S.Item : (Int, Int)] { +func indexSectionItems(sections: [S]) throws -> [S.Item : (Int, Int)] { var totalItems = 0 for i in 0 ..< sections.count { totalItems += sections[i].items.count @@ -82,7 +102,12 @@ func indexSectionItems( for i in 0 ..< sections.count { for (j, item) in sections[i].items.enumerate() { - precondition(indexedItems[item] == nil, "Item \(item) has already been indexed at \(indexedItems[item]!)" ) + guard indexedItems[item] == nil else { + #if DEBUG + precondition(indexedItems[item] == nil, "Item \(item) has already been indexed at \(indexedItems[item]!)" ) + #endif + throw DifferentiatorError.DuplicateItem(item: item) + } indexedItems[item] = (i, j) } } @@ -191,7 +216,6 @@ to = [ ] */ - // Generates differential changes suitable for sectioned view consumption. // It will not only detect changes between two states, but it will also try to compress those changes into // almost minimal set of changes. @@ -215,11 +239,11 @@ to = [ // // There maybe exists a better division, but time will tell. // -func differentiate( +func differencesForSectionedView( initialSections: [S], finalSections: [S] ) - -> [Changeset] { + throws -> [Changeset] { typealias I = S.Item @@ -242,11 +266,11 @@ func differentiate( return [ItemAdditionalInfo](count: s.items.count, repeatedValue: defaultItemInfo) } - initialSectionIndexes = indexSections(initialSections) - finalSectionIndexes = indexSections(finalSections) + initialSectionIndexes = try indexSections(initialSections) + finalSectionIndexes = try indexSections(finalSections) - var initialItemIndexes: [I: (Int, Int)] = indexSectionItems(initialSections) - var finalItemIndexes: [I: (Int, Int)] = indexSectionItems(finalSections) + var initialItemIndexes: [I: (Int, Int)] = try indexSectionItems(initialSections) + var finalItemIndexes: [I: (Int, Int)] = try indexSectionItems(finalSections) // mark deleted sections { // 1rst stage diff --git a/RxDataSourceStarterKit/ObservableConvertibleType+Differentiator.swift b/RxDataSourceStarterKit/ObservableConvertibleType+Differentiator.swift new file mode 100644 index 00000000..7a0a1f17 --- /dev/null +++ b/RxDataSourceStarterKit/ObservableConvertibleType+Differentiator.swift @@ -0,0 +1,44 @@ +// +// ObservableConvertibleType+Differentiator.swift +// RxExample +// +// Created by Krunoslav Zaher on 11/14/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE + import RxSwift + import RxCocoa +#endif + +extension ObservableConvertibleType where E: SequenceType, E.Generator.Element : protocol, E.Generator.Element.Item: Hashable { + typealias Section = E.Generator.Element + + func differentiateForSectionedView() + -> Observable<[Changeset
]> { + + return self.asObservable().multicast({ + return PublishSubject() + }) { (sharedSource: Observable) in + let newValues = sharedSource.skip(1) + + let initialValueSequence: Observable<[Changeset
]>= sharedSource + .take(1) + .map { [Changeset.initialValue(Array($0))] } + + let differences = zip(sharedSource, newValues) { oldSections, newSections -> [Changeset
] in + do { + return try differencesForSectionedView(Array(oldSections), finalSections: Array(newSections)) + } + // in case of error, print it to terminal only + catch let e { + print(e) + return [Changeset.initialValue(Array(newSections))] + } + } + + return sequenceOf(initialValueSequence, differences).merge() + } + } +} diff --git a/RxDataSourceStarterKit/UISectionedViewType+RxAnimatedDataSource.swift b/RxDataSourceStarterKit/UISectionedViewType+RxAnimatedDataSource.swift new file mode 100644 index 00000000..33f35a81 --- /dev/null +++ b/RxDataSourceStarterKit/UISectionedViewType+RxAnimatedDataSource.swift @@ -0,0 +1,54 @@ +// +// UISectionedView+RxAnimatedDataSource.swift +// RxExample +// +// Created by Krunoslav Zaher on 11/14/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +import UIKit +#if !RX_NO_MODULE +import RxSwift +import RxCocoa +#endif + +extension UITableView { + public func rx_itemsAnimatedWithDataSource< + DataSource: protocol, + S: SequenceType, + O: ObservableConvertibleType, + Section: protocol + where + DataSource.Element == [Changeset
], + O.E == S, + S.Generator.Element == Section, + Section.Item: Hashable + > + (dataSource: DataSource) + (source: O) + -> Disposable { + let differences = source.differentiateForSectionedView() + return self.rx_itemsWithDataSource(dataSource)(source: differences) + } +} + +extension UICollectionView { + public func rx_itemsAnimatedWithDataSource< + DataSource: protocol, + S: SequenceType, + O: ObservableConvertibleType, + Section: protocol + where + DataSource.Element == [Changeset
], + O.E == S, + S.Generator.Element == Section, + Section.Item: Hashable + > + (dataSource: DataSource) + (source: O) + -> Disposable { + let differences = source.differentiateForSectionedView() + return self.rx_itemsWithDataSource(dataSource)(source: differences) + } +} \ No newline at end of file diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index bd9b237f..cdfcdef2 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -128,6 +128,10 @@ C86E2F451AE5A0CA00C31024 /* WikipediaAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86E2F3B1AE5A0CA00C31024 /* WikipediaAPI.swift */; }; C86E2F461AE5A0CA00C31024 /* WikipediaPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86E2F3C1AE5A0CA00C31024 /* WikipediaPage.swift */; }; C86E2F471AE5A0CA00C31024 /* WikipediaSearchResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86E2F3D1AE5A0CA00C31024 /* WikipediaSearchResult.swift */; }; + C87335671BF79BE000E536E6 /* UISectionedViewType+RxAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C87335661BF79BE000E536E6 /* UISectionedViewType+RxAnimatedDataSource.swift */; }; + C87335681BF79BE000E536E6 /* UISectionedViewType+RxAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C87335661BF79BE000E536E6 /* UISectionedViewType+RxAnimatedDataSource.swift */; }; + C87335771BF7CC0B00E536E6 /* ObservableConvertibleType+Differentiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C87335761BF7CC0B00E536E6 /* ObservableConvertibleType+Differentiator.swift */; }; + C87335781BF7CC0C00E536E6 /* ObservableConvertibleType+Differentiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C87335761BF7CC0B00E536E6 /* ObservableConvertibleType+Differentiator.swift */; }; C88BB8BB1B07E6C90064D411 /* SearchResultViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86E2F321AE5A0CA00C31024 /* SearchResultViewModel.swift */; }; C88BB8BC1B07E6C90064D411 /* HtmlParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83367111AD029AE00C668A7 /* HtmlParsing.swift */; }; C88BB8BD1B07E6C90064D411 /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86E2F331AE5A0CA00C31024 /* SearchViewModel.swift */; }; @@ -534,6 +538,8 @@ C86E2F3B1AE5A0CA00C31024 /* WikipediaAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WikipediaAPI.swift; sourceTree = ""; }; C86E2F3C1AE5A0CA00C31024 /* WikipediaPage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = WikipediaPage.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C86E2F3D1AE5A0CA00C31024 /* WikipediaSearchResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = WikipediaSearchResult.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + C87335661BF79BE000E536E6 /* UISectionedViewType+RxAnimatedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UISectionedViewType+RxAnimatedDataSource.swift"; sourceTree = ""; }; + C87335761BF7CC0B00E536E6 /* ObservableConvertibleType+Differentiator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObservableConvertibleType+Differentiator.swift"; sourceTree = ""; }; C88BB8DC1B07E6C90064D411 /* RxExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RxExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; C88C78631B3EB0A00061C5AB /* RxTableViewSectionedAnimatedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewSectionedAnimatedDataSource.swift; sourceTree = ""; }; C88C78641B3EB0A00061C5AB /* RxTableViewSectionedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewSectionedDataSource.swift; sourceTree = ""; }; @@ -1038,13 +1044,15 @@ isa = PBXGroup; children = ( C8AF26F11B49ABD300131C03 /* README.md */, - C88C78621B3EB0A00061C5AB /* DataSources */, - C8A7501E1B94E77C00D8D046 /* RxDataSourceStarterKit.swift */, C88C788E1B3F14FD0061C5AB /* Changeset.swift */, - C88C78691B3EB0A00061C5AB /* SectionedViewType.swift */, - C88C78981B4012A90061C5AB /* SectionModelType.swift */, - C88C786A1B3EB0A00061C5AB /* SectionModel.swift */, + C88C78621B3EB0A00061C5AB /* DataSources */, C88C78941B3F20DB0061C5AB /* Differentiator.swift */, + C8A7501E1B94E77C00D8D046 /* RxDataSourceStarterKit.swift */, + C88C78691B3EB0A00061C5AB /* SectionedViewType.swift */, + C88C786A1B3EB0A00061C5AB /* SectionModel.swift */, + C88C78981B4012A90061C5AB /* SectionModelType.swift */, + C87335661BF79BE000E536E6 /* UISectionedViewType+RxAnimatedDataSource.swift */, + C87335761BF7CC0B00E536E6 /* ObservableConvertibleType+Differentiator.swift */, ); name = RxDataSourceStarterKit; path = ../RxDataSourceStarterKit; @@ -1750,6 +1758,7 @@ C8297E361B6CF905000589EA /* RootViewController.swift in Sources */, C89464BB1BC6C2B00055219D /* AnonymousObservable.swift in Sources */, C89465991BC6C2BC0055219D /* UISegmentedControl+Rx.swift in Sources */, + C87335781BF7CC0C00E536E6 /* ObservableConvertibleType+Differentiator.swift in Sources */, C8297E371B6CF905000589EA /* RxCollectionViewSectionedDataSource.swift in Sources */, B1B7C3D01BE006870076934E /* TakeLast.swift in Sources */, C89464CD1BC6C2B00055219D /* FlatMap.swift in Sources */, @@ -1839,6 +1848,7 @@ C8F6A1331BEF9DA3007DF367 /* ScheduledItem.swift in Sources */, C89464EB1BC6C2B00055219D /* Observable+Aggregate.swift in Sources */, C8297E441B6CF905000589EA /* PseudoRandomGenerator.swift in Sources */, + C87335681BF79BE000E536E6 /* UISectionedViewType+RxAnimatedDataSource.swift in Sources */, C8297E451B6CF905000589EA /* SectionedViewType.swift in Sources */, C89464D51BC6C2B00055219D /* ObserveOnSerialDispatchQueue.swift in Sources */, C894658E1BC6C2BC0055219D /* UIAlertView+Rx.swift in Sources */, @@ -1948,6 +1958,7 @@ C86E2F3E1AE5A0CA00C31024 /* SearchResultViewModel.swift in Sources */, C83367241AD029AE00C668A7 /* HtmlParsing.swift in Sources */, C859B9AC1B45CF9100D012D7 /* NumberCell.swift in Sources */, + C87335671BF79BE000E536E6 /* UISectionedViewType+RxAnimatedDataSource.swift in Sources */, C84B913C1B8A282000C9CCCF /* RxCollectionViewSectionedDataSource.swift in Sources */, 0706E19B1B17361100BA2D3A /* UIImageView+Extensions.swift in Sources */, C859B9AE1B45CFAB00D012D7 /* NumberSectionView.swift in Sources */, @@ -1985,6 +1996,7 @@ 07E300071B14995F00F00100 /* TableViewController.swift in Sources */, B18F3BE21BDB2E8F000AAC79 /* ReachabilityService.swift in Sources */, B18F3BBC1BD92EC8000AAC79 /* Reachability.swift in Sources */, + C87335771BF7CC0B00E536E6 /* ObservableConvertibleType+Differentiator.swift in Sources */, C859B9A41B45C5D900D012D7 /* PartialUpdatesViewController.swift in Sources */, 07E3C2331B03605B0010338D /* Dependencies.swift in Sources */, C84B913A1B8A282000C9CCCF /* RxTableViewSectionedReloadDataSource.swift in Sources */, diff --git a/RxExample/RxExample/Examples/PartialUpdates/PartialUpdatesViewController.swift b/RxExample/RxExample/Examples/PartialUpdates/PartialUpdatesViewController.swift index 59d81da3..5dc9e998 100644 --- a/RxExample/RxExample/Examples/PartialUpdates/PartialUpdatesViewController.swift +++ b/RxExample/RxExample/Examples/PartialUpdates/PartialUpdatesViewController.swift @@ -116,19 +116,9 @@ class PartialUpdatesViewController : ViewController { skinTableViewDataSource(tvAnimatedDataSource) skinTableViewDataSource(reloadDataSource) - let newSections = self.sections .skip(1) - let initialState = [Changeset.initialValue(self.sections.value)] - - // reactive data sources - - let updates = zip(self.sections, newSections) { (old, new) in - return differentiate(old, finalSections: new) - } - .startWith(initialState) - - updates - .bindTo(partialUpdatesTableViewOutlet.rx_itemsWithDataSource(tvAnimatedDataSource)) + self.sections + .bindTo(partialUpdatesTableViewOutlet.rx_itemsAnimatedWithDataSource(tvAnimatedDataSource)) .addDisposableTo(disposeBag) self.sections From 7deb1499166d2461a306b1c35b2c4755354c2293 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sat, 14 Nov 2015 21:39:50 +0100 Subject: [PATCH 187/210] Returns `NSRecursiveLocks`. It was interesting excursion ... --- RxSwift/Concurrency/Lock.swift | 4 +++- RxSwift/Observables/Implementations/ShareReplay1.swift | 2 +- RxSwift/Subjects/PublishSubject.swift | 2 +- RxSwift/Subjects/ReplaySubject.swift | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/RxSwift/Concurrency/Lock.swift b/RxSwift/Concurrency/Lock.swift index d9d2b1fe..815133c6 100644 --- a/RxSwift/Concurrency/Lock.swift +++ b/RxSwift/Concurrency/Lock.swift @@ -78,6 +78,7 @@ extension NSRecursiveLock : Lock { } } +/* let RECURSIVE_MUTEX = _initializeRecursiveMutex() func _initializeRecursiveMutex() -> pthread_mutex_t { @@ -100,4 +101,5 @@ extension pthread_mutex_t { mutating func unlock() { pthread_mutex_unlock(&self) } -} \ No newline at end of file +} +*/ \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/ShareReplay1.swift b/RxSwift/Observables/Implementations/ShareReplay1.swift index 21b5b2bc..52cb5ebe 100644 --- a/RxSwift/Observables/Implementations/ShareReplay1.swift +++ b/RxSwift/Observables/Implementations/ShareReplay1.swift @@ -18,7 +18,7 @@ final class ShareReplay1 private let _source: Observable - private var _lock = RECURSIVE_MUTEX + private var _lock = NSRecursiveLock() private var _connection: SingleAssignmentDisposable? private var _element: Element? diff --git a/RxSwift/Subjects/PublishSubject.swift b/RxSwift/Subjects/PublishSubject.swift index c569b392..2ed8f704 100644 --- a/RxSwift/Subjects/PublishSubject.swift +++ b/RxSwift/Subjects/PublishSubject.swift @@ -23,7 +23,7 @@ final public class PublishSubject typealias DisposeKey = Bag>.KeyType - private var _lock = RECURSIVE_MUTEX + private var _lock = NSRecursiveLock() // state private var _disposed = false diff --git a/RxSwift/Subjects/ReplaySubject.swift b/RxSwift/Subjects/ReplaySubject.swift index 307ebde7..818521e8 100644 --- a/RxSwift/Subjects/ReplaySubject.swift +++ b/RxSwift/Subjects/ReplaySubject.swift @@ -68,7 +68,7 @@ class ReplayBufferBase : ReplaySubject , SynchronizedUnsubscribeType { - private var _lock = RECURSIVE_MUTEX + private var _lock = NSRecursiveLock() // state private var _disposed = false From b1185750f4c94843655689041d32d71f756e2842 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 15 Nov 2015 00:07:37 +0100 Subject: [PATCH 188/210] Adds scheduler overloads to `just`, `sequenceOf`. Deprecates `asObservable` in favor of `toObservable`. --- Rx.xcodeproj/project.pbxproj | 10 + RxExample/RxExample.xcodeproj/project.pbxproj | 6 +- .../Observables/Implementations/Just.swift | 38 ++++ .../Implementations/Sequence.swift | 58 ++++++ RxSwift/Observables/Observable+Creation.swift | 57 ++++-- .../Tests/Observable+CreationTest.swift | 174 +++++++++++++++++- 6 files changed, 322 insertions(+), 21 deletions(-) create mode 100644 RxSwift/Observables/Implementations/Sequence.swift diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index 693caf24..3e51a1d3 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -289,6 +289,10 @@ C80DDEB41BCE8CA3006A1832 /* Driver+Operators+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DDEB01BCE8CA3006A1832 /* Driver+Operators+arity.swift */; }; C821DBA21BA4DCAB008F3809 /* Buffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C821DBA11BA4DCAB008F3809 /* Buffer.swift */; }; C821DBA31BA4DCAB008F3809 /* Buffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C821DBA11BA4DCAB008F3809 /* Buffer.swift */; }; + C83100641BF7D51600AAE3CD /* Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83100631BF7D51600AAE3CD /* Sequence.swift */; }; + C83100651BF7D51600AAE3CD /* Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83100631BF7D51600AAE3CD /* Sequence.swift */; }; + C83100661BF7D51600AAE3CD /* Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83100631BF7D51600AAE3CD /* Sequence.swift */; }; + C83100671BF7D51600AAE3CD /* Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83100631BF7D51600AAE3CD /* Sequence.swift */; }; C849BE2B1BAB5D070019AD27 /* ObservableConvertibleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C849BE2A1BAB5D070019AD27 /* ObservableConvertibleType.swift */; }; C849BE2C1BAB5D070019AD27 /* ObservableConvertibleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C849BE2A1BAB5D070019AD27 /* ObservableConvertibleType.swift */; }; C84B38E91BA43380001B7D88 /* ScheduledItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B38E71BA43380001B7D88 /* ScheduledItem.swift */; }; @@ -1000,6 +1004,7 @@ C80DDEAB1BCE83B2006A1832 /* Driver+Operators+arity.tt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Driver+Operators+arity.tt"; sourceTree = ""; }; C80DDEB01BCE8CA3006A1832 /* Driver+Operators+arity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Driver+Operators+arity.swift"; sourceTree = ""; }; C821DBA11BA4DCAB008F3809 /* Buffer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Buffer.swift; sourceTree = ""; }; + C83100631BF7D51600AAE3CD /* Sequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sequence.swift; sourceTree = ""; }; C849BE2A1BAB5D070019AD27 /* ObservableConvertibleType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableConvertibleType.swift; sourceTree = ""; }; C84B38E71BA43380001B7D88 /* ScheduledItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduledItem.swift; sourceTree = ""; }; C84B38ED1BA433CD001B7D88 /* Generate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Generate.swift; sourceTree = ""; }; @@ -1306,6 +1311,7 @@ CB255BD61BC46A9C00798A4C /* RetryWhen.swift */, C8093C861B8A72BE0088E94D /* Sample.swift */, C8093C871B8A72BE0088E94D /* Scan.swift */, + C83100631BF7D51600AAE3CD /* Sequence.swift */, C89CDB351BCB0DD7002063D9 /* ShareReplay1.swift */, CB30D9E81BF0E3500084C1C0 /* SingleAsync.swift */, C8093C881B8A72BE0088E94D /* Sink.swift */, @@ -2259,6 +2265,7 @@ C89CDB371BCB0DD7002063D9 /* ShareReplay1.swift in Sources */, C8093D2A1B8A72BE0088E94D /* ObserveOn.swift in Sources */, CB883B411BE24C15000AC2EE /* RefCountDisposable.swift in Sources */, + C83100651BF7D51600AAE3CD /* Sequence.swift in Sources */, C8093D361B8A72BE0088E94D /* Sample.swift in Sources */, C84CC54F1BDCF48200E06A64 /* LockOwnerType.swift in Sources */, D2752D621BC5551A0070C418 /* SkipUntil.swift in Sources */, @@ -2397,6 +2404,7 @@ C89CDB361BCB0DD7002063D9 /* ShareReplay1.swift in Sources */, C8093D291B8A72BE0088E94D /* ObserveOn.swift in Sources */, CB883B401BE24C15000AC2EE /* RefCountDisposable.swift in Sources */, + C83100641BF7D51600AAE3CD /* Sequence.swift in Sources */, C8093D351B8A72BE0088E94D /* Sample.swift in Sources */, C84CC54E1BDCF48200E06A64 /* LockOwnerType.swift in Sources */, D285BAC41BC0231000B3F602 /* SkipUntil.swift in Sources */, @@ -2535,6 +2543,7 @@ C89CDB391BCB0DD7002063D9 /* ShareReplay1.swift in Sources */, C8F0BFB71BBBFB8B001B112F /* ObserveOn.swift in Sources */, CB883B431BE24C15000AC2EE /* RefCountDisposable.swift in Sources */, + C83100671BF7D51600AAE3CD /* Sequence.swift in Sources */, C8F0BFB81BBBFB8B001B112F /* Sample.swift in Sources */, C84CC5511BDCF48200E06A64 /* LockOwnerType.swift in Sources */, D21C29311BC6A1C300448E70 /* SkipUntil.swift in Sources */, @@ -2824,6 +2833,7 @@ D2EBEB2A1BB9B6C5003A27DC /* Zip+CollectionType.swift in Sources */, C89CDB381BCB0DD7002063D9 /* ShareReplay1.swift in Sources */, CB883B421BE24C15000AC2EE /* RefCountDisposable.swift in Sources */, + C83100661BF7D51600AAE3CD /* Sequence.swift in Sources */, D2EBEB401BB9B6DE003A27DC /* BehaviorSubject.swift in Sources */, C84CC5501BDCF48200E06A64 /* LockOwnerType.swift in Sources */, D2EBEB271BB9B6C1003A27DC /* Timer.swift in Sources */, diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index cdfcdef2..e8c6fa8c 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -103,6 +103,7 @@ C8297E5F1B6CF905000589EA /* WikipediaImageCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C8C46DA41B47F7110020D71E /* WikipediaImageCell.xib */; }; C8297E601B6CF905000589EA /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; }; C8297E611B6CF905000589EA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E11B0B32DA009BCF9A /* Main.storyboard */; }; + C83100691BF7F4CA00AAE3CD /* Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83100681BF7F4CA00AAE3CD /* Sequence.swift */; }; C83367231AD029AE00C668A7 /* Example.swift in Sources */ = {isa = PBXBuildFile; fileRef = C833670F1AD029AE00C668A7 /* Example.swift */; }; C83367241AD029AE00C668A7 /* HtmlParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83367111AD029AE00C668A7 /* HtmlParsing.swift */; }; C83367251AD029AE00C668A7 /* ImageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83367121AD029AE00C668A7 /* ImageService.swift */; }; @@ -516,6 +517,7 @@ C80DDEC91BCE9041006A1832 /* ObservableConvertibleType+Driver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObservableConvertibleType+Driver.swift"; sourceTree = ""; }; C81B39F11BC1C28400EF5A9F /* Rx.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Rx.xcodeproj; path = ../Rx.xcodeproj; sourceTree = ""; }; C8297E691B6CF905000589EA /* RxExample-iOS-no-module.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "RxExample-iOS-no-module.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + C83100681BF7F4CA00AAE3CD /* Sequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sequence.swift; sourceTree = ""; }; C83366DD1AD0293800C668A7 /* RxExample-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "RxExample-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; C833670F1AD029AE00C668A7 /* Example.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Example.swift; sourceTree = ""; }; C83367111AD029AE00C668A7 /* HtmlParsing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HtmlParsing.swift; sourceTree = ""; }; @@ -1170,7 +1172,6 @@ C89464481BC6C2B00055219D /* Implementations */ = { isa = PBXGroup; children = ( - C8F6A1361BEF9DD4007DF367 /* RetryWhen.swift */, CB883B5F1BE3AC72000AC2EE /* AddRef.swift */, C89464491BC6C2B00055219D /* Amb.swift */, C894644A1BC6C2B00055219D /* AnonymousObservable.swift */, @@ -1205,8 +1206,10 @@ C89464671BC6C2B00055219D /* Reduce.swift */, C89464681BC6C2B00055219D /* RefCount.swift */, C89464691BC6C2B00055219D /* Repeat.swift */, + C8F6A1361BEF9DD4007DF367 /* RetryWhen.swift */, C894646A1BC6C2B00055219D /* Sample.swift */, C894646B1BC6C2B00055219D /* Scan.swift */, + C83100681BF7F4CA00AAE3CD /* Sequence.swift */, C89CDB611BCC45DC002063D9 /* ShareReplay1.swift */, CB30D9ED1BF106260084C1C0 /* SingleAsync.swift */, C894646C1BC6C2B00055219D /* Sink.swift */, @@ -1849,6 +1852,7 @@ C89464EB1BC6C2B00055219D /* Observable+Aggregate.swift in Sources */, C8297E441B6CF905000589EA /* PseudoRandomGenerator.swift in Sources */, C87335681BF79BE000E536E6 /* UISectionedViewType+RxAnimatedDataSource.swift in Sources */, + C83100691BF7F4CA00AAE3CD /* Sequence.swift in Sources */, C8297E451B6CF905000589EA /* SectionedViewType.swift in Sources */, C89464D51BC6C2B00055219D /* ObserveOnSerialDispatchQueue.swift in Sources */, C894658E1BC6C2BC0055219D /* UIAlertView+Rx.swift in Sources */, diff --git a/RxSwift/Observables/Implementations/Just.swift b/RxSwift/Observables/Implementations/Just.swift index fecb935a..280827ae 100644 --- a/RxSwift/Observables/Implementations/Just.swift +++ b/RxSwift/Observables/Implementations/Just.swift @@ -8,6 +8,44 @@ import Foundation +class JustScheduledSink : Sink { + typealias Parent = JustScheduled + + private let _parent: Parent + + init(parent: Parent, observer: O) { + _parent = parent + super.init(observer: observer) + } + + func run() -> Disposable { + let scheduler = _parent._scheduler + return scheduler.schedule(_parent._element) { element in + self.forwardOn(.Next(element)) + return scheduler.schedule(()) { _ in + self.forwardOn(.Completed) + return NopDisposable.instance + } + } + } +} + +class JustScheduled : Producer { + private let _scheduler: ImmediateSchedulerType + private let _element: Element + + init(element: Element, scheduler: ImmediateSchedulerType) { + _scheduler = scheduler + _element = element + } + + override func subscribe(observer: O) -> Disposable { + let sink = JustScheduledSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink + } +} + class Just : Producer { private let _element: Element diff --git a/RxSwift/Observables/Implementations/Sequence.swift b/RxSwift/Observables/Implementations/Sequence.swift new file mode 100644 index 00000000..63ee7138 --- /dev/null +++ b/RxSwift/Observables/Implementations/Sequence.swift @@ -0,0 +1,58 @@ +// +// Sequence.swift +// Rx +// +// Created by Krunoslav Zaher on 11/14/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +class SequenceSink : Sink { + typealias Parent = Sequence + + private let _parent: Parent + + init(parent: Parent, observer: O) { + _parent = parent + super.init(observer: observer) + } + + func run() -> Disposable { + return _parent._scheduler!.scheduleRecursive((0, _parent._elements)) { (state, recurse) in + if state.0 < state.1.count { + self.forwardOn(.Next(state.1[state.0])) + recurse((state.0 + 1, state.1)) + } + else { + self.forwardOn(.Completed) + } + } + } +} + +class Sequence : Producer { + private let _elements: [E] + private let _scheduler: ImmediateSchedulerType? + + init(elements: [E], scheduler: ImmediateSchedulerType?) { + _elements = elements + _scheduler = scheduler + } + + override func subscribe(observer: O) -> Disposable { + // optimized version without scheduler + guard _scheduler != nil else { + for element in _elements { + observer.on(.Next(element)) + } + + observer.on(.Completed) + return NopDisposable.instance + } + + let sink = SequenceSink(parent: self, observer: observer) + sink.disposable = sink.run() + return sink + } +} \ No newline at end of file diff --git a/RxSwift/Observables/Observable+Creation.swift b/RxSwift/Observables/Observable+Creation.swift index 9b3e9678..b7dfc667 100644 --- a/RxSwift/Observables/Observable+Creation.swift +++ b/RxSwift/Observables/Observable+Creation.swift @@ -58,23 +58,30 @@ public func just(element: E) -> Observable { return Just(element: element) } +/** +Returns an observable sequence that contains a single element. + +- parameter element: Single element in the resulting observable sequence. +- parameter: Scheduler to send the single element on. +- returns: An observable sequence containing the single specified element. +*/ +@warn_unused_result(message="http://git.io/rxs.uo") +public func just(element: E, scheduler: ImmediateSchedulerType) -> Observable { + return JustScheduled(element: element, scheduler: scheduler) +} + // MARK: of /** This method creates a new Observable instance with a variable number of elements. +- parameter elements: Elements to generate. +- parameter scheduler: Scheduler to send elements on. If `nil`, elements are sent immediatelly on subscription. - returns: The observable sequence whose elements are pulled from the given arguments. */ @warn_unused_result(message="http://git.io/rxs.uo") -public func sequenceOf(elements: E ...) -> Observable { - return AnonymousObservable { observer in - for element in elements { - observer.on(.Next(element)) - } - - observer.on(.Completed) - return NopDisposable.instance - } +public func sequenceOf(elements: E ..., scheduler: ImmediateSchedulerType? = nil) -> Observable { + return Sequence(elements: elements, scheduler: scheduler) } @@ -85,15 +92,31 @@ extension SequenceType { - returns: The observable sequence whose elements are pulled from the given enumerable sequence. */ @warn_unused_result(message="http://git.io/rxs.uo") + @available(*, deprecated=2.0.0, message="Please use toObservable extension.") public func asObservable() -> Observable { - return AnonymousObservable { observer in - for element in self { - observer.on(.Next(element)) - } - - observer.on(.Completed) - return NopDisposable.instance - } + return Sequence(elements: Array(self), scheduler: nil) + } + + /** + Converts a sequence to an observable sequence. + + - returns: The observable sequence whose elements are pulled from the given enumerable sequence. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func toObservable(scheduler: ImmediateSchedulerType? = nil) -> Observable { + return Sequence(elements: Array(self), scheduler: scheduler) + } +} + +extension Array { + /** + Converts a sequence to an observable sequence. + + - returns: The observable sequence whose elements are pulled from the given enumerable sequence. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func toObservable(scheduler: ImmediateSchedulerType? = nil) -> Observable { + return Sequence(elements: self, scheduler: scheduler) } } diff --git a/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift b/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift index 2e671a9e..31fd2ee6 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift @@ -14,6 +14,174 @@ class ObservableCreationTests : RxTest { } +// MARK: just +extension ObservableCreationTests { + func testJust_Immediate() { + let scheduler = TestScheduler(initialClock: 0) + + let res = scheduler.start { + return just(42) + } + + XCTAssertEqual(res.messages, [ + next(200, 42), + completed(200) + ]) + } + + func testJust_Basic() { + let scheduler = TestScheduler(initialClock: 0) + + let res = scheduler.start { + return just(42, scheduler: scheduler) + } + + XCTAssertEqual(res.messages, [ + next(201, 42), + completed(202) + ]) + } + + func testJust_Disposed() { + let scheduler = TestScheduler(initialClock: 0) + + let res = scheduler.start(200) { + return just(42, scheduler: scheduler) + } + + XCTAssertEqual(res.messages, [ + ]) + } + + func testJust_DisposeAfterNext() { + let scheduler = TestScheduler(initialClock: 0) + + let d = SingleAssignmentDisposable() + + let res = createObserver(scheduler) as MockObserver + + scheduler.scheduleAt(100) { + d.disposable = just(42, scheduler: scheduler).subscribe { e in + res.on(e) + + switch e { + case .Next: + d.dispose() + default: + break + } + } + } + + scheduler.start() + + XCTAssertEqual(res.messages, [ + next(101, 42) + ]) + } + + func testJust_DefaultScheduler() { + let res = try! just(42, scheduler: MainScheduler.sharedInstance) + .toBlocking() + .toArray() + + XCTAssertEqual(res, [ + 42 + ]) + } +} + +// MARK: toObservable +extension ObservableCreationTests { + func testToObservable_complete_immediate() { + let scheduler = TestScheduler(initialClock: 0) + let res = scheduler.start { + [3, 1, 2, 4].toObservable() + } + + XCTAssertEqual(res.messages, [ + next(200, 3), + next(200, 1), + next(200, 2), + next(200, 4), + completed(200) + ]) + } + + func testToObservable_complete() { + let scheduler = TestScheduler(initialClock: 0) + let res = scheduler.start { + [3, 1, 2, 4].toObservable(scheduler) + } + + XCTAssertEqual(res.messages, [ + next(201, 3), + next(202, 1), + next(203, 2), + next(204, 4), + completed(205) + ]) + } + + func testToObservable_dispose() { + let scheduler = TestScheduler(initialClock: 0) + let res = scheduler.start(203) { + [3, 1, 2, 4].toObservable(scheduler) + } + + XCTAssertEqual(res.messages, [ + next(201, 3), + next(202, 1), + ]) + } +} + +// MARK: sequenceOf +extension ObservableCreationTests { + func testSequenceOf_complete_immediate() { + let scheduler = TestScheduler(initialClock: 0) + let res = scheduler.start { + sequenceOf(3, 1, 2, 4) + } + + XCTAssertEqual(res.messages, [ + next(200, 3), + next(200, 1), + next(200, 2), + next(200, 4), + completed(200) + ]) + } + + func testSequenceOf_complete() { + let scheduler = TestScheduler(initialClock: 0) + let res = scheduler.start { + sequenceOf(3, 1, 2, 4, scheduler: scheduler) + } + + XCTAssertEqual(res.messages, [ + next(201, 3), + next(202, 1), + next(203, 2), + next(204, 4), + completed(205) + ]) + } + + func testSequenceOf_dispose() { + let scheduler = TestScheduler(initialClock: 0) + let res = scheduler.start(203) { + sequenceOf(3, 1, 2, 4, scheduler: scheduler) + } + + XCTAssertEqual(res.messages, [ + next(201, 3), + next(202, 1), + ]) + } +} + +// MARK: generate extension ObservableCreationTests { func testGenerate_Finite() { let scheduler = TestScheduler(initialClock: 0) @@ -100,7 +268,7 @@ extension ObservableCreationTests { } } -// range +// MARK: range extension ObservableCreationTests { func testRange_Boundaries() { let scheduler = TestScheduler(initialClock: 0) @@ -130,7 +298,7 @@ extension ObservableCreationTests { } } -// repeatElement +// MARK: repeatElement extension ObservableCreationTests { func testRepeat_Element() { let scheduler = TestScheduler(initialClock: 0) @@ -150,7 +318,7 @@ extension ObservableCreationTests { } } -// using +// MARK: using extension ObservableCreationTests { func testUsing_Complete() { let scheduler = TestScheduler(initialClock: 0) From ba5fcff91e8566e29b04af9ce5a1658e53759710 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 15 Nov 2015 13:10:42 +0100 Subject: [PATCH 189/210] Optimizations: `map.map.map ...` -> `map`. --- RxSwift/Observable.swift | 10 + RxSwift/Observables/Implementations/Map.swift | 122 +++++--- ...Observable+StandardSequenceOperators.swift | 4 +- ...rvable+StandardSequenceOperatorsTest.swift | 266 +++++++++++++++++- 4 files changed, 353 insertions(+), 49 deletions(-) diff --git a/RxSwift/Observable.swift b/RxSwift/Observable.swift index 8fa99d36..0bc931bd 100644 --- a/RxSwift/Observable.swift +++ b/RxSwift/Observable.swift @@ -38,4 +38,14 @@ public class Observable : ObservableType { OSAtomicDecrement32(&resourceCount) #endif } + + // this is kind of ugly I know :( + // Swift compiler reports "Not supported yet" when trying to override protocol extensions, so ¯\_(ツ)_/¯ + + /** + Optimizations for map operator + */ + internal func composeMap(selector: Element throws -> R) -> Observable { + return Map(source: self, selector: selector) + } } diff --git a/RxSwift/Observables/Implementations/Map.swift b/RxSwift/Observables/Implementations/Map.swift index 20c6bd1a..035b7273 100644 --- a/RxSwift/Observables/Implementations/Map.swift +++ b/RxSwift/Observables/Implementations/Map.swift @@ -19,16 +19,12 @@ class MapSink : Sink, ObserverType { _parent = parent super.init(observer: observer) } - - func performMap(element: SourceType) throws -> ResultType { - abstractMethod() - } func on(event: Event) { switch event { case .Next(let element): do { - let mappedElement = try performMap(element) + let mappedElement = try _parent._selector(element) forwardOn(.Next(mappedElement)) } catch let e { @@ -45,63 +41,97 @@ class MapSink : Sink, ObserverType { } } -class MapSink1 : MapSink { +class MapWithIndexSink : Sink, ObserverType { typealias ResultType = O.E + typealias Element = SourceType + typealias Parent = MapWithIndex - override init(parent: Map, observer: O) { - super.init(parent: parent, observer: observer) + private let _parent: Parent + + private var _index = 0 + + init(parent: Parent, observer: O) { + _parent = parent + super.init(observer: observer) } - - override func performMap(element: SourceType) throws -> ResultType { - return try _parent._selector1!(element) + + func on(event: Event) { + switch event { + case .Next(let element): + do { + let mappedElement = try _parent._selector(element, try incrementChecked(&_index)) + forwardOn(.Next(mappedElement)) + } + catch let e { + forwardOn(.Error(e)) + dispose() + } + case .Error(let error): + forwardOn(.Error(error)) + dispose() + case .Completed: + forwardOn(.Completed) + dispose() + } } } -class MapSink2 : MapSink { - typealias ResultType = O.E - - private var _index = 0 - - override init(parent: Map, observer: O) { - super.init(parent: parent, observer: observer) +class MapWithIndex : Producer { + typealias Selector = (SourceType, Int) throws -> ResultType + + private let _source: Observable + + private let _selector: Selector + + init(source: Observable, selector: Selector) { + _source = source + _selector = selector } - override func performMap(element: SourceType) throws -> ResultType { - return try _parent._selector2!(element, try incrementChecked(&_index)) + + override func run(observer: O) -> Disposable { + let sink = MapWithIndexSink(parent: self, observer: observer) + sink.disposable = _source.subscribe(sink) + return sink } } +#if TRACE_RESOURCES +public var numberOfMapOperators: Int32 = 0 +#endif + class Map: Producer { - typealias Selector1 = (SourceType) throws -> ResultType - typealias Selector2 = (SourceType, Int) throws -> ResultType - + typealias Selector = (SourceType) throws -> ResultType + private let _source: Observable - - private let _selector1: Selector1? - private let _selector2: Selector2? - - init(source: Observable, selector: Selector1) { + + private let _selector: Selector + + init(source: Observable, selector: Selector) { _source = source - _selector1 = selector - _selector2 = nil + _selector = selector + +#if TRACE_RESOURCES + OSAtomicIncrement32(&numberOfMapOperators) +#endif } - - init(source: Observable, selector: Selector2) { - _source = source - _selector2 = selector - _selector1 = nil + + override func composeMap(selector: ResultType throws -> R) -> Observable { + let originalSelector = _selector + return Map(source: _source, selector: { (s: SourceType) throws -> R in + let r: ResultType = try originalSelector(s) + return try selector(r) + }) } override func run(observer: O) -> Disposable { - if let _ = _selector1 { - let sink = MapSink1(parent: self, observer: observer) - sink.disposable = _source.subscribe(sink) - return sink - } - else { - let sink = MapSink2(parent: self, observer: observer) - sink.disposable = _source.subscribe(sink) - return sink - } - + let sink = MapSink(parent: self, observer: observer) + sink.disposable = _source.subscribe(sink) + return sink } + + #if TRACE_RESOURCES + deinit { + OSAtomicDecrement32(&numberOfMapOperators) + } + #endif } \ No newline at end of file diff --git a/RxSwift/Observables/Observable+StandardSequenceOperators.swift b/RxSwift/Observables/Observable+StandardSequenceOperators.swift index 31dad0bb..ad73a342 100644 --- a/RxSwift/Observables/Observable+StandardSequenceOperators.swift +++ b/RxSwift/Observables/Observable+StandardSequenceOperators.swift @@ -156,7 +156,7 @@ extension ObservableType { @warn_unused_result(message="http://git.io/rxs.uo") public func map(selector: E throws -> R) -> Observable { - return Map(source: self.asObservable(), selector: selector) + return self.asObservable().composeMap(selector) } /** @@ -168,7 +168,7 @@ extension ObservableType { @warn_unused_result(message="http://git.io/rxs.uo") public func mapWithIndex(selector: (E, Int) throws -> R) -> Observable { - return Map(source: self.asObservable(), selector: selector) + return MapWithIndex(source: self.asObservable(), selector: selector) } } diff --git a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift index db342e69..46383f41 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift @@ -701,7 +701,6 @@ extension ObservableStandardSequenceOperatorsTest { } // MARK: map -// these test are not port from Rx extension ObservableStandardSequenceOperatorsTest { func testMap_Never() { let scheduler = TestScheduler(initialClock: 0) @@ -1042,6 +1041,271 @@ extension ObservableStandardSequenceOperatorsTest { } } +// MARK: map compose +extension ObservableStandardSequenceOperatorsTest { + func testMapCompose_Never() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + ]) + + let res = scheduler.start { xs.map { $0 * 10 }.map { $0 + 1 } } + + let correctMessages: [Recorded] = [ + ] + + let correctSubscriptions = [ + Subscription(200, 1000) + ] + + XCTAssertEqual(res.messages, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func testMapCompose_Empty() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + completed(300) + ]) + + let res = scheduler.start { xs.map { $0 * 10 }.map { $0 + 1 } } + + let correctMessages: [Recorded] = [ + completed(300) + ] + + let correctSubscriptions = [ + Subscription(200, 300) + ] + + XCTAssertEqual(res.messages, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func testMapCompose_Range() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 0), + next(220, 1), + next(230, 2), + next(240, 4), + completed(300) + ]) + + let res = scheduler.start { xs.map { $0 * 10 }.map { $0 + 1 } } + + let correctMessages: [Recorded] = [ + next(210, 0 * 10 + 1), + next(220, 1 * 10 + 1), + next(230, 2 * 10 + 1), + next(240, 4 * 10 + 1), + completed(300) + ] + + let correctSubscriptions = [ + Subscription(200, 300) + ] + + XCTAssertEqual(res.messages, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func testMapCompose_Error() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 0), + next(220, 1), + next(230, 2), + next(240, 4), + error(300, testError) + ]) + + let res = scheduler.start { xs.map { $0 * 10 }.map { $0 + 1 } } + + let correctMessages: [Recorded] = [ + next(210, 0 * 10 + 1), + next(220, 1 * 10 + 1), + next(230, 2 * 10 + 1), + next(240, 4 * 10 + 1), + error(300, testError) + ] + + let correctSubscriptions = [ + Subscription(200, 300) + ] + + XCTAssertEqual(res.messages, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func testMapCompose_Dispose() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 0), + next(220, 1), + next(230, 2), + next(240, 4), + error(300, testError) + ]) + + let res = scheduler.start(290) { xs.map { $0 * 10 }.map { $0 + 1 } } + + let correctMessages: [Recorded] = [ + next(210, 0 * 10 + 1), + next(220, 1 * 10 + 1), + next(230, 2 * 10 + 1), + next(240, 4 * 10 + 1), + ] + + let correctSubscriptions = [ + Subscription(200, 290) + ] + + XCTAssertEqual(res.messages, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func testMapCompose_Selector1Throws() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 0), + next(220, 1), + next(230, 2), + next(240, 4), + error(300, testError) + ]) + + let res = scheduler.start { + xs + .map { x throws -> Int in if x < 2 { return x * 10 } else { throw testError } } + .map { $0 + 1 } + } + + let correctMessages: [Recorded] = [ + next(210, 0 * 10 + 1), + next(220, 1 * 10 + 1), + error(230, testError) + ] + + let correctSubscriptions = [ + Subscription(200, 230) + ] + + XCTAssertEqual(res.messages, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func testMapCompose_Selector2Throws() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 0), + next(220, 1), + next(230, 2), + next(240, 4), + error(300, testError) + ]) + + let res = scheduler.start { + xs + .map { $0 * 10 } + .map { x throws -> Int in if x < 20 { return x + 1 } else { throw testError } } + } + + let correctMessages: [Recorded] = [ + next(210, 0 * 10 + 1), + next(220, 1 * 10 + 1), + error(230, testError) + ] + + let correctSubscriptions = [ + Subscription(200, 230) + ] + + XCTAssertEqual(res.messages, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + #if TRACE_RESOURCES + func testMapCompose_OptimizationIsPerformed() { + let scheduler = TestScheduler(initialClock: 0) + + var checked = false + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 0), + ]) + + let res = scheduler.start { + xs + .map { $0 * 10 } + .map { x -> Int in + checked = true + XCTAssertTrue(numberOfMapOperators == 1) + return x + 1 + } + } + + let correctMessages: [Recorded] = [ + next(210, 0 * 10 + 1), + ] + + let correctSubscriptions = [ + Subscription(200, 1000) + ] + + XCTAssertTrue(checked) + XCTAssertEqual(res.messages, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func testMapCompose_OptimizationIsNotPerformed() { + let scheduler = TestScheduler(initialClock: 0) + + var checked = false + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 0), + ]) + + let res = scheduler.start { + xs + .map { $0 * 10 } + .filter { _ in true } + .map { x -> Int in + checked = true + XCTAssertTrue(numberOfMapOperators == 2) + return x + 1 + } + } + + let correctMessages: [Recorded] = [ + next(210, 0 * 10 + 1), + ] + + let correctSubscriptions = [ + Subscription(200, 1000) + ] + + XCTAssertTrue(checked) + XCTAssertEqual(res.messages, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + #endif +} + // MARK: flatMapFirst extension ObservableStandardSequenceOperatorsTest { From 90dad761a2cf7abf85f0409fd5b69658ed4e2355 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 15 Nov 2015 13:11:21 +0100 Subject: [PATCH 190/210] Fixes warnings in example app. --- RxCocoa/Common/CocoaUnits/Driver/Driver.swift | 2 +- .../Examples/Calculator/CalculatorViewController.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver.swift index 2536d8de..bbd534c2 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/Driver.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver.swift @@ -153,7 +153,7 @@ public struct Drive { @warn_unused_result(message="http://git.io/rxs.uo") public static func sequenceOf(elements: E ...) -> Driver { - let source = elements.asObservable().subscribeOn(ConcurrentMainScheduler.sharedInstance) + let source = elements.toObservable().subscribeOn(ConcurrentMainScheduler.sharedInstance) return Driver(raw: source) } diff --git a/RxExample/RxExample/Examples/Calculator/CalculatorViewController.swift b/RxExample/RxExample/Examples/Calculator/CalculatorViewController.swift index b094033e..201e8327 100644 --- a/RxExample/RxExample/Examples/Calculator/CalculatorViewController.swift +++ b/RxExample/RxExample/Examples/Calculator/CalculatorViewController.swift @@ -98,7 +98,7 @@ class CalculatorViewController: ViewController { ] commands - .asObservable() + .toObservable() .merge() .scan(CLEAR_STATE) { [unowned self] a, x in return self.tranformState(a, x) From 218b342a9a143dd8d437714f355188ba9d01f79e Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 15 Nov 2015 13:18:05 +0100 Subject: [PATCH 191/210] Makes disposal explicit in playgrounds so it doesn't confuses people. --- .../Contents.swift | 4 +-- .../Subjects.xcplaygroundpage/Contents.swift | 26 ++++++++++++------- Rx.playground/contents.xcplayground | 2 +- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Rx.playground/Pages/Introduction.xcplaygroundpage/Contents.swift b/Rx.playground/Pages/Introduction.xcplaygroundpage/Contents.swift index 9055e826..fc517484 100644 --- a/Rx.playground/Pages/Introduction.xcplaygroundpage/Contents.swift +++ b/Rx.playground/Pages/Introduction.xcplaygroundpage/Contents.swift @@ -81,8 +81,8 @@ example("sequenceOf") { `from` creates a sequence from `SequenceType` */ -example("from") { - let sequenceFromArray = [1, 2, 3, 4, 5].asObservable() +example("toObservable") { + let sequenceFromArray = [1, 2, 3, 4, 5].toObservable() let subscription = sequenceFromArray .subscribe { event in diff --git a/Rx.playground/Pages/Subjects.xcplaygroundpage/Contents.swift b/Rx.playground/Pages/Subjects.xcplaygroundpage/Contents.swift index c9f667ee..77737bbc 100644 --- a/Rx.playground/Pages/Subjects.xcplaygroundpage/Contents.swift +++ b/Rx.playground/Pages/Subjects.xcplaygroundpage/Contents.swift @@ -7,8 +7,8 @@ import RxSwift A Subject is a sort of bridge or proxy that is available in some implementations of ReactiveX that acts both as an observer and as an Observable. Because it is an observer, it can subscribe to one or more Observables, and because it is an Observable, it can pass through the items it observes by reemitting them, and it can also emit new items. */ -func writeSequenceToConsole(name: String, sequence: O) { - _ = sequence +func writeSequenceToConsole(name: String, sequence: O) -> Disposable { + return sequence .subscribe { e in print("Subscription: \(name), event: \(e)") } @@ -27,11 +27,13 @@ func writeSequenceToConsole(name: String, sequence: O) { */ example("PublishSubject") { + let disposeBag = DisposeBag() + let subject = PublishSubject() - writeSequenceToConsole("1", sequence: subject) + writeSequenceToConsole("1", sequence: subject).addDisposableTo(disposeBag) subject.on(.Next("a")) subject.on(.Next("b")) - writeSequenceToConsole("2", sequence: subject) + writeSequenceToConsole("2", sequence: subject).addDisposableTo(disposeBag) subject.on(.Next("c")) subject.on(.Next("d")) } @@ -46,12 +48,13 @@ example("PublishSubject") { ![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/replaysubject.png) */ example("ReplaySubject") { + let disposeBag = DisposeBag() let subject = ReplaySubject.create(bufferSize: 1) - writeSequenceToConsole("1", sequence: subject) + writeSequenceToConsole("1", sequence: subject).addDisposableTo(disposeBag) subject.on(.Next("a")) subject.on(.Next("b")) - writeSequenceToConsole("2", sequence: subject) + writeSequenceToConsole("2", sequence: subject).addDisposableTo(disposeBag) subject.on(.Next("c")) subject.on(.Next("d")) } @@ -68,11 +71,13 @@ When an observer subscribes to a `BehaviorSubject`, it begins by emitting the it ![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/behaviorsubject_error.png) */ example("BehaviorSubject") { + let disposeBag = DisposeBag() + let subject = BehaviorSubject(value: "z") - writeSequenceToConsole("1", sequence: subject) + writeSequenceToConsole("1", sequence: subject).addDisposableTo(disposeBag) subject.on(.Next("a")) subject.on(.Next("b")) - writeSequenceToConsole("2", sequence: subject) + writeSequenceToConsole("2", sequence: subject).addDisposableTo(disposeBag) subject.on(.Next("c")) subject.on(.Next("d")) subject.on(.Completed) @@ -86,11 +91,12 @@ example("BehaviorSubject") { */ example("Variable") { + let disposeBag = DisposeBag() let variable = Variable("z") - writeSequenceToConsole("1", sequence: variable) + writeSequenceToConsole("1", sequence: variable).addDisposableTo(disposeBag) variable.value = "a" variable.value = "b" - writeSequenceToConsole("2", sequence: variable) + writeSequenceToConsole("2", sequence: variable).addDisposableTo(disposeBag) variable.value = "c" variable.value = "d" } diff --git a/Rx.playground/contents.xcplayground b/Rx.playground/contents.xcplayground index a05e9e9e..0d291cc1 100644 --- a/Rx.playground/contents.xcplayground +++ b/Rx.playground/contents.xcplayground @@ -1,5 +1,5 @@ - + From b3a66aa45fbee33cb459d782c658b58aed4661f9 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 15 Nov 2015 21:15:23 +0100 Subject: [PATCH 192/210] `Merge` operator family polish. --- Rx.xcodeproj/project.pbxproj | 10 - RxExample/RxExample.xcodeproj/project.pbxproj | 4 - .../Observables/Implementations/FlatMap.swift | 192 -------- .../Observables/Implementations/Merge.swift | 429 ++++++++++++------ RxSwift/Observables/Observable+Multiple.swift | 4 +- ...Observable+StandardSequenceOperators.swift | 26 +- .../Tests/Observable+TimeTest.swift | 2 +- 7 files changed, 303 insertions(+), 364 deletions(-) delete mode 100644 RxSwift/Observables/Implementations/FlatMap.swift diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index 3e51a1d3..b8ad069d 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -86,8 +86,6 @@ C8093D1C1B8A72BE0088E94D /* Do.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C791B8A72BE0088E94D /* Do.swift */; }; C8093D1D1B8A72BE0088E94D /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C7A1B8A72BE0088E94D /* Filter.swift */; }; C8093D1E1B8A72BE0088E94D /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C7A1B8A72BE0088E94D /* Filter.swift */; }; - C8093D1F1B8A72BE0088E94D /* FlatMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C7B1B8A72BE0088E94D /* FlatMap.swift */; }; - C8093D201B8A72BE0088E94D /* FlatMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C7B1B8A72BE0088E94D /* FlatMap.swift */; }; C8093D211B8A72BE0088E94D /* Map.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C7C1B8A72BE0088E94D /* Map.swift */; }; C8093D221B8A72BE0088E94D /* Map.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C7C1B8A72BE0088E94D /* Map.swift */; }; C8093D231B8A72BE0088E94D /* Merge.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C7D1B8A72BE0088E94D /* Merge.swift */; }; @@ -462,7 +460,6 @@ C8F0BFCB1BBBFB8B001B112F /* Scan.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C871B8A72BE0088E94D /* Scan.swift */; }; C8F0BFCC1BBBFB8B001B112F /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C511B8A72BE0088E94D /* Queue.swift */; }; C8F0BFCD1BBBFB8B001B112F /* AnonymousObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C3DA111B93A3EA004D233E /* AnonymousObservable.swift */; }; - C8F0BFCE1BBBFB8B001B112F /* FlatMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C7B1B8A72BE0088E94D /* FlatMap.swift */; }; C8F0BFCF1BBBFB8B001B112F /* DisposeBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C591B8A72BE0088E94D /* DisposeBase.swift */; }; C8F0BFD01BBBFB8B001B112F /* AnonymousDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C541B8A72BE0088E94D /* AnonymousDisposable.swift */; }; C8F0BFD11BBBFB8B001B112F /* ConcurrentDispatchQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CB51B8A72BE0088E94D /* ConcurrentDispatchQueueScheduler.swift */; }; @@ -735,7 +732,6 @@ D2EBEB0B1BB9B6C1003A27DC /* Empty.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C3DA051B9393AC004D233E /* Empty.swift */; }; D2EBEB0C1BB9B6C1003A27DC /* FailWith.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C3DA081B93941E004D233E /* FailWith.swift */; }; D2EBEB0D1BB9B6C1003A27DC /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C7A1B8A72BE0088E94D /* Filter.swift */; }; - D2EBEB0E1BB9B6C1003A27DC /* FlatMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C7B1B8A72BE0088E94D /* FlatMap.swift */; }; D2EBEB0F1BB9B6C1003A27DC /* Generate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84B38ED1BA433CD001B7D88 /* Generate.swift */; }; D2EBEB101BB9B6C1003A27DC /* Just.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C3DA021B9390C4004D233E /* Just.swift */; }; D2EBEB111BB9B6C1003A27DC /* Map.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093C7C1B8A72BE0088E94D /* Map.swift */; }; @@ -903,7 +899,6 @@ C8093C781B8A72BE0088E94D /* DistinctUntilChanged.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DistinctUntilChanged.swift; sourceTree = ""; }; C8093C791B8A72BE0088E94D /* Do.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Do.swift; sourceTree = ""; }; C8093C7A1B8A72BE0088E94D /* Filter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Filter.swift; sourceTree = ""; }; - C8093C7B1B8A72BE0088E94D /* FlatMap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlatMap.swift; sourceTree = ""; }; C8093C7C1B8A72BE0088E94D /* Map.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Map.swift; sourceTree = ""; }; C8093C7D1B8A72BE0088E94D /* Merge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Merge.swift; sourceTree = ""; }; C8093C7E1B8A72BE0088E94D /* Multicast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Multicast.swift; sourceTree = ""; }; @@ -1294,7 +1289,6 @@ C8C3DA051B9393AC004D233E /* Empty.swift */, C8C3DA081B93941E004D233E /* FailWith.swift */, C8093C7A1B8A72BE0088E94D /* Filter.swift */, - C8093C7B1B8A72BE0088E94D /* FlatMap.swift */, C84B38ED1BA433CD001B7D88 /* Generate.swift */, C8C3DA021B9390C4004D233E /* Just.swift */, C8093C7C1B8A72BE0088E94D /* Map.swift */, @@ -2295,7 +2289,6 @@ CB30D9EA1BF0E3500084C1C0 /* SingleAsync.swift in Sources */, C8093CD21B8A72BE0088E94D /* Queue.swift in Sources */, C8C3DA131B93A3EA004D233E /* AnonymousObservable.swift in Sources */, - C8093D201B8A72BE0088E94D /* FlatMap.swift in Sources */, C8093CE01B8A72BE0088E94D /* DisposeBase.swift in Sources */, C8093CD61B8A72BE0088E94D /* AnonymousDisposable.swift in Sources */, C8093D901B8A72BE0088E94D /* ConcurrentDispatchQueueScheduler.swift in Sources */, @@ -2434,7 +2427,6 @@ CB30D9E91BF0E3500084C1C0 /* SingleAsync.swift in Sources */, C8093CD11B8A72BE0088E94D /* Queue.swift in Sources */, C8C3DA121B93A3EA004D233E /* AnonymousObservable.swift in Sources */, - C8093D1F1B8A72BE0088E94D /* FlatMap.swift in Sources */, C8093CDF1B8A72BE0088E94D /* DisposeBase.swift in Sources */, C8093CD51B8A72BE0088E94D /* AnonymousDisposable.swift in Sources */, C8093D8F1B8A72BE0088E94D /* ConcurrentDispatchQueueScheduler.swift in Sources */, @@ -2573,7 +2565,6 @@ CB30D9EC1BF0E3500084C1C0 /* SingleAsync.swift in Sources */, C8F0BFCC1BBBFB8B001B112F /* Queue.swift in Sources */, C8F0BFCD1BBBFB8B001B112F /* AnonymousObservable.swift in Sources */, - C8F0BFCE1BBBFB8B001B112F /* FlatMap.swift in Sources */, C8F0BFCF1BBBFB8B001B112F /* DisposeBase.swift in Sources */, C8F0BFD01BBBFB8B001B112F /* AnonymousDisposable.swift in Sources */, C8F0BFD11BBBFB8B001B112F /* ConcurrentDispatchQueueScheduler.swift in Sources */, @@ -2909,7 +2900,6 @@ D2EBEB3F1BB9B6D8003A27DC /* CurrentThreadScheduler.swift in Sources */, C84CC55F1BDD010800E06A64 /* SynchronizedUnsubscribeType.swift in Sources */, D2EBEAF21BB9B6AE003A27DC /* CompositeDisposable.swift in Sources */, - D2EBEB0E1BB9B6C1003A27DC /* FlatMap.swift in Sources */, D2EBEB171BB9B6C1003A27DC /* Producer.swift in Sources */, D2EBEAF91BB9B6B2003A27DC /* SerialDisposable.swift in Sources */, C84CC5691BDD08A500E06A64 /* SubscriptionDisposable.swift in Sources */, diff --git a/RxExample/RxExample.xcodeproj/project.pbxproj b/RxExample/RxExample.xcodeproj/project.pbxproj index e8c6fa8c..617d5884 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -196,7 +196,6 @@ C89464CA1BC6C2B00055219D /* Empty.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464591BC6C2B00055219D /* Empty.swift */; }; C89464CB1BC6C2B00055219D /* FailWith.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894645A1BC6C2B00055219D /* FailWith.swift */; }; C89464CC1BC6C2B00055219D /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894645B1BC6C2B00055219D /* Filter.swift */; }; - C89464CD1BC6C2B00055219D /* FlatMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894645C1BC6C2B00055219D /* FlatMap.swift */; }; C89464CE1BC6C2B00055219D /* Generate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894645D1BC6C2B00055219D /* Generate.swift */; }; C89464CF1BC6C2B00055219D /* Just.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894645E1BC6C2B00055219D /* Just.swift */; }; C89464D01BC6C2B00055219D /* Map.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894645F1BC6C2B00055219D /* Map.swift */; }; @@ -600,7 +599,6 @@ C89464591BC6C2B00055219D /* Empty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Empty.swift; sourceTree = ""; }; C894645A1BC6C2B00055219D /* FailWith.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FailWith.swift; sourceTree = ""; }; C894645B1BC6C2B00055219D /* Filter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Filter.swift; sourceTree = ""; }; - C894645C1BC6C2B00055219D /* FlatMap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlatMap.swift; sourceTree = ""; }; C894645D1BC6C2B00055219D /* Generate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Generate.swift; sourceTree = ""; }; C894645E1BC6C2B00055219D /* Just.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Just.swift; sourceTree = ""; }; C894645F1BC6C2B00055219D /* Map.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Map.swift; sourceTree = ""; }; @@ -1192,7 +1190,6 @@ C89464591BC6C2B00055219D /* Empty.swift */, C894645A1BC6C2B00055219D /* FailWith.swift */, C894645B1BC6C2B00055219D /* Filter.swift */, - C894645C1BC6C2B00055219D /* FlatMap.swift */, C894645D1BC6C2B00055219D /* Generate.swift */, C894645E1BC6C2B00055219D /* Just.swift */, C894645F1BC6C2B00055219D /* Map.swift */, @@ -1764,7 +1761,6 @@ C87335781BF7CC0C00E536E6 /* ObservableConvertibleType+Differentiator.swift in Sources */, C8297E371B6CF905000589EA /* RxCollectionViewSectionedDataSource.swift in Sources */, B1B7C3D01BE006870076934E /* TakeLast.swift in Sources */, - C89464CD1BC6C2B00055219D /* FlatMap.swift in Sources */, C8297E381B6CF905000589EA /* Changeset.swift in Sources */, C8297E391B6CF905000589EA /* CollectionViewImageCell.swift in Sources */, C894649E1BC6C2B00055219D /* Cancelable.swift in Sources */, diff --git a/RxSwift/Observables/Implementations/FlatMap.swift b/RxSwift/Observables/Implementations/FlatMap.swift deleted file mode 100644 index 60ceca12..00000000 --- a/RxSwift/Observables/Implementations/FlatMap.swift +++ /dev/null @@ -1,192 +0,0 @@ -// -// FlatMap.swift -// RxSwift -// -// Created by Krunoslav Zaher on 6/11/15. -// Copyright (c) 2015 Krunoslav Zaher. All rights reserved. -// - -import Foundation - -// It's value is one because initial source subscription is always in CompositeDisposable -let FlatMapNoIterators = 1 - -class FlatMapSinkIter : ObserverType { - typealias Parent = FlatMapSink - typealias DisposeKey = CompositeDisposable.DisposeKey - typealias E = O.E - - private let _parent: Parent - private let _disposeKey: DisposeKey - - init(parent: Parent, disposeKey: DisposeKey) { - _parent = parent - _disposeKey = disposeKey - } - - func on(event: Event) { - switch event { - case .Next(let value): - _parent._lock.lock(); defer { _parent._lock.unlock() } // lock { - _parent.forwardOn(.Next(value)) - // } - case .Error(let error): - _parent._lock.lock(); defer { _parent._lock.unlock() } // lock { - _parent.forwardOn(.Error(error)) - _parent.dispose() - // } - case .Completed: - _parent._group.removeDisposable(_disposeKey) - // If this has returned true that means that `Completed` should be sent. - // In case there is a race who will sent first completed, - // lock will sort it out. When first Completed message is sent - // it will set observer to nil, and thus prevent further complete messages - // to be sent, and thus preserving the sequence grammar. - if _parent._stopped && _parent._group.count == FlatMapNoIterators { - _parent._lock.lock(); defer { _parent._lock.unlock() } // lock { - _parent.forwardOn(.Completed) - _parent.dispose() - // } - } - } - } -} - -class FlatMapSink : Sink, ObserverType { - typealias ResultType = O.E - typealias Element = SourceType - typealias Parent = FlatMap - - private let _parent: Parent - - private let _lock = NSRecursiveLock() - - // state - private let _group = CompositeDisposable() - private let _sourceSubscription = SingleAssignmentDisposable() - - private var _stopped = false - - init(parent: Parent, observer: O) { - _parent = parent - super.init(observer: observer) - } - - func performMap(element: SourceType) throws -> S { - abstractMethod() - } - - func on(event: Event) { - switch event { - case .Next(let element): - if _parent._onlyFirst && _group.count > FlatMapNoIterators { - return - } - do { - let value = try performMap(element) - subscribeInner(value.asObservable()) - } - catch let e { - forwardOn(.Error(e)) - dispose() - } - case .Error(let error): - _lock.lock(); defer { _lock.unlock() } // lock { - forwardOn(.Error(error)) - dispose() - // } - case .Completed: - _lock.lock(); defer { _lock.unlock() } // lock { - _stopped = true - if _group.count == FlatMapNoIterators { - forwardOn(.Completed) - dispose() - } - else { - _sourceSubscription.dispose() - } - //} - } - } - - func subscribeInner(source: Observable) { - let iterDisposable = SingleAssignmentDisposable() - if let disposeKey = _group.addDisposable(iterDisposable) { - let iter = FlatMapSinkIter(parent: self, disposeKey: disposeKey) - let subscription = source.subscribe(iter) - iterDisposable.disposable = subscription - } - } - - func run() -> Disposable { - _group.addDisposable(_sourceSubscription) - - let subscription = _parent._source.subscribe(self) - _sourceSubscription.disposable = subscription - - return _group - } -} - -class FlatMapSink1 : FlatMapSink { - override init(parent: Parent, observer: O) { - super.init(parent: parent, observer: observer) - } - - override func performMap(element: SourceType) throws -> S { - return try _parent._selector1!(element) - } -} - -class FlatMapSink2 : FlatMapSink { - private var _index = 0 - - override init(parent: Parent, observer: O) { - super.init(parent: parent, observer: observer) - } - - override func performMap(element: SourceType) throws -> S { - return try _parent._selector2!(element, try incrementChecked(&_index)) - } -} - -class FlatMap: Producer { - typealias Selector1 = (SourceType) throws -> S - typealias Selector2 = (SourceType, Int) throws -> S - - private let _source: Observable - - private let _selector1: Selector1? - private let _selector2: Selector2? - - private let _onlyFirst: Bool - - init(source: Observable, selector: Selector1, onlyFirst:Bool = false) { - _source = source - _selector1 = selector - _onlyFirst = onlyFirst - _selector2 = nil - } - - init(source: Observable, selector: Selector2) { - _source = source - _selector2 = selector - _onlyFirst = false - _selector1 = nil - } - - override func run(observer: O) -> Disposable { - let sink: FlatMapSink - if let _ = _selector1 { - sink = FlatMapSink1(parent: self, observer: observer) - } - else { - sink = FlatMapSink2(parent: self, observer: observer) - } - - let subscription = sink.run() - sink.disposable = subscription - - return sink - } -} \ No newline at end of file diff --git a/RxSwift/Observables/Implementations/Merge.swift b/RxSwift/Observables/Implementations/Merge.swift index d2d97e5b..8d902def 100644 --- a/RxSwift/Observables/Implementations/Merge.swift +++ b/RxSwift/Observables/Implementations/Merge.swift @@ -8,130 +8,15 @@ import Foundation -// sequential +// MARK: Limited concurrency version -class MergeSinkIter +class MergeLimitedSinkIter : ObserverType , LockOwnerType , SynchronizedOnType { typealias E = O.E typealias DisposeKey = Bag.KeyType - typealias Parent = MergeSink - - private let _parent: Parent - private let _disposeKey: DisposeKey - - var _lock: NSRecursiveLock { - return _parent._lock - } - - init(parent: Parent, disposeKey: DisposeKey) { - _parent = parent - _disposeKey = disposeKey - } - - func on(event: Event) { - synchronizedOn(event) - } - - func _synchronized_on(event: Event) { - switch event { - case .Next: - _parent.forwardOn(event) - case .Error: - _parent.forwardOn(event) - _parent.dispose() - case .Completed: - _parent._group.removeDisposable(_disposeKey) - - if _parent._stopped && _parent._group.count == 1 { - _parent.forwardOn(.Completed) - _parent.dispose() - } - } - } -} - -class MergeSink - : Sink - , ObserverType - , LockOwnerType - , SynchronizedOnType { - typealias E = S - typealias Parent = Merge - - private let _parent: Parent - - let _lock = NSRecursiveLock() - - // state - private var _stopped = false - - private let _group = CompositeDisposable() - private let _sourceSubscription = SingleAssignmentDisposable() - - init(parent: Parent, observer: O) { - _parent = parent - - super.init(observer: observer) - } - - func run() -> Disposable { - _group.addDisposable(_sourceSubscription) - - let disposable = _parent._sources.subscribe(self) - _sourceSubscription.disposable = disposable - - return _group - } - - func on(event: Event) { - if case .Next(let value) = event { - let innerSubscription = SingleAssignmentDisposable() - let maybeKey = _group.addDisposable(innerSubscription) - - if let key = maybeKey { - let observer = MergeSinkIter(parent: self, disposeKey: key) - let disposable = value.asObservable().subscribe(observer) - innerSubscription.disposable = disposable - } - - return - } - - synchronizedOn(event) - } - - func _synchronized_on(event: Event) { - switch event { - case .Next: - rxFatalError("Next should have been handled") - case .Error(let error): - forwardOn(.Error(error)) - dispose() - case .Completed: - _stopped = true - - if _group.count == 1 { - forwardOn(.Completed) - dispose() - } - else { - _sourceSubscription.dispose() - } - } - } -} - -// concurrent - -class MergeConcurrentSinkIter - : ObserverType - , LockOwnerType - , SynchronizedOnType { - typealias E = O.E - typealias DisposeKey = Bag.KeyType - typealias Parent = MergeConcurrentSink + typealias Parent = MergeLimitedSink private let _parent: Parent private let _disposeKey: DisposeKey @@ -175,17 +60,17 @@ class MergeConcurrentSinkIter +class MergeLimitedSink : Sink , ObserverType , LockOwnerType , SynchronizedOnType { typealias E = S - typealias Parent = Merge typealias QueueType = Queue - - private let _parent: Parent - + + private let _sources: Observable + private let _maxConcurrent: Int + let _lock = NSRecursiveLock() // state @@ -196,8 +81,9 @@ class MergeConcurrentSink, maxConcurrent: Int, observer: O) { + _sources = sources + _maxConcurrent = maxConcurrent _group.addDisposable(_sourceSubscription) super.init(observer: observer) @@ -206,7 +92,7 @@ class MergeConcurrentSink Disposable { _group.addDisposable(_sourceSubscription) - let disposable = _parent._sources.subscribe(self) + let disposable = _sources.subscribe(self) _sourceSubscription.disposable = disposable return _group } @@ -217,7 +103,7 @@ class MergeConcurrentSink : Producer { - private let _sources: Observable +class MergeLimited : Producer { + private let _source: Observable private let _maxConcurrent: Int - init(sources: Observable, maxConcurrent: Int) { - _sources = sources + init(source: Observable, maxConcurrent: Int) { + _source = source _maxConcurrent = maxConcurrent } override func run(observer: O) -> Disposable { - if _maxConcurrent > 0 { - let sink = MergeConcurrentSink(parent: self, observer: observer) - sink.disposable = sink.run() - return sink - } - else { - let sink = MergeSink(parent: self, observer: observer) - sink.disposable = sink.run() - return sink + let sink = MergeLimitedSink(sources: _source, maxConcurrent: _maxConcurrent, observer: observer) + sink.disposable = sink.run() + return sink + } +} + +// MARK: Merge + +final class MergeBasicSink : MergeSink { + override init(source: Observable, observer: O) { + super.init(source: source, observer: observer) + } + + override func performMap(element: S) throws -> S { + return element + } +} + +// MARK: flatMap + +final class FlatMapSink : MergeSink { + typealias Selector = (SourceType) throws -> S + + private let _selector: Selector + + init(source: Observable, selector: Selector, observer: O) { + _selector = selector + super.init(source: source, observer: observer) + } + + override func performMap(element: SourceType) throws -> S { + return try _selector(element) + } +} + +final class FlatMapWithIndexSink : MergeSink { + typealias Selector = (SourceType, Int) throws -> S + + private var _index = 0 + private let _selector: Selector + + init(source: Observable, selector: Selector, observer: O) { + _selector = selector + super.init(source: source, observer: observer) + } + + override func performMap(element: SourceType) throws -> S { + return try _selector(element, try incrementChecked(&_index)) + } +} + +// MARK: FlatMapFirst + +final class FlatMapFirstSink : MergeSink { + typealias Selector = (SourceType) throws -> S + + private let _selector: Selector + + override var subscribeNext: Bool { + return _group.count == MergeNoIterators + } + + init(source: Observable, selector: Selector, observer: O) { + _selector = selector + super.init(source: source, observer: observer) + } + + override func performMap(element: SourceType) throws -> S { + return try _selector(element) + } +} + +// It's value is one because initial source subscription is always in CompositeDisposable +private let MergeNoIterators = 1 + +class MergeSinkIter : ObserverType { + typealias Parent = MergeSink + typealias DisposeKey = CompositeDisposable.DisposeKey + typealias E = O.E + + private let _parent: Parent + private let _disposeKey: DisposeKey + + init(parent: Parent, disposeKey: DisposeKey) { + _parent = parent + _disposeKey = disposeKey + } + + func on(event: Event) { + switch event { + case .Next(let value): + _parent._lock.lock(); defer { _parent._lock.unlock() } // lock { + _parent.forwardOn(.Next(value)) + // } + case .Error(let error): + _parent._lock.lock(); defer { _parent._lock.unlock() } // lock { + _parent.forwardOn(.Error(error)) + _parent.dispose() + // } + case .Completed: + _parent._group.removeDisposable(_disposeKey) + // If this has returned true that means that `Completed` should be sent. + // In case there is a race who will sent first completed, + // lock will sort it out. When first Completed message is sent + // it will set observer to nil, and thus prevent further complete messages + // to be sent, and thus preserving the sequence grammar. + if _parent._stopped && _parent._group.count == MergeNoIterators { + _parent._lock.lock(); defer { _parent._lock.unlock() } // lock { + _parent.forwardOn(.Completed) + _parent.dispose() + // } + } } } -} \ No newline at end of file +} + + +class MergeSink + : Sink + , ObserverType { + typealias ResultType = O.E + typealias Element = SourceType + + private let _lock = NSRecursiveLock() + private let _source: Observable + + private var subscribeNext: Bool { + return true + } + + // state + private let _group = CompositeDisposable() + private let _sourceSubscription = SingleAssignmentDisposable() + + private var _stopped = false + + init(source: Observable, observer: O) { + _source = source + super.init(observer: observer) + } + + func performMap(element: SourceType) throws -> S { + abstractMethod() + } + + func on(event: Event) { + switch event { + case .Next(let element): + if !subscribeNext { + return + } + do { + let value = try performMap(element) + subscribeInner(value.asObservable()) + } + catch let e { + forwardOn(.Error(e)) + dispose() + } + case .Error(let error): + _lock.lock(); defer { _lock.unlock() } // lock { + forwardOn(.Error(error)) + dispose() + // } + case .Completed: + _lock.lock(); defer { _lock.unlock() } // lock { + _stopped = true + if _group.count == MergeNoIterators { + forwardOn(.Completed) + dispose() + } + else { + _sourceSubscription.dispose() + } + //} + } + } + + func subscribeInner(source: Observable) { + let iterDisposable = SingleAssignmentDisposable() + if let disposeKey = _group.addDisposable(iterDisposable) { + let iter = MergeSinkIter(parent: self, disposeKey: disposeKey) + let subscription = source.subscribe(iter) + iterDisposable.disposable = subscription + } + } + + func run() -> Disposable { + _group.addDisposable(_sourceSubscription) + + let subscription = _source.subscribe(self) + _sourceSubscription.disposable = subscription + + return _group + } +} + +// MARK: Producers + +final class FlatMap: Producer { + typealias Selector = (SourceType) throws -> S + + private let _source: Observable + + private let _selector: Selector + + init(source: Observable, selector: Selector) { + _source = source + _selector = selector + } + + override func run(observer: O) -> Disposable { + let sink = FlatMapSink(source: _source, selector: _selector, observer: observer) + sink.disposable = sink.run() + return sink + } +} + +final class FlatMapWithIndex: Producer { + typealias Selector = (SourceType, Int) throws -> S + + private let _source: Observable + + private let _selector: Selector + + init(source: Observable, selector: Selector) { + _source = source + _selector = selector + } + + override func run(observer: O) -> Disposable { + let sink = FlatMapWithIndexSink(source: _source, selector: _selector, observer: observer) + sink.disposable = sink.run() + return sink + } + +} + +final class FlatMapFirst: Producer { + typealias Selector = (SourceType) throws -> S + + private let _source: Observable + + private let _selector: Selector + + init(source: Observable, selector: Selector) { + _source = source + _selector = selector + } + + override func run(observer: O) -> Disposable { + let sink = FlatMapFirstSink(source: _source, selector: _selector, observer: observer) + sink.disposable = sink.run() + return sink + } +} + +final class Merge : Producer { + private let _source: Observable + + init(source: Observable) { + _source = source + } + + override func run(observer: O) -> Disposable { + let sink = MergeBasicSink(source: _source, observer: observer) + sink.disposable = sink.run() + return sink + } +} + diff --git a/RxSwift/Observables/Observable+Multiple.swift b/RxSwift/Observables/Observable+Multiple.swift index abc3179b..ded6dd23 100644 --- a/RxSwift/Observables/Observable+Multiple.swift +++ b/RxSwift/Observables/Observable+Multiple.swift @@ -113,7 +113,7 @@ extension ObservableType where E : ObservableConvertibleType { */ @warn_unused_result(message="http://git.io/rxs.uo") public func merge() -> Observable { - return Merge(sources: self.asObservable(), maxConcurrent: 0) + return Merge(source: self.asObservable()) } /** @@ -125,7 +125,7 @@ extension ObservableType where E : ObservableConvertibleType { @warn_unused_result(message="http://git.io/rxs.uo") public func merge(maxConcurrent maxConcurrent: Int) -> Observable { - return Merge(sources: self.asObservable(), maxConcurrent: maxConcurrent) + return MergeLimited(source: self.asObservable(), maxConcurrent: maxConcurrent) } } diff --git a/RxSwift/Observables/Observable+StandardSequenceOperators.swift b/RxSwift/Observables/Observable+StandardSequenceOperators.swift index ad73a342..df29c77b 100644 --- a/RxSwift/Observables/Observable+StandardSequenceOperators.swift +++ b/RxSwift/Observables/Observable+StandardSequenceOperators.swift @@ -21,7 +21,7 @@ extension ObservableType { @warn_unused_result(message="http://git.io/rxs.uo") public func filter(predicate: (E) throws -> Bool) -> Observable { - return Filter(source: self.asObservable(), predicate: predicate) + return Filter(source: asObservable(), predicate: predicate) } } @@ -73,7 +73,7 @@ extension ObservableType { return empty() } else { - return TakeCount(source: self.asObservable(), count: count) + return TakeCount(source: asObservable(), count: count) } } } @@ -93,7 +93,7 @@ extension ObservableType { @warn_unused_result(message="http://git.io/rxs.uo") public func takeLast(count: Int) -> Observable { - return TakeLast(source: self.asObservable(), count: count) + return TakeLast(source: asObservable(), count: count) } } @@ -111,7 +111,7 @@ extension ObservableType { @warn_unused_result(message="http://git.io/rxs.uo") public func skip(count: Int) -> Observable { - return SkipCount(source: self.asObservable(), count: count) + return SkipCount(source: asObservable(), count: count) } } @@ -127,7 +127,7 @@ extension ObservableType { */ @warn_unused_result(message="http://git.io/rxs.uo") public func skipWhile(predicate: (E) throws -> Bool) -> Observable { - return SkipWhile(source: self.asObservable(), predicate: predicate) + return SkipWhile(source: asObservable(), predicate: predicate) } /** @@ -139,7 +139,7 @@ extension ObservableType { */ @warn_unused_result(message="http://git.io/rxs.uo") public func skipWhileWithIndex(predicate: (E, Int) throws -> Bool) -> Observable { - return SkipWhile(source: self.asObservable(), predicate: predicate) + return SkipWhile(source: asObservable(), predicate: predicate) } } @@ -168,7 +168,7 @@ extension ObservableType { @warn_unused_result(message="http://git.io/rxs.uo") public func mapWithIndex(selector: (E, Int) throws -> R) -> Observable { - return MapWithIndex(source: self.asObservable(), selector: selector) + return MapWithIndex(source: asObservable(), selector: selector) } } @@ -185,7 +185,7 @@ extension ObservableType { @warn_unused_result(message="http://git.io/rxs.uo") public func flatMap(selector: (E) throws -> O) -> Observable { - return FlatMap(source: self.asObservable(), selector: selector) + return FlatMap(source: asObservable(), selector: selector) } /** @@ -197,7 +197,7 @@ extension ObservableType { @warn_unused_result(message="http://git.io/rxs.uo") public func flatMapWithIndex(selector: (E, Int) throws -> O) -> Observable { - return FlatMap(source: self.asObservable(), selector: selector) + return FlatMapWithIndex(source: asObservable(), selector: selector) } } @@ -214,7 +214,7 @@ extension ObservableType { @warn_unused_result(message="http://git.io/rxs.uo") public func flatMapFirst(selector: (E) throws -> O) -> Observable { - return FlatMap(source: asObservable(), selector: selector, onlyFirst: true) + return FlatMapFirst(source: asObservable(), selector: selector) } } @@ -231,7 +231,7 @@ extension ObservableType { @warn_unused_result(message="http://git.io/rxs.uo") public func elementAt(index: Int) -> Observable { - return ElementAt(source: self.asObservable(), index: index, throwOnEmpty: true) + return ElementAt(source: asObservable(), index: index, throwOnEmpty: true) } } @@ -248,7 +248,7 @@ extension ObservableType { @warn_unused_result(message="http://git.io/rxs.uo") public func single() -> Observable { - return SingleAsync(source: self.asObservable()) + return SingleAsync(source: asObservable()) } /** @@ -261,7 +261,7 @@ extension ObservableType { @warn_unused_result(message="http://git.io/rxs.uo") public func single(predicate: (E) throws -> Bool) -> Observable { - return SingleAsync(source: self.asObservable(), predicate: predicate) + return SingleAsync(source: asObservable(), predicate: predicate) } } \ No newline at end of file diff --git a/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift b/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift index 5b7ce9cb..092f1c6a 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift @@ -261,7 +261,7 @@ extension ObservableTimeTest { let start = NSDate() - let a = try! [just(0), never()].asObservable().concat() + let a = try! [just(0), never()].toObservable().concat() .throttle(2.0, scheduler) .toBlocking() .first() From 69d34d58c4f8b4884791da87aea8e429b8e65495 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 15 Nov 2015 21:24:45 +0100 Subject: [PATCH 193/210] Improves data locality for `Map` and `Filter`. --- .../Observables/Implementations/Filter.swift | 11 ++++---- RxSwift/Observables/Implementations/Map.swift | 27 ++++++++++--------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/RxSwift/Observables/Implementations/Filter.swift b/RxSwift/Observables/Implementations/Filter.swift index 3f3f0133..937771b9 100644 --- a/RxSwift/Observables/Implementations/Filter.swift +++ b/RxSwift/Observables/Implementations/Filter.swift @@ -9,14 +9,15 @@ import Foundation class FilterSink: Sink, ObserverType { + typealias Predicate = (Element) throws -> Bool typealias Element = O.E typealias Parent = Filter - private let _parent: Parent + private let _predicate: Predicate - init(parent: Parent, observer: O) { - _parent = parent + init(predicate: Predicate, observer: O) { + _predicate = predicate super.init(observer: observer) } @@ -24,7 +25,7 @@ class FilterSink: Sink, ObserverType { switch event { case .Next(let value): do { - let satisfies = try _parent._predicate(value) + let satisfies = try _predicate(value) if satisfies { forwardOn(.Next(value)) } @@ -52,7 +53,7 @@ class Filter : Producer { } override func run(observer: O) -> Disposable { - let sink = FilterSink(parent: self, observer: observer) + let sink = FilterSink(predicate: _predicate, observer: observer) sink.disposable = _source.subscribe(sink) return sink } diff --git a/RxSwift/Observables/Implementations/Map.swift b/RxSwift/Observables/Implementations/Map.swift index 035b7273..fbf85f98 100644 --- a/RxSwift/Observables/Implementations/Map.swift +++ b/RxSwift/Observables/Implementations/Map.swift @@ -9,14 +9,15 @@ import Foundation class MapSink : Sink, ObserverType { + typealias Selector = (SourceType) throws -> ResultType + typealias ResultType = O.E typealias Element = SourceType - typealias Parent = Map + + private let _selector: Selector - private let _parent: Parent - - init(parent: Parent, observer: O) { - _parent = parent + init(selector: Selector, observer: O) { + _selector = selector super.init(observer: observer) } @@ -24,7 +25,7 @@ class MapSink : Sink, ObserverType { switch event { case .Next(let element): do { - let mappedElement = try _parent._selector(element) + let mappedElement = try _selector(element) forwardOn(.Next(mappedElement)) } catch let e { @@ -42,16 +43,18 @@ class MapSink : Sink, ObserverType { } class MapWithIndexSink : Sink, ObserverType { + typealias Selector = (SourceType, Int) throws -> ResultType + typealias ResultType = O.E typealias Element = SourceType typealias Parent = MapWithIndex - private let _parent: Parent + private let _selector: Selector private var _index = 0 - init(parent: Parent, observer: O) { - _parent = parent + init(selector: Selector, observer: O) { + _selector = selector super.init(observer: observer) } @@ -59,7 +62,7 @@ class MapWithIndexSink : Sink, ObserverType { switch event { case .Next(let element): do { - let mappedElement = try _parent._selector(element, try incrementChecked(&_index)) + let mappedElement = try _selector(element, try incrementChecked(&_index)) forwardOn(.Next(mappedElement)) } catch let e { @@ -89,7 +92,7 @@ class MapWithIndex : Producer { } override func run(observer: O) -> Disposable { - let sink = MapWithIndexSink(parent: self, observer: observer) + let sink = MapWithIndexSink(selector: _selector, observer: observer) sink.disposable = _source.subscribe(sink) return sink } @@ -124,7 +127,7 @@ class Map: Producer { } override func run(observer: O) -> Disposable { - let sink = MapSink(parent: self, observer: observer) + let sink = MapSink(selector: _selector, observer: observer) sink.disposable = _source.subscribe(sink) return sink } From 99ae8dc30b1d99afc493d440c36c09b2f84628a7 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 15 Nov 2015 21:53:15 +0100 Subject: [PATCH 194/210] Simplifies `Merge` operators. --- .../Observables/Implementations/Merge.swift | 56 +++++++++---------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/RxSwift/Observables/Implementations/Merge.swift b/RxSwift/Observables/Implementations/Merge.swift index 8d902def..01301df4 100644 --- a/RxSwift/Observables/Implementations/Merge.swift +++ b/RxSwift/Observables/Implementations/Merge.swift @@ -68,7 +68,6 @@ class MergeLimitedSink - private let _sources: Observable private let _maxConcurrent: Int let _lock = NSRecursiveLock() @@ -81,18 +80,17 @@ class MergeLimitedSink, maxConcurrent: Int, observer: O) { - _sources = sources + init(maxConcurrent: Int, observer: O) { _maxConcurrent = maxConcurrent _group.addDisposable(_sourceSubscription) super.init(observer: observer) } - func run() -> Disposable { + func run(source: Observable) -> Disposable { _group.addDisposable(_sourceSubscription) - let disposable = _sources.subscribe(self) + let disposable = source.subscribe(self) _sourceSubscription.disposable = disposable return _group } @@ -157,8 +155,8 @@ class MergeLimited : Producer { } override func run(observer: O) -> Disposable { - let sink = MergeLimitedSink(sources: _source, maxConcurrent: _maxConcurrent, observer: observer) - sink.disposable = sink.run() + let sink = MergeLimitedSink(maxConcurrent: _maxConcurrent, observer: observer) + sink.disposable = sink.run(_source) return sink } } @@ -166,8 +164,8 @@ class MergeLimited : Producer { // MARK: Merge final class MergeBasicSink : MergeSink { - override init(source: Observable, observer: O) { - super.init(source: source, observer: observer) + override init(observer: O) { + super.init(observer: observer) } override func performMap(element: S) throws -> S { @@ -182,9 +180,9 @@ final class FlatMapSink, selector: Selector, observer: O) { + init(selector: Selector, observer: O) { _selector = selector - super.init(source: source, observer: observer) + super.init(observer: observer) } override func performMap(element: SourceType) throws -> S { @@ -198,9 +196,9 @@ final class FlatMapWithIndexSink, selector: Selector, observer: O) { + init(selector: Selector, observer: O) { _selector = selector - super.init(source: source, observer: observer) + super.init(observer: observer) } override func performMap(element: SourceType) throws -> S { @@ -219,9 +217,9 @@ final class FlatMapFirstSink, selector: Selector, observer: O) { + init(selector: Selector, observer: O) { _selector = selector - super.init(source: source, observer: observer) + super.init(observer: observer) } override func performMap(element: SourceType) throws -> S { @@ -281,7 +279,6 @@ class MergeSink private var subscribeNext: Bool { return true @@ -292,12 +289,11 @@ class MergeSink, observer: O) { - _source = source + + override init(observer: O) { super.init(observer: observer) } - + func performMap(element: SourceType) throws -> S { abstractMethod() } @@ -344,10 +340,10 @@ class MergeSink Disposable { + func run(source: Observable) -> Disposable { _group.addDisposable(_sourceSubscription) - let subscription = _source.subscribe(self) + let subscription = source.subscribe(self) _sourceSubscription.disposable = subscription return _group @@ -369,8 +365,8 @@ final class FlatMap: Producer { } override func run(observer: O) -> Disposable { - let sink = FlatMapSink(source: _source, selector: _selector, observer: observer) - sink.disposable = sink.run() + let sink = FlatMapSink(selector: _selector, observer: observer) + sink.disposable = sink.run(_source) return sink } } @@ -388,8 +384,8 @@ final class FlatMapWithIndex: Producer } override func run(observer: O) -> Disposable { - let sink = FlatMapWithIndexSink(source: _source, selector: _selector, observer: observer) - sink.disposable = sink.run() + let sink = FlatMapWithIndexSink(selector: _selector, observer: observer) + sink.disposable = sink.run(_source) return sink } @@ -408,8 +404,8 @@ final class FlatMapFirst: Producer(observer: O) -> Disposable { - let sink = FlatMapFirstSink(source: _source, selector: _selector, observer: observer) - sink.disposable = sink.run() + let sink = FlatMapFirstSink(selector: _selector, observer: observer) + sink.disposable = sink.run(_source) return sink } } @@ -422,8 +418,8 @@ final class Merge : Producer { } override func run(observer: O) -> Disposable { - let sink = MergeBasicSink(source: _source, observer: observer) - sink.disposable = sink.run() + let sink = MergeBasicSink(observer: observer) + sink.disposable = sink.run(_source) return sink } } From beaa045340878cf3bccd63d791a735f1633df5b4 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 15 Nov 2015 22:51:43 +0100 Subject: [PATCH 195/210] Adds `FlatMapLatest`. --- .../Observables/Implementations/Switch.swift | 112 ++++-- RxSwift/Observables/Observable+Multiple.swift | 24 +- ...Observable+StandardSequenceOperators.swift | 20 ++ .../Tests/Observable+MultipleTest.swift | 324 +++++++++++++++++- 4 files changed, 422 insertions(+), 58 deletions(-) diff --git a/RxSwift/Observables/Implementations/Switch.swift b/RxSwift/Observables/Implementations/Switch.swift index e6048649..700b1016 100644 --- a/RxSwift/Observables/Implementations/Switch.swift +++ b/RxSwift/Observables/Implementations/Switch.swift @@ -8,18 +8,16 @@ import Foundation -class SwitchSink +class SwitchSink : Sink , ObserverType , LockOwnerType , SynchronizedOnType { - typealias E = S - typealias Parent = Switch + typealias E = SourceType private let _subscriptions: SingleAssignmentDisposable = SingleAssignmentDisposable() private let _innerSubscription: SerialDisposable = SerialDisposable() - private let _parent: Parent - + let _lock = NSRecursiveLock() // state @@ -27,14 +25,12 @@ class SwitchSink private var _latest = 0 private var _hasLatest = false - init(parent: Parent, observer: O) { - _parent = parent - + override init(observer: O) { super.init(observer: observer) } - func run() -> Disposable { - let subscription = _parent._sources.subscribe(self) + func run(source: Observable) -> Disposable { + let subscription = source.subscribe(self) _subscriptions.disposable = subscription return StableCompositeDisposable.create(_subscriptions, _innerSubscription) } @@ -43,19 +39,30 @@ class SwitchSink synchronizedOn(event) } + func performMap(element: SourceType) throws -> S { + abstractMethod() + } + func _synchronized_on(event: Event) { switch event { - case .Next(let observable): - _hasLatest = true - _latest = _latest &+ 1 - let latest = _latest + case .Next(let element): + do { + let observable = try performMap(element).asObservable() + _hasLatest = true + _latest = _latest &+ 1 + let latest = _latest - let d = SingleAssignmentDisposable() - _innerSubscription.disposable = d - - let observer = SwitchSinkIter(parent: self, id: latest, _self: d) - let disposable = observable.asObservable().subscribe(observer) - d.disposable = disposable + let d = SingleAssignmentDisposable() + _innerSubscription.disposable = d + + let observer = SwitchSinkIter(parent: self, id: latest, _self: d) + let disposable = observable.subscribe(observer) + d.disposable = disposable + } + catch let error { + forwardOn(.Error(error)) + dispose() + } case .Error(let error): forwardOn(.Error(error)) dispose() @@ -72,12 +79,12 @@ class SwitchSink } } -class SwitchSinkIter +class SwitchSinkIter : ObserverType , LockOwnerType , SynchronizedOnType { - typealias E = O.E - typealias Parent = SwitchSink + typealias E = S.E + typealias Parent = SwitchSink private let _parent: Parent private let _id: Int @@ -124,16 +131,63 @@ class SwitchSinkIter : Producer { - private let _sources: Observable +// MARK: Specializations + +final class SwitchIdentitySink : SwitchSink { + override init(observer: O) { + super.init(observer: observer) + } + + override func performMap(element: S) throws -> S { + return element + } +} + +final class MapSwitchSink : SwitchSink { + typealias Selector = SourceType throws -> S + + private let _selector: Selector + + init(selector: Selector, observer: O) { + _selector = selector + super.init(observer: observer) + } + + override func performMap(element: SourceType) throws -> S { + return try _selector(element) + } +} + +// MARK: Producers + +final class Switch : Producer { + private let _source: Observable - init(sources: Observable) { - _sources = sources + init(source: Observable) { + _source = source } override func run(observer: O) -> Disposable { - let sink = SwitchSink(parent: self, observer: observer) - sink.disposable = sink.run() + let sink = SwitchIdentitySink(observer: observer) + sink.disposable = sink.run(_source) + return sink + } +} + +final class FlatMapLatest : Producer { + typealias Selector = SourceType throws -> S + + private let _source: Observable + private let _selector: Selector + + init(source: Observable, selector: Selector) { + _source = source + _selector = selector + } + + override func run(observer: O) -> Disposable { + let sink = MapSwitchSink(selector: _selector, observer: observer) + sink.disposable = sink.run(_source) return sink } } \ No newline at end of file diff --git a/RxSwift/Observables/Observable+Multiple.swift b/RxSwift/Observables/Observable+Multiple.swift index ded6dd23..a425d747 100644 --- a/RxSwift/Observables/Observable+Multiple.swift +++ b/RxSwift/Observables/Observable+Multiple.swift @@ -55,7 +55,7 @@ extension ObservableType where E : ObservableConvertibleType { */ @warn_unused_result(message="http://git.io/rxs.uo") public func switchLatest() -> Observable { - return Switch(sources: self.asObservable()) + return Switch(source: asObservable()) } } @@ -71,7 +71,7 @@ extension ObservableType { */ @warn_unused_result(message="http://git.io/rxs.uo") public func concat(second: O) -> Observable { - return [self.asObservable(), second.asObservable()].concat() + return [asObservable(), second.asObservable()].concat() } } @@ -98,7 +98,7 @@ extension ObservableType where E : ObservableConvertibleType { */ @warn_unused_result(message="http://git.io/rxs.uo") public func concat() -> Observable { - return self.merge(maxConcurrent: 1) + return merge(maxConcurrent: 1) } } @@ -113,7 +113,7 @@ extension ObservableType where E : ObservableConvertibleType { */ @warn_unused_result(message="http://git.io/rxs.uo") public func merge() -> Observable { - return Merge(source: self.asObservable()) + return Merge(source: asObservable()) } /** @@ -125,7 +125,7 @@ extension ObservableType where E : ObservableConvertibleType { @warn_unused_result(message="http://git.io/rxs.uo") public func merge(maxConcurrent maxConcurrent: Int) -> Observable { - return MergeLimited(source: self.asObservable(), maxConcurrent: maxConcurrent) + return MergeLimited(source: asObservable(), maxConcurrent: maxConcurrent) } } @@ -142,7 +142,7 @@ extension ObservableType { @warn_unused_result(message="http://git.io/rxs.uo") public func catchError(handler: (ErrorType) throws -> Observable) -> Observable { - return Catch(source: self.asObservable(), handler: handler) + return Catch(source: asObservable(), handler: handler) } /** @@ -154,7 +154,7 @@ extension ObservableType { @warn_unused_result(message="http://git.io/rxs.uo") public func catchErrorJustReturn(element: E) -> Observable { - return Catch(source: self.asObservable(), handler: { _ in just(element) }) + return Catch(source: asObservable(), handler: { _ in just(element) }) } } @@ -185,7 +185,7 @@ extension ObservableType { @warn_unused_result(message="http://git.io/rxs.uo") public func takeUntil(other: O) -> Observable { - return TakeUntil(source: self.asObservable(), other: other.asObservable()) + return TakeUntil(source: asObservable(), other: other.asObservable()) } } @@ -202,7 +202,7 @@ extension ObservableType { @warn_unused_result(message="http://git.io/rxs.uo") public func skipUntil(other: O) -> Observable { - return SkipUntil(source: self.asObservable(), other: other.asObservable()) + return SkipUntil(source: asObservable(), other: other.asObservable()) } } @@ -220,7 +220,7 @@ extension ObservableType { public func amb (right: O2) -> Observable { - return Amb(left: self.asObservable(), right: right.asObservable()) + return Amb(left: asObservable(), right: right.asObservable()) } } @@ -252,7 +252,7 @@ extension ObservableType { - returns: An observable sequence containing the result of combining each element of the self with the latest element from the second source, if any, using the specified result selector function. */ public func withLatestFrom(second: SecondO, resultSelector: (E, SecondO.E) throws -> ResultType) -> Observable { - return WithLatestFrom(first: self.asObservable(), second: second.asObservable(), resultSelector: resultSelector) + return WithLatestFrom(first: asObservable(), second: second.asObservable(), resultSelector: resultSelector) } /** @@ -262,6 +262,6 @@ extension ObservableType { - returns: An observable sequence containing the result of combining each element of the self with the latest element from the second source, if any, using the specified result selector function. */ public func withLatestFrom(second: SecondO) -> Observable { - return WithLatestFrom(first: self.asObservable(), second: second.asObservable(), resultSelector: { $1 }) + return WithLatestFrom(first: asObservable(), second: second.asObservable(), resultSelector: { $1 }) } } diff --git a/RxSwift/Observables/Observable+StandardSequenceOperators.swift b/RxSwift/Observables/Observable+StandardSequenceOperators.swift index df29c77b..755a2007 100644 --- a/RxSwift/Observables/Observable+StandardSequenceOperators.swift +++ b/RxSwift/Observables/Observable+StandardSequenceOperators.swift @@ -218,6 +218,26 @@ extension ObservableType { } } +// MARK: flatMapLatest + +extension ObservableType { + /** + Projects each element of an observable sequence into a new sequence of observable sequences and then + transforms an observable sequence of observable sequences into an observable sequence producing values only from the most recent observable sequence. + + It is a combination of `map` + `switchLatest` operator + + - parameter selector: A transform function to apply to each element. + - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source producing an + Observable of Observable sequences and that at any point in time produces the elements of the most recent inner observable sequence that has been received. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func flatMapLatest(selector: (E) throws -> O) + -> Observable { + return FlatMapLatest(source: asObservable(), selector: selector) + } +} + // MARK: elementAt extension ObservableType { diff --git a/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift b/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift index 80f38fdb..349a44cf 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift @@ -20,7 +20,7 @@ class ObservableMultipleTest : RxTest { } } -// onError +// MARK: catchError extension ObservableMultipleTest { func testCatch_ErrorSpecific_Caught() { let scheduler = TestScheduler(initialClock: 0) @@ -387,7 +387,7 @@ extension ObservableMultipleTest { } } -// switch +// MARK: switch extension ObservableMultipleTest { func testSwitch_Data() { @@ -616,8 +616,306 @@ extension ObservableMultipleTest { } } -// concat +// MARK: flatMapLatest +extension ObservableMultipleTest { + func testFlatMapLatest_Data() { + let scheduler = TestScheduler(initialClock: 0) + + let ys1 = scheduler.createColdObservable([ + next(10, 101), + next(20, 102), + next(110, 103), + next(120, 104), + next(210, 105), + next(220, 106), + completed(230) + ]) + + let ys2 = scheduler.createColdObservable([ + next(10, 201), + next(20, 202), + next(30, 203), + next(40, 204), + completed(50) + ]) + + let ys3 = scheduler.createColdObservable([ + next(10, 301), + next(20, 302), + next(30, 303), + next(40, 304), + completed(150) + ]) + + let observables = [ys1, ys2, ys3] + + let xSequence: [Recorded] = [ + next(300, 0), + next(400, 1), + next(500, 2), + completed(600) + ] + + let xs = scheduler.createHotObservable(xSequence) + + let res = scheduler.start { + xs.flatMapLatest { observables[$0] } + } + + let correct = [ + next(310, 101), + next(320, 102), + next(410, 201), + next(420, 202), + next(430, 203), + next(440, 204), + next(510, 301), + next(520, 302), + next(530, 303), + next(540, 304), + completed(650) + ] + + XCTAssertEqual(res.messages, correct) + + let subscriptions = [ + Subscription(200, 600) + ] + + XCTAssertEqual(xs.subscriptions, subscriptions) + + let ys1Subscriptions = [ + Subscription(300, 400) + ] + + XCTAssertEqual(ys1.subscriptions, ys1Subscriptions) + + let y2Subscriptions = [ + Subscription(400, 450) + ] + + XCTAssertEqual(ys2.subscriptions, y2Subscriptions) + + let y3Subscriptions = [ + Subscription(500, 650) + ] + + XCTAssertEqual(ys3.subscriptions, y3Subscriptions) + } + + func testFlatMapLatest_InnerThrows() { + let scheduler = TestScheduler(initialClock: 0) + + let ys1 = scheduler.createColdObservable([ + next(10, 101), + next(20, 102), + next(110, 103), + next(120, 104), + next(210, 105), + next(220, 106), + completed(230) + ]) + + let ys2 = scheduler.createColdObservable([ + next(10, 201), + next(20, 202), + next(30, 203), + next(40, 204), + error(50, testError) + ]) + + let ys3 = scheduler.createColdObservable([ + next(10, 301), + next(20, 302), + next(30, 303), + next(40, 304), + completed(150) + ]) + + let observables = [ys1, ys2, ys3] + + let xSequence: [Recorded] = [ + next(300, 0), + next(400, 1), + next(500, 2), + completed(600) + ] + + let xs = scheduler.createHotObservable(xSequence) + + let res = scheduler.start { + xs.flatMapLatest { observables[$0] } + } + + let correct = [ + next(310, 101), + next(320, 102), + next(410, 201), + next(420, 202), + next(430, 203), + next(440, 204), + error(450, testError), + ] + + XCTAssertEqual(res.messages, correct) + + let subscriptions = [ + Subscription(200, 450) + ] + + XCTAssertEqual(xs.subscriptions, subscriptions) + + let ys1Subscriptions = [ + Subscription(300, 400) + ] + + XCTAssertEqual(ys1.subscriptions, ys1Subscriptions) + + let y2Subscriptions = [ + Subscription(400, 450) + ] + + XCTAssertEqual(ys2.subscriptions, y2Subscriptions) + + let y3Subscriptions: [Subscription] = [ + ] + + XCTAssertEqual(ys3.subscriptions, y3Subscriptions) + } + + func testFlatMapLatest_OuterThrows() { + let scheduler = TestScheduler(initialClock: 0) + + let ys1 = scheduler.createColdObservable([ + next(10, 101), + next(20, 102), + next(110, 103), + next(120, 104), + next(210, 105), + next(220, 106), + completed(230) + ]) + + let ys2 = scheduler.createColdObservable([ + next(10, 201), + next(20, 202), + next(30, 203), + next(40, 204), + completed(50) + ]) + + let observables = [ys1, ys2] + + let xSequence: [Recorded] = [ + next(300, 0), + next(400, 1), + error(500, testError) + ] + + let xs = scheduler.createHotObservable(xSequence) + + let res = scheduler.start { + xs.flatMapLatest { observables[$0] } + } + + let correct = [ + next(310, 101), + next(320, 102), + next(410, 201), + next(420, 202), + next(430, 203), + next(440, 204), + error(500, testError), + ] + + XCTAssertEqual(res.messages, correct) + + let subscriptions = [ + Subscription(200, 500) + ] + + XCTAssertEqual(xs.subscriptions, subscriptions) + + let ys1Subscriptions = [ + Subscription(300, 400) + ] + + XCTAssertEqual(ys1.subscriptions, ys1Subscriptions) + + let y2Subscriptions = [ + Subscription(400, 450) + ] + + XCTAssertEqual(ys2.subscriptions, y2Subscriptions) + } + + func testFlatMapLatest_SelectorThrows() { + let scheduler = TestScheduler(initialClock: 0) + + let ys1 = scheduler.createColdObservable([ + next(10, 101), + next(20, 102), + next(110, 103), + next(120, 104), + next(210, 105), + next(220, 106), + completed(230) + ]) + + let ys2 = scheduler.createColdObservable([ + next(10, 201), + next(20, 202), + next(30, 203), + next(40, 204), + completed(50) + ]) + + let observables = [ys1, ys2] + + let xSequence: [Recorded] = [ + next(300, 0), + next(400, 1) + ] + + let xs = scheduler.createHotObservable(xSequence) + + let res = scheduler.start { + xs.flatMapLatest { x throws -> ColdObservable in + if x < 1 { + return observables[x] + } + else { + throw testError + } + } + } + + let correct = [ + next(310, 101), + next(320, 102), + error(400, testError), + ] + + XCTAssertEqual(res.messages, correct) + + let subscriptions = [ + Subscription(200, 400) + ] + + XCTAssertEqual(xs.subscriptions, subscriptions) + + let ys1Subscriptions = [ + Subscription(300, 400) + ] + + XCTAssertEqual(ys1.subscriptions, ys1Subscriptions) + + XCTAssertEqual(ys2.subscriptions, []) + } +} + +// MARK: concat extension ObservableMultipleTest { func testConcat_DefaultScheduler() { var sum = 0 @@ -1205,8 +1503,7 @@ extension ObservableMultipleTest { } } -// merge - +// MARK: merge extension ObservableMultipleTest { func testMerge_DeadlockSimple() { var nEvents = 0 @@ -2153,8 +2450,7 @@ extension ObservableMultipleTest { } } -// combine latest - +// MARK: combine latest extension ObservableMultipleTest { func testCombineLatest_DeadlockSimple() { var nEvents = 0 @@ -2214,8 +2510,7 @@ extension ObservableMultipleTest { } } -// takeUntil - +// MARK: takeUntil extension ObservableMultipleTest { func testTakeUntil_Preempt_SomeData_Next() { let scheduler = TestScheduler(initialClock: 0) @@ -2596,8 +2891,7 @@ extension ObservableMultipleTest { } -// amb - +// MARK: amb extension ObservableMultipleTest { func testAmb_Never2() { @@ -2853,8 +3147,7 @@ extension ObservableMultipleTest { } } -// combineLatest + CollectionType - +// MARK: combineLatest + CollectionType extension ObservableMultipleTest { func testCombineLatest_NeverN() { let scheduler = TestScheduler(initialClock: 0) @@ -3444,8 +3737,7 @@ extension ObservableMultipleTest { } } -// zip + CollectionType - +// MARK: zip + CollectionType extension ObservableMultipleTest { func testZip_NAry_symmetric() { let scheduler = TestScheduler(initialClock: 0) @@ -3601,7 +3893,6 @@ extension ObservableMultipleTest { // MARK: skipUntil - extension ObservableMultipleTest { func testSkipUntil_SomeData_Next() { let scheduler = TestScheduler(initialClock: 0) @@ -3952,7 +4243,6 @@ extension ObservableMultipleTest { // MARK: withLatestFrom - extension ObservableMultipleTest { func testWithLatestFrom_Simple1() { From 65cc061b1743c85b91c441f6849ab20712e05bec Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 15 Nov 2015 22:52:16 +0100 Subject: [PATCH 196/210] Changes `NopDisposable` to be struct. --- RxSwift/Disposables/NopDisposable.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RxSwift/Disposables/NopDisposable.swift b/RxSwift/Disposables/NopDisposable.swift index 189424d7..3ff4e54a 100644 --- a/RxSwift/Disposables/NopDisposable.swift +++ b/RxSwift/Disposables/NopDisposable.swift @@ -13,7 +13,7 @@ Represents a disposable that does nothing on disposal. Nop = No Operation */ -public class NopDisposable : Disposable { +public struct NopDisposable : Disposable { /** Singleton instance of `NopDisposable`. From 7d3387989d19d9dfcaf725c8f101116fbb2b9e5f Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 15 Nov 2015 23:21:39 +0100 Subject: [PATCH 197/210] Adds `flatMapLatest` to `Driver` unit. --- README.md | 3 +- .../CocoaUnits/Driver/Driver+Operators.swift | 21 ++++++ .../Implementations/KVOObservable.swift | 3 +- ...tHubSearchRepositoriesViewController.swift | 3 +- .../Views/GitHubSignupViewController.swift | 6 +- .../ViewModels/SearchViewModel.swift | 3 +- RxTests/RxSwiftTests/Tests/Driver+Test.swift | 74 +++++++++++++++---- 7 files changed, 87 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 1a8b0aa6..d83b05e8 100644 --- a/README.md +++ b/README.md @@ -190,13 +190,12 @@ Writing all of this and properly testing it would be tedious. This is that same searchTextField.rx_text .throttle(0.3, MainScheduler.sharedInstance) .distinctUntilChanged() - .map { query in + .flatMapLatest { query in API.getSearchResults(query) .retry(3) .startWith([]) // clears results on new search term .catchErrorJustReturn([]) } - .switchLatest() .subscribeNext { results in // bind to ui } diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift index 12b2bf14..fff37635 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift @@ -63,6 +63,27 @@ extension DriverConvertibleType where E : DriverConvertibleType { } } +extension DriverConvertibleType { + /** + Projects each element of an observable sequence into a new sequence of observable sequences and then + transforms an observable sequence of observable sequences into an observable sequence producing values only from the most recent observable sequence. + + It is a combination of `map` + `switchLatest` operator + + - parameter selector: A transform function to apply to each element. + - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source producing an + Observable of Observable sequences and that at any point in time produces the elements of the most recent inner observable sequence that has been received. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func flatMapLatest(selector: (E) -> Driver) + -> Driver { + let source: Observable = self + .asObservable() + .flatMapLatest(selector) + return Driver(source) + } +} + extension DriverConvertibleType { /** diff --git a/RxCocoa/Common/Observables/Implementations/KVOObservable.swift b/RxCocoa/Common/Observables/Implementations/KVOObservable.swift index 74744549..b0dc053f 100644 --- a/RxCocoa/Common/Observables/Implementations/KVOObservable.swift +++ b/RxCocoa/Common/Observables/Implementations/KVOObservable.swift @@ -112,7 +112,7 @@ func observeWeaklyKeyPathFor( // KVO recursion for value changes return propertyObservable - .map { (nextTarget: AnyObject?) -> Observable in + .flatMapLatest { (nextTarget: AnyObject?) -> Observable in if nextTarget == nil { return just(nil) } @@ -142,7 +142,6 @@ func observeWeaklyKeyPathFor( return nextElementsObservable } } - .switchLatest() } #endif diff --git a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift index 328fd062..c41bfc90 100644 --- a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift +++ b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift @@ -83,7 +83,7 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat let searchResult = searchBar.rx_text.asDriver() .throttle(0.3, $.mainScheduler) .distinctUntilChanged() - .map { query -> Driver in + .flatMapLatest { query -> Driver in if query.isEmpty { return Drive.just(RepositoriesState.empty) } else { @@ -91,7 +91,6 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat .asDriver(onErrorJustReturn: RepositoriesState.empty) } } - .switchLatest() searchResult .map { $0.serviceState } diff --git a/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift b/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift index 98b65cf8..249338e2 100644 --- a/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift +++ b/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift @@ -169,10 +169,9 @@ class GitHubSignupViewController : ViewController { let signupSampler = signupOutlet.rx_tap let usernameValidation = username - .map { username in + .flatMapLatest { username in return validationService.validateUsername(username) } - .switchLatest() .shareReplay(1) let passwordValidation = password @@ -188,10 +187,9 @@ class GitHubSignupViewController : ViewController { let signingProcess = combineLatest(username, password) { ($0, $1) } .sampleLatest(signupSampler) - .map { (username, password) in + .flatMapLatest { (username, password) in return API.signup(username, password: password) } - .switchLatest() .startWith(SignupState.InitialState) .shareReplay(1) diff --git a/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchViewModel.swift b/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchViewModel.swift index 4f611abd..2f0f097a 100644 --- a/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchViewModel.swift +++ b/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchViewModel.swift @@ -31,14 +31,13 @@ class SearchViewModel { self.rows = searchText .throttle(0.3, $.mainScheduler) .distinctUntilChanged() - .map { query in + .flatMapLatest { query in API.getSearchResults(query) .retry(3) .retryOnBecomesReachable([], reachabilityService: ReachabilityService.sharedReachabilityService) .startWith([]) // clears results on new search term .asDriver(onErrorJustReturn: []) } - .switchLatest() .map { results in results.map { SearchResultViewModel( diff --git a/RxTests/RxSwiftTests/Tests/Driver+Test.swift b/RxTests/RxSwiftTests/Tests/Driver+Test.swift index 045a2314..bbd41db7 100644 --- a/RxTests/RxSwiftTests/Tests/Driver+Test.swift +++ b/RxTests/RxSwiftTests/Tests/Driver+Test.swift @@ -89,7 +89,7 @@ extension DriverTest { } } -// conversions +// MARK: conversions extension DriverTest { func testAsDriver_onErrorJustReturn() { let hotObservable = BackgroundThreadPrimitiveHotObservable() @@ -145,7 +145,7 @@ extension DriverTest { } } -// map +// MARK: map extension DriverTest { func testAsDriver_map() { let hotObservable = BackgroundThreadPrimitiveHotObservable() @@ -169,7 +169,7 @@ extension DriverTest { } -// filter +// MARK: filter extension DriverTest { func testAsDriver_filter() { let hotObservable = BackgroundThreadPrimitiveHotObservable() @@ -193,7 +193,7 @@ extension DriverTest { } -// switch latest +// MARK: switch latest extension DriverTest { func testAsDriver_switchLatest() { let hotObservable = BackgroundThreadPrimitiveHotObservable>() @@ -232,7 +232,53 @@ extension DriverTest { } } -// doOn +// MARK: flatMapLatest +extension DriverTest { + func testAsDriver_flatMapLatest() { + let hotObservable = BackgroundThreadPrimitiveHotObservable() + let hotObservable1 = MainThreadPrimitiveHotObservable() + let hotObservable2 = MainThreadPrimitiveHotObservable() + let errorHotObservable = MainThreadPrimitiveHotObservable() + + let drivers: [Driver] = [ + hotObservable1.asDriver(onErrorJustReturn: -2), + hotObservable2.asDriver(onErrorJustReturn: -3), + errorHotObservable.asDriver(onErrorJustReturn: -4), + ] + + let driver = hotObservable.asDriver(onErrorJustReturn: 2).flatMapLatest { drivers[$0] } + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(0)) + + hotObservable1.on(.Next(1)) + hotObservable1.on(.Next(2)) + hotObservable1.on(.Error(testError)) + + hotObservable.on(.Next(1)) + + hotObservable2.on(.Next(10)) + hotObservable2.on(.Next(11)) + hotObservable2.on(.Error(testError)) + + hotObservable.on(.Error(testError)) + + errorHotObservable.on(.Completed) + hotObservable.on(.Completed) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [ + 1, 2, -2, + 10, 11, -3 + ]) + } +} + +// MARK: doOn extension DriverTest { func testAsDriver_doOn() { let hotObservable = BackgroundThreadPrimitiveHotObservable() @@ -261,7 +307,7 @@ extension DriverTest { } } -// distinct until change +// MARK: distinct until change extension DriverTest { func testAsDriver_distinctUntilChanged1() { let hotObservable = BackgroundThreadPrimitiveHotObservable() @@ -342,7 +388,7 @@ extension DriverTest { } -// flat map +// MARK: flat map extension DriverTest { func testAsDriver_flatMap() { let hotObservable = BackgroundThreadPrimitiveHotObservable() @@ -366,7 +412,7 @@ extension DriverTest { } -// merge +// MARK: merge extension DriverTest { func testAsDriver_merge() { let hotObservable = BackgroundThreadPrimitiveHotObservable() @@ -409,7 +455,7 @@ extension DriverTest { } } -// debounce +// MARK: debounce extension DriverTest { func testAsDriver_debounce() { let hotObservable = BackgroundThreadPrimitiveHotObservable() @@ -445,7 +491,7 @@ extension DriverTest { } -// scan +// MARK: scan extension DriverTest { func testAsDriver_scan() { let hotObservable = BackgroundThreadPrimitiveHotObservable() @@ -469,7 +515,7 @@ extension DriverTest { } -// concat +// MARK: concat extension DriverTest { func testAsDriver_concat() { let hotObservable1 = BackgroundThreadPrimitiveHotObservable() @@ -498,7 +544,7 @@ extension DriverTest { } } -// combine latest +// MARK: combine latest extension DriverTest { func testAsDriver_combineLatest_array() { let hotObservable1 = BackgroundThreadPrimitiveHotObservable() @@ -553,7 +599,7 @@ extension DriverTest { } } -// zip +// MARK: zip extension DriverTest { func testAsDriver_zip_array() { let hotObservable1 = BackgroundThreadPrimitiveHotObservable() @@ -608,7 +654,7 @@ extension DriverTest { } } -// withLatestFrom +// MARK: withLatestFrom extension DriverTest { func testAsDriver_withLatestFrom() { let hotObservable1 = BackgroundThreadPrimitiveHotObservable() From db478b1b9a0e766fc3155d423f1008a249c05e51 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Sun, 15 Nov 2015 23:59:17 +0100 Subject: [PATCH 198/210] Updates documentation. --- Documentation/GettingStarted.md | 72 +++++++------------ Documentation/Schedulers.md | 15 +++- .../ConcurrentDispatchQueueScheduler.swift | 2 +- .../Schedulers/OperationQueueScheduler.swift | 2 +- 4 files changed, 42 insertions(+), 49 deletions(-) diff --git a/Documentation/GettingStarted.md b/Documentation/GettingStarted.md index 74b5d768..29ef8e82 100644 --- a/Documentation/GettingStarted.md +++ b/Documentation/GettingStarted.md @@ -92,7 +92,7 @@ protocol ObserverType { **When sequence sends `Complete` or `Error` event all internal resources that compute sequence elements will be freed.** -**To cancel production of sequence elements and free resources immediatelly, call `dispose` on returned subscription.** +**To cancel production of sequence elements and free resources immediately, call `dispose` on returned subscription.** If a sequence terminates in finite time, not calling `dispose` or not using `addDisposableTo(disposeBag)` won't cause any permanent resource leaks, but those resources will be used until sequence completes in some way (finishes producing elements or error happens). @@ -745,7 +745,7 @@ If you are unsure how exactly some of the operators work, [playgrounds](../Rx.pl The are two error mechanisms. -### Anynchronous error handling mechanism in observables +### Asynchronous error handling mechanism in observables Error handling is pretty straightforward. If one sequence terminates with error, then all of the dependent sequences will terminate with error. It's usual short circuit logic. @@ -753,32 +753,6 @@ You can recover from failure of observable by using `catch` operator. There are There is also `retry` operator that enables retries in case of errored sequence. -### Synchronous error handling - -Unfortunately Swift doesn't have a concept of exceptions or some kind of built in error monad so this project introduces `RxResult` enum. -It is Swift port of Scala [`Try`](http://www.scala-lang.org/api/2.10.2/index.html#scala.util.Try) type. It is also similar to Haskell [`Either`](https://hackage.haskell.org/package/category-extras-0.52.0/docs/Control-Monad-Either.html) monad. - -**This will be replaced in Swift 2.0 with try/throws** - -```swift -public enum RxResult { - case Success(ResultType) - case Error(ErrorType) -} -``` - -To enable writing more readable code, a few `Result` operators are introduced - -```swift -result1.flatMap { okValue in // success handling block - // executed on success - return ? -}.recoverWith { error in // error handling block - // executed on error - return ? -} -``` - ## Debugging Compile Errors When writing elegant RxSwift/RxCocoa code, you are probably relying heavily on compiler to deduce types of `Observable`s. This is one of the reasons why Swift is awesome, but it can also be frustrating sometimes. @@ -972,32 +946,37 @@ There are two built in ways this library supports KVO. ```swift // KVO extension NSObject { - public func rx_observe(keyPath: String, retainSelf: Bool = true) -> Observable {} + public func rx_observe(type: E.Type, _ keyPath: String, options: NSKeyValueObservingOptions, retainSelf: Bool = true) -> Observable {} } #if !DISABLE_SWIZZLING // KVO extension NSObject { - public func rx_observeWeakly(keyPath: String) -> Observable {} + public func rx_observeWeakly(type: E.Type, _ keyPath: String, options: NSKeyValueObservingOptions) -> Observable {} } #endif ``` -**If Swift compiler doesn't have a way to deduce observed type (return Observable type), it will report error about function not existing.** +Example how to observe frame of `UIView`. -Here are some ways you can give him hints about observed type: +**WARNING: UIKit isn't KVO compliant, but this will work.** ```swift -view.rx_observe("frame") as Observable +view + .rx_observe(CGRect.self, "frame") + .subscribeNext { frame in + ... + } ``` or ```swift -view.rx_observe("frame") - .map { (rect: CGRect?) in - // - } +view + .rx_observeWeakly(CGRect.self, "frame") + .subscribeNext { frame in + ... + } ``` ### `rx_observe` @@ -1011,12 +990,12 @@ view.rx_observe("frame") E.g. ```swift -self.rx_observe("view.frame", retainSelf = false) as Observable +self.rx_observe(CGRect.self, "view.frame", retainSelf: false) ``` ### `rx_observeWeakly` -`rx_observeWeakly` has somewhat worse performance because it has to handle object deallocation in case of weak references. +`rx_observeWeakly` has somewhat slower then `rx_observe` because it has to handle object deallocation in case of weak references. It can be used in all cases where `rx_observe` can be used and additionally @@ -1026,16 +1005,18 @@ It can be used in all cases where `rx_observe` can be used and additionally E.g. ```swift -someSuspiciousViewController.rx_observeWeakly("behavingOk") as Observable +someSuspiciousViewController.rx_observeWeakly(Bool.self, "behavingOk") ``` ### Observing structs -KVO is an Objective-C mechanism so it relies heavily on `NSValue`. RxCocoa has additional specializations for `CGRect`, `CGSize` and `CGPoint` that make it convenient to observe those types. +KVO is an Objective-C mechanism so it relies heavily on `NSValue`. -When observing some other structures it is necessary to extract those structures from `NSValue` manually, or creating your own observable sequence specializations. +**RxCocoa has built in support for KVO observing of `CGRect`, `CGSize` and `CGPoint` structs.** -[Here](../RxCocoa/Common/Observables/NSObject+Rx+CoreGraphics.swift) are examples how to correctly extract structures from `NSValue`. +When observing some other structures it is necessary to extract those structures from `NSValue` manually. + +[Here](../RxCocoa/Common/KVORepresentable+CoreGraphics.swift) are examples how to extend KVO observing mechanism and `rx_observe*` methods for other structs by implementing `KVORepresentable` protocol. ## UI layer tips @@ -1069,13 +1050,12 @@ Let's say you have something like this: let searchResults = searchText .throttle(0.3, $.mainScheduler) .distinctUntilChanged - .map { query in + .flatMapLatest { query in API.getSearchResults(query) .retry(3) .startWith([]) // clears results on new search term .catchErrorJustReturn([]) } - .switchLatest() .shareReplay(1) // <- notice the `shareReplay` operator ``` @@ -1083,6 +1063,8 @@ What you usually want is to share search results once calculated. That is what ` **It is usually a good rule of thumb in the UI layer to add `shareReplay` at the end of transformation chain because you really want to share calculated results. You don't want to fire separate HTTP connections when binding `searchResults` to multiple UI elements.** +**Also take a look at `Driver` unit. It is designed to transparently wrap those `shareReply` calls, make sure elements are observed on main UI thread and that no error can be bound to UI.** + ## Making HTTP requests Making http requests is one of the first things people try. diff --git a/Documentation/Schedulers.md b/Documentation/Schedulers.md index 9dfc2adb..d9a3499b 100644 --- a/Documentation/Schedulers.md +++ b/Documentation/Schedulers.md @@ -94,6 +94,17 @@ Rx can use all types of schedulers, but it can also perform some additional opti These are currently supported schedulers +## CurrentThreadScheduler (Serial scheduler) + +Schedules units of work on the current thread. +This is the default scheduler for operators that generate elements. + +This scheduler is also sometimes called `trampoline scheduler`. + +If `CurrentThreadScheduler.instance.schedule(state) { }` is called for first time on some thread, scheduled action will be executed immediately and hidden queue will be created where all recursively scheduled actions will be temporarily enqueued. + +If some parent frame on call stack is already running `CurrentThreadScheduler.instance.schedule(state) { }`, scheduled action will be enqueued and executed when currently running action and all previously enqueued actions have finished executing. + ## MainScheduler (Serial scheduler) Abstracts work that needs to be performed on `MainThread`. In case `schedule` methods are called from main thread, it will perform action immediately without scheduling. @@ -110,12 +121,12 @@ Main scheduler is an instance of `SerialDispatchQueueScheduler`. ## ConcurrentDispatchQueueScheduler (Concurrent scheduler) -Abstracts the work that needs to be peformed on a specific `dispatch_queue_t`. You can also pass a serial dispatch queue, it shouldn't cause any problems. +Abstracts the work that needs to be performed on a specific `dispatch_queue_t`. You can also pass a serial dispatch queue, it shouldn't cause any problems. This scheduler is suitable when some work needs to be performed in background. ## OperationQueueScheduler (Concurrent scheduler) -Abstracts the work that needs to be peformed on a specific `NSOperationQueue`. +Abstracts the work that needs to be performed on a specific `NSOperationQueue`. This scheduler is suitable for cases when there is some bigger chunk of work that needs to be performed in background and you want to fine tune concurrent processing using `maxConcurrentOperationCount`. diff --git a/RxSwift/Schedulers/ConcurrentDispatchQueueScheduler.swift b/RxSwift/Schedulers/ConcurrentDispatchQueueScheduler.swift index 5c5e0b27..a880f00d 100644 --- a/RxSwift/Schedulers/ConcurrentDispatchQueueScheduler.swift +++ b/RxSwift/Schedulers/ConcurrentDispatchQueueScheduler.swift @@ -9,7 +9,7 @@ import Foundation /** -Abstracts the work that needs to be peformed on a specific `dispatch_queue_t`. You can also pass a serial dispatch queue, it shouldn't cause any problems. +Abstracts the work that needs to be performed on a specific `dispatch_queue_t`. You can also pass a serial dispatch queue, it shouldn't cause any problems. This scheduler is suitable when some work needs to be performed in background. */ diff --git a/RxSwift/Schedulers/OperationQueueScheduler.swift b/RxSwift/Schedulers/OperationQueueScheduler.swift index a2069d07..5ed07c7f 100644 --- a/RxSwift/Schedulers/OperationQueueScheduler.swift +++ b/RxSwift/Schedulers/OperationQueueScheduler.swift @@ -9,7 +9,7 @@ import Foundation /** -Abstracts the work that needs to be peformed on a specific `NSOperationQueue`. +Abstracts the work that needs to be performed on a specific `NSOperationQueue`. This scheduler is suitable for cases when there is some bigger chunk of work that needs to be performed in background and you want to fine tune concurrent processing using `maxConcurrentOperationCount`. */ From c515ba4b508721377d0a4fdd9933c21abbce9032 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 16 Nov 2015 00:11:30 +0100 Subject: [PATCH 199/210] Adds `CurrentThreadScheduler` to blocking operators. --- RxBlocking/BlockingObservable+Operators.swift | 8 ++++---- RxBlocking/RunLoopLock.swift | 15 ++++++++++++++- .../Tests/Observable+BlockingTest.swift | 6 +++--- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/RxBlocking/BlockingObservable+Operators.swift b/RxBlocking/BlockingObservable+Operators.swift index 095165a4..9430803e 100644 --- a/RxBlocking/BlockingObservable+Operators.swift +++ b/RxBlocking/BlockingObservable+Operators.swift @@ -177,6 +177,9 @@ extension BlockingObservable { lock.dispatch { d.disposable = self.source.subscribe { e in + if d.disposed { + return + } switch e { case .Next(let e): do { @@ -196,12 +199,10 @@ extension BlockingObservable { return case .Error(let e): error = e - lock.stop() case .Completed: - if error == nil && element == nil { + if element == nil { error = RxError.NoElements } - break } lock.stop() @@ -209,7 +210,6 @@ extension BlockingObservable { } lock.run() - d.dispose() if let error = error { diff --git a/RxBlocking/RunLoopLock.swift b/RxBlocking/RunLoopLock.swift index 5ea9ba73..94fefa57 100644 --- a/RxBlocking/RunLoopLock.swift +++ b/RxBlocking/RunLoopLock.swift @@ -7,6 +7,9 @@ // import Foundation +#if !RX_NO_MODULE + import RxSwift +#endif class RunLoopLock : NSObject { let currentRunLoop: CFRunLoopRef @@ -16,7 +19,17 @@ class RunLoopLock : NSObject { } func dispatch(action: () -> ()) { - CFRunLoopPerformBlock(currentRunLoop, kCFRunLoopDefaultMode, action) + CFRunLoopPerformBlock(currentRunLoop, kCFRunLoopDefaultMode) { + if CurrentThreadScheduler.isScheduleRequired { + CurrentThreadScheduler.instance.schedule(()) { _ in + action() + return NopDisposable.instance + } + } + else { + action() + } + } CFRunLoopWakeUp(currentRunLoop) } diff --git a/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift b/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift index 0fd88c2c..378ee1d7 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift @@ -225,7 +225,7 @@ extension ObservableBlockingTest { catch let e { XCTAssertTrue((e as! RxError)._code == RxError.MoreThanOneElement._code) } - XCTAssertEqual(predicateVals, [42, 43, 44, 45]) + XCTAssertEqual(predicateVals, [42, 43, 44]) } @@ -247,7 +247,7 @@ extension ObservableBlockingTest { func testSingle_predicate_throws() { var predicateVals = [Int]() do { - try (sequenceOf(42, 43, 44, 45) as Observable).toBlocking().single( { e in + try (sequenceOf(42, 43, 44, 45, scheduler: CurrentThreadScheduler.instance) as Observable).toBlocking().single( { e in predicateVals.append(e) if e < 43 { return false } throw testError @@ -257,7 +257,7 @@ extension ObservableBlockingTest { catch let e { XCTAssertTrue(e as NSError === testError) } - XCTAssertEqual(predicateVals, [42, 43, 44, 45]) + XCTAssertEqual(predicateVals, [42, 43]) } func testSingle_predicate_fail() { From af4f556ac86b56e2454c7a5183e16f1f0a28997b Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 16 Nov 2015 00:37:18 +0100 Subject: [PATCH 200/210] Updates documentation. --- CHANGELOG.md | 35 +++++++++++++++++++++++++++++++++++ Documentation/API.md | 13 +++++++++---- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63db7731..2c618c7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,41 @@ All notable changes to this project will be documented in this file. --- +## [2.0.0-beta.3](https://github.com/ReactiveX/RxSwift/releases/tag/2.0.0-beta.3) + +### Updated + +* Improves KVO mechanism. + * Type of observed object is now argument `view.rx_observe(CGRect.self, "frame")` + * Support for observing ObjC bridged enums and `RawRepresentable` protocol + * Support for easier extending of KVO using `KVORepresentable` protocol + * Deprecates KVO extensions that don't accept type as first argument in favor of ones that do. +* Adds `flatMapLatest` (also added to `Driver` unit). +* Adds `flatMapFirst` operator (also added to `Driver` unit). +* Adds `retryWhen` operator. +* Adds `window` operator. +* Adds `single` operator. +* Adds `single` (blocking version) operator. +* Adds `rx_primaryAction` on `UIButton` for `tvOS`. +* Transforms error types in `RxSwift`/`RxCocoa` projects from `NSError`s to Swift enum types. + * `RxError` + * `RxCocoaError` + * `RxCocoaURLError` + * ... +* Optimizes consecutive map operators. For example `map(validate1).map(validate2).map(parse)` is now internally optimized to one `map` operator. +* Adds scheduler overloads for `just`, `sequenceOf`, `toObservable`. +* Deprecates `asObservable` on `SequenceType` in favor of `toObservable`. +* Adds special overload of `toObservable` for `Array`. +* Improves table view animated data source example. +* Polishes `RxDataSourceStarterKit`, adds `differentiateForSectionedView` operator, `rx_itemsAnimatedWithDataSource` extension. +* Makes blocking operators run current thread runloop while blocking and thus disabling deadlocks. + +### Fixed + +* Fixes example with `Variable` in playgrounds so it less confusing regarding memory management. +* Fixes `UIImageView` extensions to use `UIImage?` instead of `UIImage!`. +* Changes improper usage of `CustomStringConvertible` with `CustomDebugStringConvertible`. + ## [2.0.0-beta.2](https://github.com/ReactiveX/RxSwift/releases/tag/2.0.0-beta.2) #### Updated diff --git a/Documentation/API.md b/Documentation/API.md index a35f98c9..93193934 100644 --- a/Documentation/API.md +++ b/Documentation/API.md @@ -26,20 +26,24 @@ Operators are stateless by default. * [`timer`](http://reactivex.io/documentation/operators/timer.html) #### Transforming Observables - * [`flatMap`](http://reactivex.io/documentation/operators/flatmap.html) - * [`map` / `select`](http://reactivex.io/documentation/operators/map.html) - * [`scan`](http://reactivex.io/documentation/operators/scan.html) * [`buffer`](http://reactivex.io/documentation/operators/buffer.html) + * [`flatMap`](http://reactivex.io/documentation/operators/flatmap.html) + * [`flatMapFirst`](http://reactivex.io/documentation/operators/flatmap.html) + * [`flatMapLatest`](http://reactivex.io/documentation/operators/flatmap.html) + * [`map`](http://reactivex.io/documentation/operators/map.html) + * [`scan`](http://reactivex.io/documentation/operators/scan.html) + * [`window`](http://reactivex.io/documentation/operators/window.html) #### Filtering Observables * [`debounce` / `throttle`](http://reactivex.io/documentation/operators/debounce.html) * [`distinctUntilChanged`](http://reactivex.io/documentation/operators/distinct.html) * [`elementAt`](http://reactivex.io/documentation/operators/elementat.html) - * [`filter` / `where`](http://reactivex.io/documentation/operators/filter.html) + * [`filter`](http://reactivex.io/documentation/operators/filter.html) * [`sample`](http://reactivex.io/documentation/operators/sample.html) * [`skip`](http://reactivex.io/documentation/operators/skip.html) * [`take`](http://reactivex.io/documentation/operators/take.html) * [`takeLast`](http://reactivex.io/documentation/operators/takelast.html) + * [`single`](http://reactivex.io/documentation/operators/first.html) #### Combining Observables @@ -53,6 +57,7 @@ Operators are stateless by default. * [`catch`](http://reactivex.io/documentation/operators/catch.html) * [`retry`](http://reactivex.io/documentation/operators/retry.html) + * [`retryWhen`](http://reactivex.io/documentation/operators/retry.html) #### Observable Utility Operators From a64594ad61cd8fd4907b5713a3c0937d04487647 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 16 Nov 2015 01:27:06 +0100 Subject: [PATCH 201/210] Release 2.0.0-beta.3 --- README.md | 2 +- RxBlocking.podspec | 2 +- RxCocoa.podspec | 2 +- RxSwift.podspec | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d83b05e8..73b8883e 100644 --- a/README.md +++ b/README.md @@ -479,7 +479,7 @@ $ pod install Add this to `Cartfile` ``` -github "ReactiveX/RxSwift" "2.0.0-beta.2" +github "ReactiveX/RxSwift" "2.0.0-beta.3" ``` ``` diff --git a/RxBlocking.podspec b/RxBlocking.podspec index 3029cbe9..e72a07ca 100644 --- a/RxBlocking.podspec +++ b/RxBlocking.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "RxBlocking" - s.version = "2.0.0-beta.2" + s.version = "2.0.0-beta.3" s.summary = "RxSwift Blocking operatos" s.description = <<-DESC Set of blocking operators for RxSwift. diff --git a/RxCocoa.podspec b/RxCocoa.podspec index b8406de4..8c0fdcb1 100644 --- a/RxCocoa.podspec +++ b/RxCocoa.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "RxCocoa" - s.version = "2.0.0-beta.2" + s.version = "2.0.0-beta.3" s.summary = "RxSwift Cocoa extensions" s.description = <<-DESC * UI extensions diff --git a/RxSwift.podspec b/RxSwift.podspec index 46c7743c..9cdaa7ea 100644 --- a/RxSwift.podspec +++ b/RxSwift.podspec @@ -1,9 +1,9 @@ Pod::Spec.new do |s| s.name = "RxSwift" - s.version = "2.0.0-beta.2" + s.version = "2.0.0-beta.3" s.summary = "Microsoft Reactive Extensions (Rx) for Swift and iOS/OSX platform" s.description = <<-DESC - This is a Swift port of ReactiveX. [https://github.com/ReactiveX](https://github.com/ReactiveX) + This is a Swift port of [ReactiveX.io](https://github.com/ReactiveX) Like the original [Rx](https://github.com/Reactive-extensions/Rx.Net), its intention is to enable easy composition of asynchronous operations and event streams. From 4952fd72bf67055213553a8d088ffb8abc9d528e Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 16 Nov 2015 01:49:26 +0100 Subject: [PATCH 202/210] Polish for `CHANGELOG.md`. --- CHANGELOG.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c618c7b..72eecf84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file. ### Updated * Improves KVO mechanism. - * Type of observed object is now argument `view.rx_observe(CGRect.self, "frame")` + * Type of observed object is now first argument `view.rx_observe(CGRect.self, "frame")` * Support for observing ObjC bridged enums and `RawRepresentable` protocol * Support for easier extending of KVO using `KVORepresentable` protocol * Deprecates KVO extensions that don't accept type as first argument in favor of ones that do. @@ -25,18 +25,20 @@ All notable changes to this project will be documented in this file. * `RxCocoaURLError` * ... * Optimizes consecutive map operators. For example `map(validate1).map(validate2).map(parse)` is now internally optimized to one `map` operator. -* Adds scheduler overloads for `just`, `sequenceOf`, `toObservable`. -* Deprecates `asObservable` on `SequenceType` in favor of `toObservable`. -* Adds special overload of `toObservable` for `Array`. +* Adds overloads for `just`, `sequenceOf`, `toObservable` that accept scheduler. +* Deprecates `asObservable` extension of `SequenceType` in favor of `toObservable`. +* Adds `toObservable` extension to `Array`. * Improves table view animated data source example. -* Polishes `RxDataSourceStarterKit`, adds `differentiateForSectionedView` operator, `rx_itemsAnimatedWithDataSource` extension. -* Makes blocking operators run current thread runloop while blocking and thus disabling deadlocks. +* Polishing of `RxDataSourceStarterKit` + * `differentiateForSectionedView` operator + * `rx_itemsAnimatedWithDataSource` extension +* Makes blocking operators run current thread's runloop while blocking and thus disabling deadlocks. ### Fixed * Fixes example with `Variable` in playgrounds so it less confusing regarding memory management. * Fixes `UIImageView` extensions to use `UIImage?` instead of `UIImage!`. -* Changes improper usage of `CustomStringConvertible` with `CustomDebugStringConvertible`. +* Fixes improper usage of `CustomStringConvertible` and replaces it with `CustomDebugStringConvertible`. ## [2.0.0-beta.2](https://github.com/ReactiveX/RxSwift/releases/tag/2.0.0-beta.2) From 16e290356095c5af534019ee5e54a5bbe6ca8bcd Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 16 Nov 2015 10:12:14 +0100 Subject: [PATCH 203/210] Makes `zip` work on `ObservableType` instead of `ObservableConvertibleType`. --- .../Observables/Implementations/Zip+arity.swift | 14 +++++++------- RxSwift/Observables/Implementations/Zip+arity.tt | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/RxSwift/Observables/Implementations/Zip+arity.swift b/RxSwift/Observables/Implementations/Zip+arity.swift index 7d70640a..52ffe35e 100644 --- a/RxSwift/Observables/Implementations/Zip+arity.swift +++ b/RxSwift/Observables/Implementations/Zip+arity.swift @@ -21,7 +21,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ @warn_unused_result(message="http://git.io/rxs.uo") -public func zip +public func zip (source1: O1, _ source2: O2, resultSelector: (O1.E, O2.E) throws -> R) -> Observable { return Zip2( @@ -110,7 +110,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ @warn_unused_result(message="http://git.io/rxs.uo") -public func zip +public func zip (source1: O1, _ source2: O2, _ source3: O3, resultSelector: (O1.E, O2.E, O3.E) throws -> R) -> Observable { return Zip3( @@ -207,7 +207,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ @warn_unused_result(message="http://git.io/rxs.uo") -public func zip +public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: (O1.E, O2.E, O3.E, O4.E) throws -> R) -> Observable { return Zip4( @@ -312,7 +312,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ @warn_unused_result(message="http://git.io/rxs.uo") -public func zip +public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> R) -> Observable { return Zip5( @@ -425,7 +425,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ @warn_unused_result(message="http://git.io/rxs.uo") -public func zip +public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> R) -> Observable { return Zip6( @@ -546,7 +546,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ @warn_unused_result(message="http://git.io/rxs.uo") -public func zip +public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> R) -> Observable { return Zip7( @@ -675,7 +675,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ @warn_unused_result(message="http://git.io/rxs.uo") -public func zip +public func zip (source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> R) -> Observable { return Zip8( diff --git a/RxSwift/Observables/Implementations/Zip+arity.tt b/RxSwift/Observables/Implementations/Zip+arity.tt index c16b8418..59c9b984 100644 --- a/RxSwift/Observables/Implementations/Zip+arity.tt +++ b/RxSwift/Observables/Implementations/Zip+arity.tt @@ -19,7 +19,7 @@ Merges the specified observable sequences into one observable sequence by using - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ @warn_unused_result(message="http://git.io/rxs.uo") -public func zip<<%= (Array(1...i).map { "O\($0): ObservableConvertibleType" }).joinWithSeparator(", ") %>, R> +public func zip<<%= (Array(1...i).map { "O\($0): ObservableType" }).joinWithSeparator(", ") %>, R> (<%= (Array(1...i).map { "source\($0): O\($0)" }).joinWithSeparator(", _ ") %>, resultSelector: (<%= (Array(1...i).map { "O\($0).E" }).joinWithSeparator(", ") %>) throws -> R) -> Observable { return Zip<%= i %>( From c909f0f572d84f7b7578c55b7b16d853ebad5f8a Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Mon, 16 Nov 2015 10:24:37 +0100 Subject: [PATCH 204/210] Add `NSURLSession` changes to `CHANGELOG.md`. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72eecf84..fac3b753 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ All notable changes to this project will be documented in this file. * `RxCocoaError` * `RxCocoaURLError` * ... +* `NSURLSession` extensions now return `Observable<(NSData!, NSHTTPURLResponse)>` instead of `Observable<(NSData!, NSURLResponse!)>`. * Optimizes consecutive map operators. For example `map(validate1).map(validate2).map(parse)` is now internally optimized to one `map` operator. * Adds overloads for `just`, `sequenceOf`, `toObservable` that accept scheduler. * Deprecates `asObservable` extension of `SequenceType` in favor of `toObservable`. From 34f9e092f9c1c4d9ce5ecc35116e7d9ff24dd19f Mon Sep 17 00:00:00 2001 From: Ash Furrow Date: Tue, 10 Nov 2015 21:46:08 -0500 Subject: [PATCH 205/210] Adds ignoreElements(). --- CHANGELOG.md | 2 ++ RxSwift/Observables/Observable+Time.swift | 17 ++++++++++++ .../Tests/Observable+TimeTest.swift | 26 +++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fac3b753..a573f09f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Change Log All notable changes to this project will be documented in this file. +* Adds `ignoreElements` operator. + --- ## [2.0.0-beta.3](https://github.com/ReactiveX/RxSwift/releases/tag/2.0.0-beta.3) diff --git a/RxSwift/Observables/Observable+Time.swift b/RxSwift/Observables/Observable+Time.swift index 7b46104a..5413105b 100644 --- a/RxSwift/Observables/Observable+Time.swift +++ b/RxSwift/Observables/Observable+Time.swift @@ -170,6 +170,23 @@ extension ObservableType { } } +// MARK: ignoreElements + +extension ObservableType { + + /** + Skips elements and completes (or errors) when the receiver completes (or errors). Equivalent to filter that always returns false. + + - returns: An observable sequence that skips all elements of the source sequence. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func ignoreElements() + -> Observable { + return filter { _ -> Bool in + return false + } + } +} // MARK: delaySubscription diff --git a/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift b/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift index 092f1c6a..1a624d41 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift @@ -1178,6 +1178,32 @@ extension ObservableTimeTest { } } +// MARK: IgnoreElements + +extension ObservableTimeTest { + func testIgnoreElements_DoesNotSendValues() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(210, 1), + next(220, 2), + completed(230) + ]) + + let res = scheduler.start { + xs.ignoreElements() + } + + XCTAssertEqual(res.messages, [ + completed(230) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 230) + ]) + } +} + // MARK: Buffer extension ObservableTimeTest { func testBufferWithTimeOrCount_Basic() { From 1fd303433619bf4c1e3761f769a76ce3031215c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Garci=CC=81a?= Date: Tue, 17 Nov 2015 20:06:42 +0100 Subject: [PATCH 206/210] 2 more combineLatest examples and added .DS_Store to .gitignore --- .gitignore | 5 +++ .../Contents.swift | 35 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c90a0fd4..4551351c 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,8 @@ timeline.xctimeline # Carthage/Checkouts Carthage/Build + + +# Various + +.DS_Store diff --git a/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift b/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift index 5ae6ce57..ac6b9639 100644 --- a/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift +++ b/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift @@ -86,7 +86,7 @@ example("combineLatest 3") { let intOb3 = sequenceOf(0, 1, 2, 3, 4) _ = combineLatest(intOb1, intOb2, intOb3) { - ($0 + $1) * $2 + ($0 + $1) * $2 } .subscribe { print($0) @@ -95,6 +95,39 @@ example("combineLatest 3") { +//: Combinelatest allow combine sequences of several types. + +example("combineLatest 4") { + let intOb = just(2) + let stringOb = just("a") + + _ = combineLatest(intOb, stringOb) { + "\($0) " + $1 + } + .subscribe { + print($0) + } +} + + +//: There are a `combineLatest` extension method for Array of ObservableConvertibleType conformed types +//: The array must be formed by `Observables` of the same type + +example("combineLatest 5") { + let intOb1 = just(2) + let intOb2 = sequenceOf(0, 1, 2, 3) + let intOb3 = sequenceOf(0, 1, 2, 3, 4) + + _ = [intOb1, intOb2, intOb3].combineLatest { intArray -> Int in + Int((intArray[0] + intArray[1]) * intArray[2]) + } + .subscribe { (event: Event) -> Void in + print(event) + } +} + + + /*: ### `zip` From 7ade18ba7575387be40a2e57e1afc2646cd49df2 Mon Sep 17 00:00:00 2001 From: Mike Lewis Date: Tue, 17 Nov 2015 14:37:19 -0800 Subject: [PATCH 207/210] Making withLatestFrom subscribe to the right hand side first This resolves an issue where you have two observables that yield values immediately and it wasn't yielding a result --- .../Implementations/WithLatestFrom.swift | 2 +- .../Tests/Observable+MultipleTest.swift | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/RxSwift/Observables/Implementations/WithLatestFrom.swift b/RxSwift/Observables/Implementations/WithLatestFrom.swift index a312456b..39a7eb0c 100644 --- a/RxSwift/Observables/Implementations/WithLatestFrom.swift +++ b/RxSwift/Observables/Implementations/WithLatestFrom.swift @@ -32,8 +32,8 @@ class WithLatestFromSink(value: 3) + let ys = BehaviorSubject(value: 5) + + let scheduler = TestScheduler(initialClock: 0) + + + let res = scheduler.start { + xs.withLatestFrom(ys) { x, y in "\(x)\(y)" } + .take(1) + } + + XCTAssertEqual(res.messages, [ + next(200, "35"), + completed(200) + ]) + } + func testWithLatestFrom_Simple2() { let scheduler = TestScheduler(initialClock: 0) From 5f5a65fe98bb2fa602d029b3ae8dc8257b57eb55 Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Wed, 18 Nov 2015 09:52:33 +0100 Subject: [PATCH 208/210] Improves testing code. --- RxTests/RxCocoaTests/Control+RxTests.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/RxTests/RxCocoaTests/Control+RxTests.swift b/RxTests/RxCocoaTests/Control+RxTests.swift index 5b322d5b..5bde2612 100644 --- a/RxTests/RxCocoaTests/Control+RxTests.swift +++ b/RxTests/RxCocoaTests/Control+RxTests.swift @@ -55,6 +55,7 @@ class ControlTests : RxTest { func ensureEventDeallocated(createControl: () -> (C, Disposable), _ eventSelector: C -> ControlEvent) { var completed = false var deallocated = false + let outerDisposable = SingleAssignmentDisposable() autoreleasepool { let (control, disposable) = createControl() @@ -70,10 +71,10 @@ class ControlTests : RxTest { deallocated = true } - disposable.dispose() + outerDisposable.disposable = disposable } - + outerDisposable.dispose() XCTAssertTrue(deallocated) XCTAssertTrue(completed) } From 018523bf1ea12d43270568eee40e8be45fcc3b3f Mon Sep 17 00:00:00 2001 From: Krunoslav Zaher Date: Wed, 18 Nov 2015 10:53:59 +0100 Subject: [PATCH 209/210] Improves explanation in playgrounds. --- .../Combining_Observables.xcplaygroundpage/Contents.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift b/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift index ac6b9639..2fff9798 100644 --- a/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift +++ b/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift @@ -95,7 +95,7 @@ example("combineLatest 3") { -//: Combinelatest allow combine sequences of several types. +//: Combinelatest version that allows combining sequences with different types. example("combineLatest 4") { let intOb = just(2) @@ -110,8 +110,8 @@ example("combineLatest 4") { } -//: There are a `combineLatest` extension method for Array of ObservableConvertibleType conformed types -//: The array must be formed by `Observables` of the same type +//: `combineLatest` extension method for Array of `ObservableType` conformable types +//: The array must be formed by `Observables` of the same type. example("combineLatest 5") { let intOb1 = just(2) From 731e69a68003ba250bde60bcd5e9f8a74e2968f9 Mon Sep 17 00:00:00 2001 From: Ash Furrow Date: Tue, 10 Nov 2015 21:46:30 -0500 Subject: [PATCH 210/210] Adds UILabel rx_attributedText. --- CHANGELOG.md | 1 + RxCocoa/iOS/UILabel+Rx.swift | 19 ++++++++++++++++++ .../RxCocoaTests/Control+RxTests+UIKit.swift | 16 +++++++++++++++ RxTests/RxCocoaTests/Control+RxTests.swift | 20 +++++++++++++++++++ 4 files changed, 56 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a573f09f..909dbe1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ All notable changes to this project will be documented in this file. * Adds `ignoreElements` operator. +* Adds `rx_attributedText` to `UILabel`. --- diff --git a/RxCocoa/iOS/UILabel+Rx.swift b/RxCocoa/iOS/UILabel+Rx.swift index ea82bef9..fdab2069 100644 --- a/RxCocoa/iOS/UILabel+Rx.swift +++ b/RxCocoa/iOS/UILabel+Rx.swift @@ -34,6 +34,25 @@ extension UILabel { } } } + + /** + Bindable sink for `attributedText` property. + */ + public var rx_attributedText: AnyObserver { + return AnyObserver { [weak self] event in + MainScheduler.ensureExecutingOnScheduler() + + switch event { + case .Next(let value): + self?.attributedText = value + case .Error(let error): + bindingErrorToInterface(error) + break + case .Completed: + break + } + } + } } diff --git a/RxTests/RxCocoaTests/Control+RxTests+UIKit.swift b/RxTests/RxCocoaTests/Control+RxTests+UIKit.swift index e7e963ec..59061274 100644 --- a/RxTests/RxCocoaTests/Control+RxTests+UIKit.swift +++ b/RxTests/RxCocoaTests/Control+RxTests+UIKit.swift @@ -154,6 +154,22 @@ extension ControlTests { } } +// UILabel +extension ControlTests { + func testLabel_HasWeakReference() { + ensureControlObserverHasWeakReference(UILabel(), { (label: UILabel) -> AnyObserver in label.rx_attributedText }, { Variable(nil).asObservable() }) + } + + func testLabel_NextElementsSetsValue() { + let subject = UILabel() + let attributedTextSequence = Variable(nil) + + attributedTextSequence.subscribe(subject.rx_attributedText) + + attributedTextSequence.value = NSAttributedString(string: "Hello!") + XCTAssert(subject.attributedText == attributedTextSequence.value, "Expected attributedText to have been set") + } +} // UITableView extension ControlTests { diff --git a/RxTests/RxCocoaTests/Control+RxTests.swift b/RxTests/RxCocoaTests/Control+RxTests.swift index 5bde2612..33988d84 100644 --- a/RxTests/RxCocoaTests/Control+RxTests.swift +++ b/RxTests/RxCocoaTests/Control+RxTests.swift @@ -78,4 +78,24 @@ class ControlTests : RxTest { XCTAssertTrue(deallocated) XCTAssertTrue(completed) } + + func ensureControlObserverHasWeakReference(@autoclosure createControl: () -> (C), _ observerSelector: C -> AnyObserver, _ observableSelector: () -> (Observable)) { + var deallocated = false + + let disposeBag = DisposeBag() + + autoreleasepool { + let control = createControl() + let propertyObserver = observerSelector(control) + let observable = observableSelector() + + observable.bindTo(propertyObserver).addDisposableTo(disposeBag) + + _ = control.rx_deallocated.subscribeNext { _ in + deallocated = true + } + } + + XCTAssertTrue(deallocated) + } }