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/CHANGELOG.md b/CHANGELOG.md index 77320a5e..909dbe1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,104 @@ # Change Log All notable changes to this project will be documented in this file. +* Adds `ignoreElements` operator. +* Adds `rx_attributedText` to `UILabel`. + --- +## [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 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. +* 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` + * ... +* `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`. +* Adds `toObservable` extension to `Array`. +* Improves table view animated data source example. +* 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!`. +* 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) + +#### 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 + +* 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 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 diff --git a/Documentation/API.md b/Documentation/API.md index 112eb515..93193934 100644 --- a/Documentation/API.md +++ b/Documentation/API.md @@ -21,21 +21,29 @@ 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 - * [`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) - * [`filter` / `where`](http://reactivex.io/documentation/operators/filter.html) + * [`elementAt`](http://reactivex.io/documentation/operators/elementat.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 @@ -49,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 @@ -57,10 +66,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) @@ -68,6 +80,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 @@ -433,6 +446,6 @@ extension NSTextField { public var rx_delegate: DelegateProxy {} public var rx_text: ControlProperty {} - + } ``` 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. // ... ``` diff --git a/Documentation/GettingStarted.md b/Documentation/GettingStarted.md index a8f38b47..29ef8e82 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) @@ -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). @@ -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. @@ -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 } } @@ -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,68 +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 ? -} -``` - -### Error handling and function names - -For every group of transforming functions there are versions with and without "OrDie" suffix. - -**This will change in 2.0 version and map will have two overloads, with and without `throws`.** - -e.g. - -```swift -public func mapOrDie - (selector: E -> RxResult) - -> (Observable -> Observable) { - return { source in - return selectOrDie(selector)(source) - } -} - -public func map - (selector: E -> R) - -> (Observable -> Observable) { - return { source in - return select(selector)(source) - } -} -``` - -Returning an error from a selector will cause entire graph of dependent sequence transformers to "die" and fail with error. Dying implies that it will release all of its resources and never produce another sequence value. This is usually not an obvious effect. - -If there is some `UITextField` bound to a observable sequence that fails with error or completes, screen won't be updated ever again. - -To make those situations more obvious, RxCocoa debug build will throw an exception in case some sequence that is bound to UI control terminates with an error. - -Using functions without "OrDie" suffix is usually a more safe option. - -There is also the `catch` operator for easier error handling. - ## 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. @@ -1008,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` @@ -1047,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 @@ -1062,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 @@ -1105,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 ``` @@ -1119,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/Documentation/Units.md b/Documentation/Units.md new file mode 100644 index 00000000..22a504c5 --- /dev/null +++ b/Documentation/Units.md @@ -0,0 +1,24 @@ +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 + +TBD... diff --git a/Documentation/Warnings.md b/Documentation/Warnings.md new file mode 100644 index 00000000..284c43cc --- /dev/null +++ b/Documentation/Warnings.md @@ -0,0 +1,124 @@ +Warnings +======== + +### 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) +``` diff --git a/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift b/Rx.playground/Pages/Combining_Observables.xcplaygroundpage/Contents.swift index ea0ef648..2fff9798 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,8 +85,8 @@ example("combineLatest 3") { let intOb2 = sequenceOf(0, 1, 2, 3) let intOb3 = sequenceOf(0, 1, 2, 3, 4) - combineLatest(intOb1, intOb2, intOb3) { - ($0 + $1) * $2 + _ = combineLatest(intOb1, intOb2, intOb3) { + ($0 + $1) * $2 } .subscribe { print($0) @@ -95,6 +95,39 @@ example("combineLatest 3") { +//: Combinelatest version that allows combining sequences with different types. + +example("combineLatest 4") { + let intOb = just(2) + let stringOb = just("a") + + _ = combineLatest(intOb, stringOb) { + "\($0) " + $1 + } + .subscribe { + print($0) + } +} + + +//: `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) + 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` @@ -108,7 +141,7 @@ example("zip 1") { let intOb1 = PublishSubject() let intOb2 = PublishSubject() - zip(intOb1, intOb2) { + _ = zip(intOb1, intOb2) { "\($0) \($1)" } .subscribe { @@ -132,7 +165,7 @@ example("zip 2") { let intOb2 = sequenceOf(0, 1, 2, 3, 4) - zip(intOb1, intOb2) { + _ = zip(intOb1, intOb2) { $0 * $1 } .subscribe { @@ -146,7 +179,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 +203,7 @@ example("merge 1") { let subject1 = PublishSubject() let subject2 = PublishSubject() - sequenceOf(subject1, subject2) + _ = sequenceOf(subject1, subject2) .merge() .subscribeNext { int in print(int) @@ -190,7 +223,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 01c41486..c366e4a2 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 @@ -15,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)") } @@ -44,7 +43,7 @@ func sampleWithMulticast() { let subject1 = PublishSubject() - subject1 + _ = subject1 .subscribe { print("Subject \($0)") } @@ -52,7 +51,7 @@ func sampleWithMulticast() { let int1 = interval(1, MainScheduler.sharedInstance) .multicast(subject1) - int1 + _ = int1 .subscribe { print("first subscription \($0)") } @@ -62,14 +61,14 @@ func sampleWithMulticast() { } delay(4) { - int1 + _ = int1 .subscribe { print("second subscription \($0)") } } delay(6) { - int1 + _ = int1 .subscribe { print("third subscription \($0)") } @@ -95,7 +94,7 @@ func sampleWithReplayBuffer0() { let int1 = interval(1, MainScheduler.sharedInstance) .replay(0) - int1 + _ = int1 .subscribe { print("first subscription \($0)") } @@ -105,14 +104,14 @@ func sampleWithReplayBuffer0() { } delay(4) { - int1 + _ = int1 .subscribe { print("second subscription \($0)") } } delay(6) { - int1 + _ = int1 .subscribe { print("third subscription \($0)") } @@ -130,7 +129,7 @@ func sampleWithReplayBuffer2() { let int1 = interval(1, MainScheduler.sharedInstance) .replay(2) - int1 + _ = int1 .subscribe { print("first subscription \($0)") } @@ -140,14 +139,14 @@ func sampleWithReplayBuffer2() { } delay(4) { - int1 + _ = int1 .subscribe { print("second subscription \($0)") } } delay(6) { - int1 + _ = int1 .subscribe { print("third subscription \($0)") } @@ -173,7 +172,7 @@ func sampleWithPublish() { let int1 = interval(1, MainScheduler.sharedInstance) .publish() - int1 + _ = int1 .subscribe { print("first subscription \($0)") } @@ -183,14 +182,14 @@ func sampleWithPublish() { } delay(4) { - int1 + _ = int1 .subscribe { print("second subscription \($0)") } } delay(6) { - int1 + _ = int1 .subscribe { print("third subscription \($0)") } @@ -200,6 +199,6 @@ func sampleWithPublish() { // sampleWithPublish() -XCPSetExecutionShouldContinueIndefinitely(true) +playgroundShouldContinueIndefinitely() //: [Index](Index) 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..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 @@ -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..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/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 } 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/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 @@ - + diff --git a/Rx.xcodeproj/project.pbxproj b/Rx.xcodeproj/project.pbxproj index cec55033..25e340cf 100644 --- a/Rx.xcodeproj/project.pbxproj +++ b/Rx.xcodeproj/project.pbxproj @@ -7,6 +7,11 @@ 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 */; }; + 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 */; }; @@ -59,8 +64,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 */; }; @@ -83,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 */; }; @@ -149,8 +150,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 */; }; @@ -246,22 +247,84 @@ C8093F4E1B8A732E0088E94D /* NSTextField+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093ECA1B8A732E0088E94D /* NSTextField+Rx.swift */; }; C8093F4F1B8A732E0088E94D /* RxCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = C8093ECB1B8A732E0088E94D /* RxCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; C8093F501B8A732E0088E94D /* RxCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = C8093ECB1B8A732E0088E94D /* RxCocoa.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C8093F5E1B8A73A20088E94D /* Observable+Blocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093F581B8A73A20088E94D /* Observable+Blocking.swift */; }; - C8093F5F1B8A73A20088E94D /* Observable+Blocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093F581B8A73A20088E94D /* Observable+Blocking.swift */; }; - C80D338F1B91EF9E0014629D /* Observable+CocoaExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D338E1B91EF9E0014629D /* Observable+CocoaExtensions.swift */; }; - C80D33901B91EF9E0014629D /* Observable+CocoaExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D338E1B91EF9E0014629D /* Observable+CocoaExtensions.swift */; }; + C8093F5E1B8A73A20088E94D /* ObservableConvertibleType+Blocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093F581B8A73A20088E94D /* ObservableConvertibleType+Blocking.swift */; }; + C8093F5F1B8A73A20088E94D /* ObservableConvertibleType+Blocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093F581B8A73A20088E94D /* ObservableConvertibleType+Blocking.swift */; }; + C80D338F1B91EF9E0014629D /* Observable+Bind.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D338E1B91EF9E0014629D /* Observable+Bind.swift */; }; + C80D33901B91EF9E0014629D /* Observable+Bind.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D338E1B91EF9E0014629D /* Observable+Bind.swift */; }; C80D33981B922FB00014629D /* ControlEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D33931B922FB00014629D /* ControlEvent.swift */; }; C80D33991B922FB00014629D /* ControlEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D33931B922FB00014629D /* ControlEvent.swift */; }; C80D339A1B922FB00014629D /* ControlProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D33941B922FB00014629D /* ControlProperty.swift */; }; 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 */; }; + 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 */; }; 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 */; }; + 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 */; }; + 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 */; }; @@ -298,6 +361,32 @@ 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 */; }; + C8941BE21BD5695C00A0E874 /* BlockingObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8941BDE1BD5695C00A0E874 /* BlockingObservable.swift */; }; + C8941BE41BD56B0700A0E874 /* BlockingObservable+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8941BE31BD56B0700A0E874 /* BlockingObservable+Operators.swift */; }; + C8941BE51BD56B0700A0E874 /* BlockingObservable+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8941BE31BD56B0700A0E874 /* BlockingObservable+Operators.swift */; }; + C8941BE61BD56B0700A0E874 /* BlockingObservable+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8941BE31BD56B0700A0E874 /* BlockingObservable+Operators.swift */; }; + C8941BE71BD56B0700A0E874 /* BlockingObservable+Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8941BE31BD56B0700A0E874 /* BlockingObservable+Operators.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 */; }; + C8B144FB1BD2D44500267DCE /* ConcurrentMainScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B144FA1BD2D44500267DCE /* ConcurrentMainScheduler.swift */; }; + C8B144FC1BD2D44500267DCE /* ConcurrentMainScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B144FA1BD2D44500267DCE /* ConcurrentMainScheduler.swift */; }; + C8B144FD1BD2D44500267DCE /* ConcurrentMainScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B144FA1BD2D44500267DCE /* ConcurrentMainScheduler.swift */; }; + C8B144FE1BD2D44500267DCE /* ConcurrentMainScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B144FA1BD2D44500267DCE /* ConcurrentMainScheduler.swift */; }; + C8B145001BD2D80100267DCE /* ImmediateScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B144FF1BD2D80100267DCE /* ImmediateScheduler.swift */; }; + C8B145011BD2D80100267DCE /* ImmediateScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B144FF1BD2D80100267DCE /* ImmediateScheduler.swift */; }; + C8B145021BD2D80100267DCE /* ImmediateScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B144FF1BD2D80100267DCE /* ImmediateScheduler.swift */; }; + C8B145031BD2D80100267DCE /* ImmediateScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B144FF1BD2D80100267DCE /* ImmediateScheduler.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 */; }; @@ -312,6 +401,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 */; }; @@ -320,7 +425,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 */; }; @@ -337,7 +442,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 */; }; @@ -372,7 +476,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 */; }; @@ -442,7 +545,7 @@ C8F0C01C1BBBFBB9001B112F /* UILabel+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C882540C1B8A752B00B02D69 /* UILabel+Rx.swift */; }; C8F0C01D1BBBFBB9001B112F /* RxSearchBarDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88253FF1B8A752B00B02D69 /* RxSearchBarDelegateProxy.swift */; }; C8F0C01E1BBBFBB9001B112F /* RxAlertViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88253FB1B8A752B00B02D69 /* RxAlertViewDelegateProxy.swift */; }; - C8F0C01F1BBBFBB9001B112F /* Observable+CocoaExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D338E1B91EF9E0014629D /* Observable+CocoaExtensions.swift */; }; + C8F0C01F1BBBFBB9001B112F /* Observable+Bind.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D338E1B91EF9E0014629D /* Observable+Bind.swift */; }; C8F0C0201BBBFBB9001B112F /* UISegmentedControl+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C882540F1B8A752B00B02D69 /* UISegmentedControl+Rx.swift */; }; C8F0C0211BBBFBB9001B112F /* KVOObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E931B8A732E0088E94D /* KVOObservable.swift */; }; C8F0C0221BBBFBB9001B112F /* UIButton+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88254061B8A752B00B02D69 /* UIButton+Rx.swift */; }; @@ -478,7 +581,55 @@ C8F0C0431BBBFBB9001B112F /* _RX.h in Headers */ = {isa = PBXBuildFile; fileRef = C8093E821B8A732E0088E94D /* _RX.h */; settings = {ATTRIBUTES = (Public, ); }; }; 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 /* Observable+Blocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093F581B8A73A20088E94D /* Observable+Blocking.swift */; }; + 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 */; }; + 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 */; }; + 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 */; }; + 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 */; }; + 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 */; }; + 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 */; }; @@ -496,7 +647,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 */; }; @@ -520,7 +670,7 @@ D2138C831BB9BEBE00339B5C /* _RXKVOObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = C8093E871B8A732E0088E94D /* _RXKVOObserver.m */; }; D2138C841BB9BEBE00339B5C /* _RXSwizzling.h in Headers */ = {isa = PBXBuildFile; fileRef = C8093E881B8A732E0088E94D /* _RXSwizzling.h */; settings = {ATTRIBUTES = (Public, ); }; }; D2138C851BB9BEBE00339B5C /* _RXSwizzling.m in Sources */ = {isa = PBXBuildFile; fileRef = C8093E891B8A732E0088E94D /* _RXSwizzling.m */; }; - D2138C861BB9BEBE00339B5C /* Observable+CocoaExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D338E1B91EF9E0014629D /* Observable+CocoaExtensions.swift */; }; + D2138C861BB9BEBE00339B5C /* Observable+Bind.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D338E1B91EF9E0014629D /* Observable+Bind.swift */; }; D2138C871BB9BEBE00339B5C /* CLLocationManager+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E8A1B8A732E0088E94D /* CLLocationManager+Rx.swift */; }; D2138C881BB9BEBE00339B5C /* DelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E8B1B8A732E0088E94D /* DelegateProxy.swift */; }; D2138C891BB9BEBE00339B5C /* DelegateProxyType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093E8C1B8A732E0088E94D /* DelegateProxyType.swift */; }; @@ -540,6 +690,19 @@ 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 */; }; + 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 */; }; + 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 */; }; 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 */; }; @@ -549,7 +712,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 */; }; @@ -574,7 +737,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 */; }; @@ -590,7 +752,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 */; }; @@ -645,7 +806,10 @@ D2EBEB421BB9B6DE003A27DC /* ReplaySubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CC01B8A72BE0088E94D /* ReplaySubject.swift */; }; 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 */; }; + D2EBEB8A1BB9B9EE003A27DC /* ObservableConvertibleType+Blocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093F581B8A73A20088E94D /* ObservableConvertibleType+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 */ @@ -710,6 +874,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; }; @@ -742,7 +907,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 = ""; }; @@ -755,7 +919,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 = ""; }; @@ -782,14 +945,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 = ""; }; @@ -805,9 +968,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 = ""; }; @@ -836,20 +999,37 @@ 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; }; + C8093F581B8A73A20088E94D /* ObservableConvertibleType+Blocking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObservableConvertibleType+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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 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 = ""; }; @@ -868,10 +1048,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 = ""; }; @@ -885,18 +1065,44 @@ 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; }; C8A56AD71AD7424700B4673B /* RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C8B144FA1BD2D44500267DCE /* ConcurrentMainScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConcurrentMainScheduler.swift; sourceTree = ""; }; + C8B144FF1BD2D80100267DCE /* ImmediateScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImmediateScheduler.swift; sourceTree = ""; }; 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 = ""; }; C8C3DA051B9393AC004D233E /* Empty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Empty.swift; sourceTree = ""; }; 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; }; + 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; }; + 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 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; }; F31F35AF1BB4FED800961002 /* UIStepper+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStepper+Rx.swift"; sourceTree = ""; }; @@ -1002,8 +1208,9 @@ C8093C651B8A72BE0088E94D /* ImmediateSchedulerType.swift */, C8093C681B8A72BE0088E94D /* Observable.swift */, 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 */, @@ -1025,6 +1232,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 = ""; @@ -1044,12 +1256,15 @@ children = ( C8093C541B8A72BE0088E94D /* AnonymousDisposable.swift */, C8093C551B8A72BE0088E94D /* BinaryDisposable.swift */, + CB883B441BE256D4000AC2EE /* BooleanDisposable.swift */, C8093C571B8A72BE0088E94D /* CompositeDisposable.swift */, C8093C581B8A72BE0088E94D /* DisposeBag.swift */, C8093C591B8A72BE0088E94D /* DisposeBase.swift */, + C84CC5661BDD08A500E06A64 /* SubscriptionDisposable.swift */, C8093C5A1B8A72BE0088E94D /* NAryDisposable.swift */, C8093C5B1B8A72BE0088E94D /* NAryDisposable.tt */, C8093C5C1B8A72BE0088E94D /* NopDisposable.swift */, + CB883B3F1BE24C15000AC2EE /* RefCountDisposable.swift */, C8093C5D1B8A72BE0088E94D /* ScheduledDisposable.swift */, C8093C5E1B8A72BE0088E94D /* ScopedDisposable.swift */, C8093C5F1B8A72BE0088E94D /* SerialDisposable.swift */, @@ -1079,9 +1294,9 @@ C8093C6A1B8A72BE0088E94D /* Implementations */ = { isa = PBXGroup; children = ( + CB883B491BE369AA000AC2EE /* AddRef.swift */, C8093C6B1B8A72BE0088E94D /* Amb.swift */, C8C3DA111B93A3EA004D233E /* AnonymousObservable.swift */, - C8093C6D1B8A72BE0088E94D /* AsObservable.swift */, C821DBA11BA4DCAB008F3809 /* Buffer.swift */, C8093C6E1B8A72BE0088E94D /* Catch.swift */, C8093C711B8A72BE0088E94D /* CombineLatest.swift */, @@ -1095,10 +1310,10 @@ C8093C771B8A72BE0088E94D /* DelaySubscription.swift */, C8093C781B8A72BE0088E94D /* DistinctUntilChanged.swift */, C8093C791B8A72BE0088E94D /* Do.swift */, + C84CC53F1BDC3B3700E06A64 /* ElementAt.swift */, C8C3DA051B9393AC004D233E /* Empty.swift */, C8C3DA081B93941E004D233E /* FailWith.swift */, C8093C7A1B8A72BE0088E94D /* Filter.swift */, - C8093C7B1B8A72BE0088E94D /* FlatMap.swift */, C84B38ED1BA433CD001B7D88 /* Generate.swift */, C8C3DA021B9390C4004D233E /* Just.swift */, C8093C7C1B8A72BE0088E94D /* Map.swift */, @@ -1112,18 +1327,29 @@ C8093C841B8A72BE0088E94D /* Reduce.swift */, C8093C851B8A72BE0088E94D /* RefCount.swift */, C8640A021BA5B12A00D3C4E8 /* Repeat.swift */, + CB255BD61BC46A9C00798A4C /* RetryWhen.swift */, C8093C861B8A72BE0088E94D /* Sample.swift */, C8093C871B8A72BE0088E94D /* Scan.swift */, + C83100631BF7D51600AAE3CD /* Sequence.swift */, + C89CDB351BCB0DD7002063D9 /* ShareReplay1.swift */, + CB30D9E81BF0E3500084C1C0 /* SingleAsync.swift */, C8093C881B8A72BE0088E94D /* Sink.swift */, C8093C891B8A72BE0088E94D /* Skip.swift */, + D285BAC31BC0231000B3F602 /* SkipUntil.swift */, + D22B6D251BC8504A00BCE0AB /* SkipWhile.swift */, C8093C8A1B8A72BE0088E94D /* StartWith.swift */, C8093C8B1B8A72BE0088E94D /* SubscribeOn.swift */, C8093C8C1B8A72BE0088E94D /* Switch.swift */, C8093C8D1B8A72BE0088E94D /* Take.swift */, + B1B7C3BC1BDD39DB0076934E /* TakeLast.swift */, C8093C8E1B8A72BE0088E94D /* TakeUntil.swift */, C8093C8F1B8A72BE0088E94D /* TakeWhile.swift */, 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 */, @@ -1145,15 +1371,21 @@ C8093CB41B8A72BE0088E94D /* Schedulers */ = { isa = PBXGroup; children = ( - C84B38E71BA43380001B7D88 /* ScheduledItem.swift */, C8093CB51B8A72BE0088E94D /* ConcurrentDispatchQueueScheduler.swift */, + C8B144FA1BD2D44500267DCE /* ConcurrentMainScheduler.swift */, + C8C3DA0E1B939767004D233E /* CurrentThreadScheduler.swift */, C8093CB61B8A72BE0088E94D /* DispatchQueueSchedulerPriority.swift */, + C8B144FF1BD2D80100267DCE /* ImmediateScheduler.swift */, C8093CB71B8A72BE0088E94D /* MainScheduler.swift */, C8093CB81B8A72BE0088E94D /* OperationQueueScheduler.swift */, C8093CB91B8A72BE0088E94D /* RecursiveScheduler.swift */, + C84B38E71BA43380001B7D88 /* ScheduledItem.swift */, C8093CBB1B8A72BE0088E94D /* SchedulerServices+Emulation.swift */, C8093CBC1B8A72BE0088E94D /* SerialDispatchQueueScheduler.swift */, - C8C3DA0E1B939767004D233E /* CurrentThreadScheduler.swift */, + C8F6A0EE1BEE2CB6007DF367 /* ScheduledItemType.swift */, + C8F6A0F31BEE3395007DF367 /* InvocableType.swift */, + C8F6A0F81BEE33C1007DF367 /* InvocableScheduledItem.swift */, + C8F6A0FD1BEE42DD007DF367 /* AnonymousInvocable.swift */, ); path = Schedulers; sourceTree = ""; @@ -1193,7 +1425,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 */, @@ -1203,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 = ""; @@ -1214,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; @@ -1255,7 +1492,10 @@ isa = PBXGroup; children = ( A111CE961B91C97C00D0DCEE /* Info.plist */, - C8093F581B8A73A20088E94D /* Observable+Blocking.swift */, + C8093F581B8A73A20088E94D /* ObservableConvertibleType+Blocking.swift */, + C8941BDE1BD5695C00A0E874 /* BlockingObservable.swift */, + C8941BE31BD56B0700A0E874 /* BlockingObservable+Operators.swift */, + C88E296A1BEB712E001CCB92 /* RunLoopLock.swift */, C8093F591B8A73A20088E94D /* README.md */, ); path = RxBlocking; @@ -1264,12 +1504,28 @@ C80D33911B922FB00014629D /* CocoaUnits */ = { isa = PBXGroup; children = ( + 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 = ( @@ -1712,7 +1968,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0700; + LastUpgradeCheck = 0710; ORGANIZATIONNAME = "Krunoslav Zaher"; TargetAttributes = { C8A56AD61AD7424700B4673B = { @@ -1852,11 +2108,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 */, @@ -1865,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 */, @@ -1874,11 +2135,16 @@ C882542E1B8A752B00B02D69 /* UILabel+Rx.swift in Sources */, C88254211B8A752B00B02D69 /* RxSearchBarDelegateProxy.swift in Sources */, C882541D1B8A752B00B02D69 /* RxAlertViewDelegateProxy.swift in Sources */, - C80D338F1B91EF9E0014629D /* Observable+CocoaExtensions.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 */, 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 */, + C80DDEA31BCE69BA006A1832 /* Driver.swift in Sources */, C8093EEB1B8A732E0088E94D /* DeinitAction.swift in Sources */, C882541C1B8A752B00B02D69 /* RxActionSheetDelegateProxy.swift in Sources */, C8093ED51B8A732E0088E94D /* _RXDelegateProxy.m in Sources */, @@ -1895,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 */, @@ -1904,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; @@ -1912,34 +2180,46 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; 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 */, C8093EF61B8A732E0088E94D /* NSObject+Rx.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 */, 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+CocoaExtensions.swift in Sources */, + C80D33901B91EF9E0014629D /* Observable+Bind.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1947,7 +2227,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - C8093F5E1B8A73A20088E94D /* Observable+Blocking.swift in Sources */, + C88E296B1BEB712E001CCB92 /* RunLoopLock.swift in Sources */, + C8941BDF1BD5695C00A0E874 /* BlockingObservable.swift in Sources */, + C8941BE41BD56B0700A0E874 /* BlockingObservable+Operators.swift in Sources */, + C8093F5E1B8A73A20088E94D /* ObservableConvertibleType+Blocking.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1955,7 +2238,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - C8093F5F1B8A73A20088E94D /* Observable+Blocking.swift in Sources */, + C88E296C1BEB712E001CCB92 /* RunLoopLock.swift in Sources */, + C8941BE01BD5695C00A0E874 /* BlockingObservable.swift in Sources */, + C8941BE51BD56B0700A0E874 /* BlockingObservable+Operators.swift in Sources */, + C8093F5F1B8A73A20088E94D /* ObservableConvertibleType+Blocking.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1969,18 +2255,24 @@ 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 */, + C8093D6A1B8A72BE0088E94D /* AnyObserver.swift in Sources */, 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 */, + CB883B3C1BE24355000AC2EE /* Window.swift in Sources */, C8093D4C1B8A72BE0088E94D /* Timer.swift in Sources */, C8C3DA071B9393AC004D233E /* Empty.swift in Sources */, + CB255BD81BC46A9C00798A4C /* RetryWhen.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 */, @@ -1988,9 +2280,12 @@ 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 */, + 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 */, @@ -1998,15 +2293,24 @@ 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 */, 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 */, + C8F6A0FF1BEE42DD007DF367 /* AnonymousInvocable.swift in Sources */, + C84CC5541BDCF49300E06A64 /* SynchronizedOnType.swift in Sources */, C8093CEA1B8A72BE0088E94D /* ScopedDisposable.swift in Sources */, C8093D261B8A72BE0088E94D /* Multicast.swift in Sources */, 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 */, @@ -2015,15 +2319,16 @@ 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 */, 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 */, C8093CE01B8A72BE0088E94D /* DisposeBase.swift in Sources */, C8093CD61B8A72BE0088E94D /* AnonymousDisposable.swift in Sources */, C8093D901B8A72BE0088E94D /* ConcurrentDispatchQueueScheduler.swift in Sources */, @@ -2033,6 +2338,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 */, @@ -2046,11 +2352,14 @@ 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 */, 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 */, C8093CF81B8A72BE0088E94D /* ImmediateSchedulerType.swift in Sources */, @@ -2061,11 +2370,14 @@ 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 */, + 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 */, ); @@ -2081,18 +2393,24 @@ 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 */, + C8093D691B8A72BE0088E94D /* AnyObserver.swift in Sources */, 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 */, + CB883B3B1BE24355000AC2EE /* Window.swift in Sources */, C8093D4B1B8A72BE0088E94D /* Timer.swift in Sources */, C8C3DA061B9393AC004D233E /* Empty.swift in Sources */, + CB255BD71BC46A9C00798A4C /* RetryWhen.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 */, @@ -2100,9 +2418,12 @@ 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 */, + 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 */, @@ -2110,15 +2431,24 @@ 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 */, 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 */, + C8F6A0FE1BEE42DD007DF367 /* AnonymousInvocable.swift in Sources */, + C84CC5531BDCF49300E06A64 /* SynchronizedOnType.swift in Sources */, C8093CE91B8A72BE0088E94D /* ScopedDisposable.swift in Sources */, C8093D251B8A72BE0088E94D /* Multicast.swift in Sources */, C8C3DA0F1B939767004D233E /* CurrentThreadScheduler.swift in Sources */, C8093D851B8A72BE0088E94D /* Rx.swift in Sources */, C80D342E1B9245A40014629D /* CombineLatest+CollectionType.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 */, @@ -2127,15 +2457,16 @@ 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 */, 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 */, C8093CDF1B8A72BE0088E94D /* DisposeBase.swift in Sources */, C8093CD51B8A72BE0088E94D /* AnonymousDisposable.swift in Sources */, C8093D8F1B8A72BE0088E94D /* ConcurrentDispatchQueueScheduler.swift in Sources */, @@ -2145,6 +2476,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 */, @@ -2158,11 +2490,14 @@ 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 */, 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 */, C8093CF71B8A72BE0088E94D /* ImmediateSchedulerType.swift in Sources */, @@ -2173,11 +2508,14 @@ 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 */, + 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 */, ); @@ -2193,18 +2531,24 @@ 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 */, + C8F0BF9A1BBBFB8B001B112F /* AnyObserver.swift in Sources */, 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 */, + CB883B3E1BE24355000AC2EE /* Window.swift in Sources */, C8F0BF9E1BBBFB8B001B112F /* Timer.swift in Sources */, C8F0BF9F1BBBFB8B001B112F /* Empty.swift in Sources */, + CB255BDA1BC46A9C00798A4C /* RetryWhen.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 */, @@ -2212,9 +2556,12 @@ 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 */, + 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 */, @@ -2222,15 +2569,24 @@ 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 */, 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 */, + C8F6A1011BEE42DD007DF367 /* AnonymousInvocable.swift in Sources */, + C84CC5561BDCF49300E06A64 /* SynchronizedOnType.swift in Sources */, C8F0BFB91BBBFB8B001B112F /* ScopedDisposable.swift in Sources */, C8F0BFBA1BBBFB8B001B112F /* Multicast.swift in Sources */, 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 */, @@ -2239,15 +2595,16 @@ 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 */, 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 */, C8F0BFCF1BBBFB8B001B112F /* DisposeBase.swift in Sources */, C8F0BFD01BBBFB8B001B112F /* AnonymousDisposable.swift in Sources */, C8F0BFD11BBBFB8B001B112F /* ConcurrentDispatchQueueScheduler.swift in Sources */, @@ -2257,6 +2614,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 */, @@ -2270,11 +2628,14 @@ 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 */, 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 */, C8F0BFEC1BBBFB8B001B112F /* ImmediateSchedulerType.swift in Sources */, @@ -2285,11 +2646,14 @@ 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 */, + 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 */, ); @@ -2302,11 +2666,14 @@ 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 */, @@ -2315,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 */, @@ -2324,11 +2693,16 @@ C8F0C01C1BBBFBB9001B112F /* UILabel+Rx.swift in Sources */, C8F0C01D1BBBFBB9001B112F /* RxSearchBarDelegateProxy.swift in Sources */, C8F0C01E1BBBFBB9001B112F /* RxAlertViewDelegateProxy.swift in Sources */, - C8F0C01F1BBBFBB9001B112F /* Observable+CocoaExtensions.swift in Sources */, + C80DDEAA1BCE69BA006A1832 /* ObservableConvertibleType+Driver.swift in Sources */, + C80DDEA21BCE69BA006A1832 /* Driver+Subscription.swift in Sources */, + 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 */, + C80DDEA61BCE69BA006A1832 /* Driver.swift in Sources */, C8F0C0241BBBFBB9001B112F /* DeinitAction.swift in Sources */, C8F0C0251BBBFBB9001B112F /* RxActionSheetDelegateProxy.swift in Sources */, C8F0C0261BBBFBB9001B112F /* _RXDelegateProxy.m in Sources */, @@ -2345,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 */, @@ -2354,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; @@ -2362,7 +2738,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - C8F0C04F1BBBFBCE001B112F /* Observable+Blocking.swift in Sources */, + C88E296E1BEB712E001CCB92 /* RunLoopLock.swift in Sources */, + C8941BE21BD5695C00A0E874 /* BlockingObservable.swift in Sources */, + C8941BE71BD56B0700A0E874 /* BlockingObservable+Operators.swift in Sources */, + C8F0C04F1BBBFBCE001B112F /* ObservableConvertibleType+Blocking.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2373,11 +2752,14 @@ 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 */, @@ -2386,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 */, @@ -2394,12 +2778,16 @@ 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 */, 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 */, D203C4F81BB9C53700D02D00 /* RxActionSheetDelegateProxy.swift in Sources */, D2138C8F1BB9BED600339B5C /* Deallocating.swift in Sources */, D2138C961BB9BEDA00339B5C /* NSURLSession+Rx.swift in Sources */, @@ -2408,7 +2796,7 @@ 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 */, D203C4F51BB9C52900D02D00 /* ItemEvents.swift in Sources */, @@ -2417,14 +2805,17 @@ 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 */, 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 */, D203C5011BB9C53E00D02D00 /* UIActionSheet+Rx.swift in Sources */, + C8DB968A1BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift in Sources */, D203C50F1BB9C53E00D02D00 /* UIStepper+Rx.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2439,18 +2830,24 @@ 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 */, 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 */, + CB883B3D1BE24355000AC2EE /* Window.swift in Sources */, D2EBEAEA1BB9B697003A27DC /* SchedulerType.swift in Sources */, D2EBEB031BB9B6C1003A27DC /* CombineLatest+CollectionType.swift in Sources */, + CB255BD91BC46A9C00798A4C /* RetryWhen.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 */, @@ -2459,24 +2856,37 @@ D2EBEAEE1BB9B6A4003A27DC /* InfiniteSequence.swift in Sources */, 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 */, D2EBEB041BB9B6C1003A27DC /* Concat.swift in Sources */, 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 */, + 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 */, + C8F6A1001BEE42DD007DF367 /* AnonymousInvocable.swift in Sources */, + C84CC5551BDCF49300E06A64 /* SynchronizedOnType.swift in Sources */, + D2752D631BC5551B0070C418 /* SkipUntil.swift in Sources */, D2EBEB351BB9B6D2003A27DC /* ObserverBase.swift in Sources */, D2EBEB0F1BB9B6C1003A27DC /* Generate.swift in Sources */, 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 */, @@ -2484,12 +2894,14 @@ 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 */, 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 */, @@ -2503,6 +2915,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 */, @@ -2515,27 +2928,31 @@ 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 /* 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 */, + C8F6A0F11BEE2CB6007DF367 /* ScheduledItemType.swift in Sources */, + CB883B4C1BE369AA000AC2EE /* AddRef.swift in Sources */, D2EBEAFA1BB9B6B2003A27DC /* SingleAssignmentDisposable.swift in Sources */, D2EBEAF31BB9B6AE003A27DC /* DisposeBag.swift in Sources */, 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 */, D2EBEB201BB9B6C1003A27DC /* StartWith.swift in Sources */, 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 */, ); @@ -2545,7 +2962,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D2EBEB8A1BB9B9EE003A27DC /* Observable+Blocking.swift in Sources */, + C88E296D1BEB712E001CCB92 /* RunLoopLock.swift in Sources */, + C8941BE11BD5695C00A0E874 /* BlockingObservable.swift in Sources */, + C8941BE61BD56B0700A0E874 /* BlockingObservable+Operators.swift in Sources */, + D2EBEB8A1BB9B9EE003A27DC /* ObservableConvertibleType+Blocking.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2605,6 +3025,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; @@ -2621,6 +3042,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; @@ -2637,6 +3059,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; @@ -2655,6 +3078,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; @@ -2673,6 +3097,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; @@ -2691,6 +3116,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; @@ -2707,6 +3133,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; @@ -2723,6 +3150,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; @@ -2739,6 +3167,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; @@ -2757,6 +3186,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; @@ -2775,6 +3205,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; @@ -2793,6 +3224,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; @@ -2803,6 +3235,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; @@ -2823,6 +3256,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"; @@ -2834,10 +3268,11 @@ 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 = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -2856,6 +3291,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; @@ -2874,6 +3310,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; @@ -2892,6 +3329,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; @@ -2910,6 +3348,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; @@ -2920,6 +3359,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; @@ -2957,7 +3397,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"; @@ -2973,6 +3413,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; @@ -3003,9 +3444,10 @@ 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 = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -3024,6 +3466,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; @@ -3041,6 +3484,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; @@ -3058,6 +3502,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; @@ -3076,6 +3521,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; @@ -3094,6 +3540,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; @@ -3112,6 +3559,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; @@ -3130,6 +3578,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; @@ -3148,6 +3597,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; @@ -3166,6 +3616,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; @@ -3184,6 +3635,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; @@ -3202,6 +3654,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; @@ -3223,6 +3676,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; @@ -3241,6 +3695,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; @@ -3259,6 +3714,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; @@ -3282,7 +3738,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; @@ -3301,7 +3757,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; @@ -3320,7 +3776,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; @@ -3339,6 +3795,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; @@ -3357,6 +3814,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; @@ -3375,6 +3833,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/Rx.xcodeproj/xcshareddata/xcschemes/RxSwift-tvOS.xcscheme b/Rx.xcodeproj/xcshareddata/xcschemes/RxSwift-tvOS.xcscheme index dac71b61..4fe17aac 100644 --- a/Rx.xcodeproj/xcshareddata/xcschemes/RxSwift-tvOS.xcscheme +++ b/Rx.xcodeproj/xcshareddata/xcschemes/RxSwift-tvOS.xcscheme @@ -28,26 +28,7 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> - - - - - - - - diff --git a/RxBlocking.podspec b/RxBlocking.podspec index fce3d161..e72a07ca 100644 --- a/RxBlocking.podspec +++ b/RxBlocking.podspec @@ -1,9 +1,9 @@ Pod::Spec.new do |s| s.name = "RxBlocking" - s.version = "2.0.0-alpha.4" + s.version = "2.0.0-beta.3" 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' @@ -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/RxBlocking/BlockingObservable+Operators.swift b/RxBlocking/BlockingObservable+Operators.swift new file mode 100644 index 00000000..9430803e --- /dev/null +++ b/RxBlocking/BlockingObservable+Operators.swift @@ -0,0 +1,221 @@ +// +// BlockingObservable+Operators.swift +// Rx +// +// Created by Krunoslav Zaher on 10/19/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE + import RxSwift +#endif + +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] { + 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() + } + } + } + + lock.run() + + d.dispose() + + if let error = error { + throw error + } + + return elements + } +} + +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. + */ + public func first() 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 + } + break + case .Error(let e): + error = e + default: + break + } + + lock.stop() + } + } + + lock.run() + + d.dispose() + + 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. + */ + public func last() 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): + element = e + return + case .Error(let e): + error = e + default: + break + } + + lock.stop() + } + } + + lock.run() + + d.dispose() + + if let error = error { + throw error + } + + return element + } +} + +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 error if there is not exactly one element in the observable sequence. + */ + public func single() throws -> E? { + return try single { _ in true } + } + + /** + 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 + if d.disposed { + return + } + switch e { + case .Next(let e): + do { + 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 + case .Completed: + if element == nil { + error = RxError.NoElements + } + } + + lock.stop() + } + } + + lock.run() + d.dispose() + + if let error = error { + throw error + } + + return element + } +} diff --git a/RxBlocking/BlockingObservable.swift b/RxBlocking/BlockingObservable.swift new file mode 100644 index 00000000..2197ce13 --- /dev/null +++ b/RxBlocking/BlockingObservable.swift @@ -0,0 +1,24 @@ +// +// BlockingObservable.swift +// Rx +// +// Created by Krunoslav Zaher on 10/19/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE + import RxSwift +#endif + +/** +`BlockingObservable` is a variety of `Observable` that provides blocking operators. + +It can be useful for testing and demo purposes, but is generally inappropriate for production applications. + +If you think you need to use a `BlockingObservable` this is usually a sign that you should rethink your +design. +*/ +public struct BlockingObservable { + let source: Observable +} \ No newline at end of file diff --git a/RxBlocking/Info.plist b/RxBlocking/Info.plist index c1620d0a..7e7479f0 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 @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0 + 2.0.0 CFBundleSignature ???? CFBundleVersion diff --git a/RxBlocking/Observable+Blocking.swift b/RxBlocking/Observable+Blocking.swift deleted file mode 100644 index 799cbf98..00000000 --- a/RxBlocking/Observable+Blocking.swift +++ /dev/null @@ -1,164 +0,0 @@ -// -// Observable+Blocking.swift -// RxBlocking -// -// Created by Krunoslav Zaher on 7/12/15. -// Copyright (c) 2015 Krunoslav Zaher. All rights reserved. -// - -import Foundation -#if !RX_NO_MODULE -import RxSwift -#endif - -extension ObservableType { - /** - 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]() - - var error: ErrorType? - - var ended = false - - _ = self.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() - } - } - condition.lock() - while !ended { - condition.wait() - } - condition.unlock() - - if let error = error { - throw error - } - - return elements - } -} - -extension ObservableType { - /** - 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.subscribe { e in - switch e { - case .Next(let e): - if element == nil { - element = e - } - break - case .Error(let e): - error = e - default: - break - } - - condition.lock() - ended = true - condition.signal() - condition.unlock() - } - - condition.lock() - while !ended { - condition.wait() - } - d.dispose() - condition.unlock() - - if let error = error { - throw error - } - - return element - } -} - -extension ObservableType { - /** - 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.subscribe { e in - switch e { - case .Next(let e): - element = e - return - case .Error(let e): - error = e - default: - break - } - - condition.lock() - ended = true - condition.signal() - condition.unlock() - } - - condition.lock() - while !ended { - condition.wait() - } - d.dispose() - condition.unlock() - - if let error = error { - throw error - } - - return element - } -} \ No newline at end of file diff --git a/RxBlocking/ObservableConvertibleType+Blocking.swift b/RxBlocking/ObservableConvertibleType+Blocking.swift new file mode 100644 index 00000000..2d7b255c --- /dev/null +++ b/RxBlocking/ObservableConvertibleType+Blocking.swift @@ -0,0 +1,24 @@ +// +// Observable+Blocking.swift +// RxBlocking +// +// Created by Krunoslav Zaher on 7/12/15. +// Copyright (c) 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE +import RxSwift +#endif + +extension ObservableConvertibleType { + /** + Converts an Observable into a `BlockingObservable` (an Observable with blocking operators). + + - returns: `BlockingObservable` version of `self` + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func toBlocking() -> BlockingObservable { + return BlockingObservable(source: self.asObservable()) + } +} \ No newline at end of file diff --git a/RxBlocking/RunLoopLock.swift b/RxBlocking/RunLoopLock.swift new file mode 100644 index 00000000..94fefa57 --- /dev/null +++ b/RxBlocking/RunLoopLock.swift @@ -0,0 +1,46 @@ +// +// RunLoopLock.swift +// Rx +// +// Created by Krunoslav Zaher on 11/5/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE + import RxSwift +#endif + +class RunLoopLock : NSObject { + let currentRunLoop: CFRunLoopRef + + override init() { + currentRunLoop = CFRunLoopGetCurrent() + } + + func dispatch(action: () -> ()) { + CFRunLoopPerformBlock(currentRunLoop, kCFRunLoopDefaultMode) { + if CurrentThreadScheduler.isScheduleRequired { + CurrentThreadScheduler.instance.schedule(()) { _ in + action() + return NopDisposable.instance + } + } + else { + action() + } + } + CFRunLoopWakeUp(currentRunLoop) + } + + func stop() { + CFRunLoopPerformBlock(currentRunLoop, kCFRunLoopDefaultMode) { + CFRunLoopStop(self.currentRunLoop) + } + CFRunLoopWakeUp(currentRunLoop) + } + + func run() { + CFRunLoopRun() + } +} diff --git a/RxCocoa.podspec b/RxCocoa.podspec index 6d087620..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-alpha.4" + s.version = "2.0.0-beta.3" 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/RxCocoa/Common/CocoaUnits/ControlEvent.swift b/RxCocoa/Common/CocoaUnits/ControlEvent.swift index 6b45100c..16421af8 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(ConcurrentMainScheduler.sharedInstance) } /** @@ -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 151ee797..9af9569b 100644 --- a/RxCocoa/Common/CocoaUnits/ControlProperty.swift +++ b/RxCocoa/Common/CocoaUnits/ControlProperty.swift @@ -38,10 +38,10 @@ public struct ControlProperty : ControlPropertyType { public typealias E = PropertyType let source: Observable - let observer: ObserverOf + let observer: AnyObserver - init(source: Observable, observer: ObserverOf) { - self.source = source + init(source: Observable, observer: AnyObserver) { + self.source = source.subscribeOn(ConcurrentMainScheduler.sharedInstance) self.observer = observer } @@ -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 new file mode 100644 index 00000000..093632b6 --- /dev/null +++ b/RxCocoa/Common/CocoaUnits/Driver/ControlEvent+Driver.swift @@ -0,0 +1,30 @@ +// +// 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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func asDriver() -> Driver { + return self.asDriver { (error) -> Driver in + #if DEBUG + rxFatalError("Somehow driver received error from a source that shouldn't fail.") + #else + return Drive.empty() + #endif + } + } +} \ No newline at end of file diff --git a/RxCocoa/Common/CocoaUnits/Driver/ControlProperty+Driver.swift b/RxCocoa/Common/CocoaUnits/Driver/ControlProperty+Driver.swift new file mode 100644 index 00000000..d42535a6 --- /dev/null +++ b/RxCocoa/Common/CocoaUnits/Driver/ControlProperty+Driver.swift @@ -0,0 +1,30 @@ +// +// 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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func asDriver() -> Driver { + return self.asDriver { (error) -> Driver in + #if DEBUG + rxFatalError("Somehow driver received error from a source that shouldn't fail.") + #else + return Drive.empty() + #endif + } + } +} \ No newline at end of file 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..7c3e0b22 --- /dev/null +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.swift @@ -0,0 +1,295 @@ +// 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. +*/ +@warn_unused_result(message="http://git.io/rxs.uo") +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. +*/ +@warn_unused_result(message="http://git.io/rxs.uo") +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. +*/ +@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 { + 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. +*/ +@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 { + 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. +*/ +@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 { + 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. +*/ +@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 { + 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. +*/ +@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 { + 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. +*/ +@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 { + 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. +*/ +@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 { + 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. +*/ +@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 { + 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. +*/ +@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 { + 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. +*/ +@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 { + 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. +*/ +@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 { + 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. +*/ +@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 { + 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..c4b5cc08 --- /dev/null +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators+arity.tt @@ -0,0 +1,54 @@ +// +// 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. +*/ +@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 { + 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. +*/ +@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 { + 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/Driver+Operators.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift new file mode 100644 index 00000000..fff37635 --- /dev/null +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift @@ -0,0 +1,381 @@ +// +// 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 DriverConvertibleType { + + /** + 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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func map(selector: E -> R) -> Driver { + let source = self + .asObservable() + .map(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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func filter(predicate: (E) -> Bool) -> Driver { + let source = self + .asObservable() + .filter(predicate) + return Driver(source) + } +} + +extension DriverConvertibleType where E : 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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func switchLatest() -> Driver { + let source: Observable = self + .asObservable() + .map { $0.asDriver() } + .switchLatest() + return Driver(source) + } +} + +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 { + + /** + 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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func doOn(eventHandler: (Event) -> Void) + -> Driver { + let source = self.asObservable() + .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. + */ + @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() + .doOn(onNext: onNext, onError: onError, onCompleted: onCompleted) + + return Driver(source) + } +} + +extension DriverConvertibleType { + + /** + 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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func debug(identifier: String = "\(__FILE__):\(__LINE__)") -> Driver { + let source = self.asObservable() + .debug(identifier) + return Driver(source) + } +} + +extension DriverConvertibleType where E: 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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func distinctUntilChanged() + -> Driver { + let source = self.asObservable() + .distinctUntilChanged({ $0 }, comparer: { ($0 == $1) }) + + return Driver(source) + } +} + +extension DriverConvertibleType { + + /** + 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. + */ + @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 }) + 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. + */ + @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) + 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. + */ + @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) + return Driver(source) + } +} + + +extension DriverConvertibleType { + + /** + 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 flatMap(selector: (E) -> Driver) -> Driver { + let source = self.asObservable() + .flatMap(selector) + + return Driver(source) + } +} + +// merge +extension DriverConvertibleType where E : 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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func merge() -> Driver { + let source = self.asObservable() + .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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func merge(maxConcurrent maxConcurrent: Int) + -> Driver { + let source = self.asObservable() + .map { $0.asDriver() } + .merge(maxConcurrent: maxConcurrent) + return Driver(source) + } +} + +// throttle +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. + + `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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func throttle(dueTime: S.TimeInterval, _ scheduler: S) + -> Driver { + let source = self.asObservable() + .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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func debounce(dueTime: S.TimeInterval, _ scheduler: S) + -> Driver { + let source = self.asObservable() + .debounce(dueTime, scheduler) + + return Driver(source) + } +} + +// scan +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. + + 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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func scan(seed: A, accumulator: (A, E) -> A) + -> Driver { + let source = self.asObservable() + .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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + 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. + */ + @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) + } +} + +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. + */ + @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) + } +} + +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/RxCocoa/Common/CocoaUnits/Driver/Driver+Subscription.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver+Subscription.swift new file mode 100644 index 00000000..c1a2fb4d --- /dev/null +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Subscription.swift @@ -0,0 +1,85 @@ +// +// 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 DriverConvertibleType { + /** + 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. + */ + @warn_unused_result(message="http://git.io/rxs.ud") + 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. + */ + @warn_unused_result(message="http://git.io/rxs.ud") + 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. + */ + @warn_unused_result(message="http://git.io/rxs.ud") + 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. + */ + @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) + } + + /** + 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. + */ + @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 new file mode 100644 index 00000000..bbd534c2 --- /dev/null +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver.swift @@ -0,0 +1,178 @@ +// +// 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`. + */ + @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() + } +} + +/** + 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 + + `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) + } + + init(raw: Observable) { + self._source = raw + } + + #if EXPANDABLE_DRIVER + public static func createUnsafe(source: O) -> Driver { + 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 + } +} + +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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public static func empty() -> Driver { + return Driver(raw: RxSwift.empty().subscribeOn(ConcurrentMainScheduler.sharedInstance)) + } + + /** + 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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public static func never() -> Driver { + return Driver(raw: RxSwift.never().subscribeOn(ConcurrentMainScheduler.sharedInstance)) + } + + /** + 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 static func just(element: E) -> Driver { + return Driver(raw: RxSwift.just(element).subscribeOn(ConcurrentMainScheduler.sharedInstance)) + } + +#else + + /** + Returns an empty observable sequence, using the specified scheduler to send out the single `Completed` message. + + - 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)) + } + + /** + 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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public static func never() -> Driver { + return Driver(raw: _never().subscribeOn(ConcurrentMainScheduler.sharedInstance)) + } + + /** + 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 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.toObservable().subscribeOn(ConcurrentMainScheduler.sharedInstance) + return Driver(raw: 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/Driver/ObservableConvertibleType+Driver.swift b/RxCocoa/Common/CocoaUnits/Driver/ObservableConvertibleType+Driver.swift new file mode 100644 index 00000000..8473871b --- /dev/null +++ b/RxCocoa/Common/CocoaUnits/Driver/ObservableConvertibleType+Driver.swift @@ -0,0 +1,63 @@ +// +// 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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func asDriver(onErrorJustReturn onErrorJustReturn: E) -> Driver { + let source = self + .asObservable() + .observeOn(MainScheduler.sharedInstance) + .catchErrorJustReturn(onErrorJustReturn) + 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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func asDriver(onErrorDriveWith onErrorDriveWith: Driver) -> Driver { + let source = self + .asObservable() + .observeOn(MainScheduler.sharedInstance) + .catchError { _ in + onErrorDriveWith.asObservable() + } + 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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func asDriver(onErrorRecover onErrorRecover: (error: ErrorType) -> Driver) -> Driver { + let source = self + .asObservable() + .observeOn(MainScheduler.sharedInstance) + .catchError { error in + onErrorRecover(error: error).asObservable() + } + return Driver(source) + } +} \ No newline at end of file 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 562f0078..f703c7fe 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:") ... } @@ -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/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/Observable+CocoaExtensions.swift b/RxCocoa/Common/Observable+Bind.swift similarity index 72% rename from RxCocoa/Common/Observable+CocoaExtensions.swift rename to RxCocoa/Common/Observable+Bind.swift index 0dce226c..6dea933f 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. @@ -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,8 +49,20 @@ 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) } + + /** + 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. + */ + @warn_unused_result(message="http://git.io/rxs.ud") + public func bindNext(onNext: E -> Void) -> Disposable { + return subscribeNext(onNext) + } } \ No newline at end of file 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/Observables/Implementations/KVOObservable.swift b/RxCocoa/Common/Observables/Implementations/KVOObservable.swift index 06577002..b0dc053f 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)) @@ -99,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); @@ -109,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) } @@ -118,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 @@ -139,7 +142,6 @@ func observeWeaklyKeyPathFor( return nextElementsObservable } } - .switchLatest() } #endif 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..435d11e4 100644 --- a/RxCocoa/Common/Observables/NSObject+Rx+CoreGraphics.swift +++ b/RxCocoa/Common/Observables/NSObject+Rx+CoreGraphics.swift @@ -12,38 +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 +// MARK: Deprecated, CGPoint, CGRect, CGSize are now KVORepresentable -// 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") + @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) } /** @@ -51,21 +32,11 @@ 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 - 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) } /** @@ -73,21 +44,11 @@ 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 - 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) } } @@ -101,21 +62,11 @@ 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 - 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) } /** @@ -123,21 +74,11 @@ 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 - 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) } /** @@ -145,21 +86,11 @@ 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 - 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 d61992c6..8629066a 100644 --- a/RxCocoa/Common/Observables/NSObject+Rx.swift +++ b/RxCocoa/Common/Observables/NSObject+Rx.swift @@ -18,29 +18,52 @@ 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: 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. @@ -58,8 +81,10 @@ 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) + return KVOObservable(object: self, keyPath: keyPath, options: options, retainTarget: retainSelf).asObservable() } } @@ -67,7 +92,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: E.Type, _ keyPath: String, options: NSKeyValueObservingOptions = [.New, .Initial]) -> Observable { + return observeWeaklyKeyPathFor(self, keyPath: keyPath, options: options) + .map { n in + return n as? E + } + } + /** Observes values on `keyPath` starting from `self` with `options` and doesn't retain `self`. @@ -82,6 +128,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/RxCocoa/Common/Observables/NSURLSession+Rx.swift b/RxCocoa/Common/Observables/NSURLSession+Rx.swift index 164f88f6..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) } @@ -74,7 +116,8 @@ extension NSURLSession { - parameter request: URL request. - returns: Observable sequence of URL responses. */ - public func rx_response(request: NSURLRequest) -> Observable<(NSData!, NSURLResponse!)> { + @warn_unused_result(message="http://git.io/rxs.uo") + public func rx_response(request: NSURLRequest) -> Observable<(NSData!, NSHTTPURLResponse)> { return create { observer in // smart compiler should be able to optimize this out @@ -92,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) } @@ -126,20 +174,14 @@ 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 { - 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) } } } @@ -161,9 +203,14 @@ 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: []) + do { + return try NSJSONSerialization.JSONObjectWithData(data ?? NSData(), options: []) + } catch let error { + throw RxCocoaURLError.DeserializationError(error: error) + } } } @@ -184,6 +231,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/RxCocoa/Common/RxCocoa.swift b/RxCocoa/Common/RxCocoa.swift index fc54151e..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) { @@ -65,11 +72,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 +92,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 +101,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 +114,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/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/Info.plist b/RxCocoa/Info.plist index c1620d0a..7e7479f0 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 @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0 + 2.0.0 CFBundleSignature ???? CFBundleVersion diff --git a/RxCocoa/OSX/NSControl+Rx.swift b/RxCocoa/OSX/NSControl+Rx.swift index f4b611ea..bb7c76f6 100644 --- a/RxCocoa/OSX/NSControl+Rx.swift +++ b/RxCocoa/OSX/NSControl+Rx.swift @@ -12,37 +12,75 @@ 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 + create { [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 create { [weak self] observer in + guard let control = self else { + observer.on(.Completed) + return NopDisposable.instance + } + observer.on(.Next(getter())) - } - - return observer - }.takeUntil(rx_deallocated) - - return ControlProperty(source: source, observer: ObserverOf { event in + + 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): 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..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,9 +58,11 @@ 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: ObserverOf { [weak self] event in + return ControlProperty(source: source, observer: AnyObserver { [weak self] event in MainScheduler.ensureExecutingOnScheduler() switch event { 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/UIBarButtonItem+Rx.swift b/RxCocoa/iOS/UIBarButtonItem+Rx.swift index ce8a4f01..17c496af 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 { @@ -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 = create { [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/UIButton+Rx.swift b/RxCocoa/iOS/UIButton+Rx.swift index 9621acb2..6c16240c 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_primaryAction: ControlEvent { + return rx_controlEvents(.PrimaryActionTriggered) + } +} + +#endif diff --git a/RxCocoa/iOS/UICollectionView+Rx.swift b/RxCocoa/iOS/UICollectionView+Rx.swift index 92d1518b..0e729c2f 100644 --- a/RxCocoa/iOS/UICollectionView+Rx.swift +++ b/RxCocoa/iOS/UICollectionView+Rx.swift @@ -143,14 +143,49 @@ 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)! + let source: Observable = rx_itemSelected.flatMap { [weak self] indexPath -> Observable in + guard let view = self else { + return empty() + } + + return just(try view.rx_modelAtIndexPath(indexPath)) } 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_itemsWith*` methods was used.") + + guard let element = dataSource.modelAtIndex(indexPath.item) else { + throw RxCocoaError.ItemsNotYetBound(object: self) + } + + return element + } +} +#endif + +#if os(tvOS) + +extension UICollectionView { + + /** + Reactive wrapper for `delegate` message `collectionView:didUpdateFocusInContext:withAnimationCoordinator:`. + */ + public var rx_didUpdateFocusInContextWithAnimationCoordinator: ControlEvent<(context: UIFocusUpdateContext, animationCoordinator: UIFocusAnimationCoordinator)> { + + let source = rx_delegate.observe("collectionView: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 diff --git a/RxCocoa/iOS/UIControl+Rx.swift b/RxCocoa/iOS/UIControl+Rx.swift index 854fb7d2..82371b10 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 { @@ -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 = create { [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 = create { [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())) } @@ -71,9 +80,9 @@ 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 { case .Next(let value): setter(value) diff --git a/RxCocoa/iOS/UIGestureRecognizer+Rx.swift b/RxCocoa/iOS/UIGestureRecognizer+Rx.swift index 16685d1d..114f7819 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 } } @@ -57,12 +57,17 @@ 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 { + 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/UIImageView+Rx.swift b/RxCocoa/iOS/UIImageView+Rx.swift index 7c112c24..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: 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..fdab2069 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 { @@ -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/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/UITableView+Rx.swift b/RxCocoa/iOS/UITableView+Rx.swift index 97ca683f..fbfa0080 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) } /** @@ -187,15 +187,50 @@ 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.") - - return dataSource.modelAtIndex(ip.item)! + let source: Observable = rx_itemSelected.flatMap { [weak self] indexPath -> Observable in + guard let view = self else { + return empty() + } + + return just(try view.rx_modelAtIndexPath(indexPath)) } return ControlEvent(source: source) } + /** + Synchronous helper method for retrieving a model at indexPath through a reactive data source + */ + public func rx_modelAtIndexPath(indexPath: NSIndexPath) throws -> T { + 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 RxCocoaError.ItemsNotYetBound(object: self) + } + + 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 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/Changeset.swift b/RxDataSourceStarterKit/Changeset.swift index 161bfa98..f7a3949c 100644 --- a/RxDataSourceStarterKit/Changeset.swift +++ b/RxDataSourceStarterKit/Changeset.swift @@ -13,20 +13,22 @@ 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 reloadData: Bool = false + var finalSections: [S] = [] var insertedSections: [Int] = [] @@ -43,10 +45,12 @@ public struct Changeset : CustomStringConvertible { var initialValue = Changeset() initialValue.insertedSections = Array(0 ..< sections.count) initialValue.finalSections = sections + initialValue.reloadData = true + 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/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/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/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/RxDataSourceStarterKit/Differentiator.swift b/RxDataSourceStarterKit/Differentiator.swift index a7789de4..995333bf 100644 --- a/RxDataSourceStarterKit/Differentiator.swift +++ b/RxDataSourceStarterKit/Differentiator.swift @@ -8,14 +8,31 @@ import Foundation -enum EditEvent : CustomStringConvertible { +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 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,39 +50,48 @@ 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)" } } } -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 @@ -76,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) } } @@ -185,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. @@ -209,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 @@ -236,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 9a5726e9..38b2aa91 100644 --- a/RxExample/RxExample.xcodeproj/project.pbxproj +++ b/RxExample/RxExample.xcodeproj/project.pbxproj @@ -17,6 +17,47 @@ 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 */; }; + 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 */; }; + 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 */; }; + 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 */; }; + 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 */; }; + 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 */; }; @@ -48,7 +89,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 */; }; @@ -63,189 +103,41 @@ 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 */; }; - 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 */; }; + 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 */; }; 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 */; }; + 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 */; }; - 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 */; }; 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 */; }; @@ -253,7 +145,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 */; }; @@ -265,9 +156,166 @@ 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 */; }; + 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 */; }; + 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 */; }; + 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 /* 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 */; }; + 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 */; }; + 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 */; }; + C89465671BC6C2BC0055219D /* ControlEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89465191BC6C2BC0055219D /* ControlEvent.swift */; }; + C89465691BC6C2BC0055219D /* ControlProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = C894651B1BC6C2BC0055219D /* ControlProperty.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,6 +340,31 @@ 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 */; }; + 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 */; }; + 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 */; }; + 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 */; }; EC91FB951BBA144400973245 /* GitHubSearchRepositoriesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC91FB941BBA144400973245 /* GitHubSearchRepositoriesViewController.swift */; }; /* End PBXBuildFile section */ @@ -428,194 +501,56 @@ 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 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; }; + 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 = ""; }; 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 = ""; }; + 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 = ""; }; + 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 = ""; }; 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 = ""; }; 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 = ""; }; @@ -628,6 +563,178 @@ 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; lineEnding = 0; path = AnonymousObservable.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 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; 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 = ""; }; + 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 = ""; }; + 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; 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 /* 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 = ""; }; + 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; 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 = ""; }; + 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 = ""; }; + C89465191BC6C2BC0055219D /* ControlEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlEvent.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 = ""; }; + 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; 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; 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 = ""; }; + 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; 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 = ""; }; C8A468EB1B8A8BC900BF917B /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -635,6 +742,8 @@ C8A468EF1B8A8BD000BF917B /* RxBlocking.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = RxBlocking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C8A7501E1B94E77C00D8D046 /* RxDataSourceStarterKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxDataSourceStarterKit.swift; sourceTree = ""; }; C8AF26F11B49ABD300131C03 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + C8B145041BD2E45200267DCE /* ImmediateScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImmediateScheduler.swift; sourceTree = ""; }; + C8B145051BD2E45200267DCE /* ConcurrentMainScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConcurrentMainScheduler.swift; sourceTree = ""; }; C8C46DA31B47F7110020D71E /* CollectionViewImageCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewImageCell.swift; sourceTree = ""; }; C8C46DA41B47F7110020D71E /* WikipediaImageCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WikipediaImageCell.xib; sourceTree = ""; }; C8C46DA51B47F7110020D71E /* WikipediaSearchCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WikipediaSearchCell.swift; sourceTree = ""; }; @@ -650,6 +759,19 @@ 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 = ""; }; + 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 = ""; }; + 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 = ""; }; EC91FB941BBA144400973245 /* GitHubSearchRepositoriesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubSearchRepositoriesViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -740,6 +862,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 = ( @@ -792,7 +929,6 @@ 07E3C2321B03605B0010338D /* Dependencies.swift */, C890A65C1AEC084100AFF7E6 /* ViewController.swift */, C833670F1AD029AE00C668A7 /* Example.swift */, - C83367211AD029AE00C668A7 /* Wireframe.swift */, C8DF92D11B0B2F8C009BCF9A /* OSX */, C8DF92C71B0B2F84009BCF9A /* iOS */, C83366E01AD0293800C668A7 /* Supporting Files */, @@ -812,10 +948,17 @@ C83367101AD029AE00C668A7 /* Services */ = { isa = PBXGroup; children = ( + C809E9791BE6841C0058D948 /* Wireframe.swift */, C83367111AD029AE00C668A7 /* HtmlParsing.swift */, C83367121AD029AE00C668A7 /* ImageService.swift */, C8A2A2C71B4049E300F11F09 /* PseudoRandomGenerator.swift */, C8A2A2CA1B404A1200F11F09 /* Randomizer.swift */, + C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */, + B18F3BBB1BD92EC8000AAC79 /* Reachability.swift */, + B18F3BE11BDB2E8F000AAC79 /* ReachabilityService.swift */, + B1604CB41BE49F8D002E1279 /* DownloadableImage.swift */, + B1604CC81BE5BBFA002E1279 /* UIImageView+DownloadableImage.swift */, + C809E97C1BE697100058D948 /* UIImage+Extensions.swift */, ); path = Services; sourceTree = ""; @@ -823,161 +966,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 = ( @@ -989,187 +983,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 = ( - 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 = ( @@ -1241,13 +1054,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; @@ -1266,6 +1081,369 @@ 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 /* AnyObserver.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 = ( + C84CC56B1BDD08F500E06A64 /* LockOwnerType.swift */, + C84CC56C1BDD08F500E06A64 /* SynchronizedDisposeType.swift */, + C84CC56D1BDD08F500E06A64 /* SynchronizedOnType.swift */, + C84CC56E1BDD08F500E06A64 /* SynchronizedSubscribeType.swift */, + C84CC56F1BDD08F500E06A64 /* SynchronizedUnsubscribeType.swift */, + 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 = ( + 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 */, + 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 = ( + CB883B5F1BE3AC72000AC2EE /* AddRef.swift */, + C89464491BC6C2B00055219D /* Amb.swift */, + C894644A1BC6C2B00055219D /* AnonymousObservable.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 */, + C84CC52D1BDC344100E06A64 /* ElementAt.swift */, + C89464591BC6C2B00055219D /* Empty.swift */, + C894645A1BC6C2B00055219D /* FailWith.swift */, + C894645B1BC6C2B00055219D /* Filter.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 */, + C8F6A1361BEF9DD4007DF367 /* RetryWhen.swift */, + C894646A1BC6C2B00055219D /* Sample.swift */, + C894646B1BC6C2B00055219D /* Scan.swift */, + C83100681BF7F4CA00AAE3CD /* Sequence.swift */, + C89CDB611BCC45DC002063D9 /* ShareReplay1.swift */, + CB30D9ED1BF106260084C1C0 /* SingleAsync.swift */, + C894646C1BC6C2B00055219D /* Sink.swift */, + C894646D1BC6C2B00055219D /* Skip.swift */, + C89CDB621BCC45DC002063D9 /* SkipUntil.swift */, + C80DDE7A1BCDA952006A1832 /* SkipWhile.swift */, + C894646E1BC6C2B00055219D /* StartWith.swift */, + C894646F1BC6C2B00055219D /* SubscribeOn.swift */, + C89464701BC6C2B00055219D /* Switch.swift */, + C89464711BC6C2B00055219D /* Take.swift */, + B1B7C3CF1BE006870076934E /* TakeLast.swift */, + C89464721BC6C2B00055219D /* TakeUntil.swift */, + C89464731BC6C2B00055219D /* TakeWhile.swift */, + C89464741BC6C2B00055219D /* Throttle.swift */, + 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 */, + ); + 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 = ( + C8F6A1041BEF9D83007DF367 /* AnonymousInvocable.swift */, + C8F6A1051BEF9D83007DF367 /* InvocableScheduledItem.swift */, + C8F6A1061BEF9D83007DF367 /* InvocableType.swift */, + C8F6A1071BEF9D83007DF367 /* ScheduledItemType.swift */, + C894648E1BC6C2B00055219D /* ConcurrentDispatchQueueScheduler.swift */, + C8B145051BD2E45200267DCE /* ConcurrentMainScheduler.swift */, + C894648F1BC6C2B00055219D /* CurrentThreadScheduler.swift */, + C89464901BC6C2B00055219D /* DispatchQueueSchedulerPriority.swift */, + C8B145041BD2E45200267DCE /* ImmediateScheduler.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 = ( + C839740F1BF77406004F02CC /* KVORepresentable.swift */, + C83974101BF77406004F02CC /* KVORepresentable+CoreGraphics.swift */, + C83974111BF77406004F02CC /* KVORepresentable+Swift.swift */, + 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 = ( + C80DDEC11BCE9041006A1832 /* Driver */, + C89465191BC6C2BC0055219D /* ControlEvent.swift */, + C894651B1BC6C2BC0055219D /* ControlProperty.swift */, + ); + path = CocoaUnits; + sourceTree = ""; + }; + 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 */, + 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 = ( @@ -1299,6 +1477,7 @@ isa = PBXGroup; children = ( EC91FB941BBA144400973245 /* GitHubSearchRepositoriesViewController.swift */, + C80397481BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift */, ); name = "08 AutoLoading"; path = AutoLoading; @@ -1494,16 +1673,17 @@ 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 */, + C80DDED51BCE9046006A1832 /* Driver+Operators+arity.tt in Resources */, + C89464B61BC6C2B00055219D /* Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1535,209 +1715,257 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C84CC58B1BDD486300E06A64 /* LockOwnerType.swift in Sources */, + C89465971BC6C2BC0055219D /* UIScrollView+Rx.swift in Sources */, C8297E2F1B6CF905000589EA /* RxTableViewSectionedAnimatedDataSource.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 */, + C8F6A1381BEF9DF6007DF367 /* RetryWhen.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 */, + 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 */, + C83974121BF77406004F02CC /* KVORepresentable.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 */, + C83974241BF77413004F02CC /* NSObject+Rx+RawRepresentable.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 */, + C80DDED81BCE9046006A1832 /* Driver.swift in Sources */, + C80DDED71BCE9046006A1832 /* Driver+Subscription.swift in Sources */, + C8F6A1321BEF9DA3007DF367 /* RecursiveScheduler.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 */, + C84CC58C1BDD486300E06A64 /* SynchronizedDisposeType.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 */, + B1604CC41BE5B8CE002E1279 /* DownloadableImage.swift in Sources */, + C80DDED61BCE9046006A1832 /* Driver+Operators.swift in Sources */, + C89464B81BC6C2B00055219D /* Observable.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 */, + C89464BB1BC6C2B00055219D /* AnonymousObservable.swift in Sources */, + C89465991BC6C2BC0055219D /* UISegmentedControl+Rx.swift in Sources */, + C87335781BF7CC0C00E536E6 /* ObservableConvertibleType+Differentiator.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 */, + B1B7C3D01BE006870076934E /* TakeLast.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 */, - C86409FA1BA5909000D3C4E8 /* Variable.swift in Sources */, + C894649E1BC6C2B00055219D /* Cancelable.swift in Sources */, + C89464E01BC6C2B00055219D /* SubscribeOn.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 */, + 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 */, + 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 */, - C86409AC1BA5909000D3C4E8 /* AnonymousObservable.swift in Sources */, - C84B3A401BA4345A001B7D88 /* NSURLSession+Rx.swift in Sources */, + C89464F21BC6C2B00055219D /* Observable+StandardSequenceOperators.swift in Sources */, + C89464CC1BC6C2B00055219D /* Filter.swift in Sources */, + C80DDED31BCE9046006A1832 /* ControlProperty+Driver.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 */, - 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 */, + C80DDED21BCE9046006A1832 /* ControlEvent+Driver.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 */, + B1604CC21BE5B895002E1279 /* ReachabilityService.swift in Sources */, + C89464DE1BC6C2B00055219D /* Skip.swift in Sources */, + C89464DB1BC6C2B00055219D /* Sample.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 */, + D2245A191BD5654C00E7146F /* WithLatestFrom.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 */, + C809E97E1BE697100058D948 /* UIImage+Extensions.swift in Sources */, + C89464E61BC6C2B00055219D /* Timer.swift in Sources */, C8297E3E1B6CF905000589EA /* DetailViewController.swift in Sources */, - C86409DB1BA5909000D3C4E8 /* Observable+Binding.swift in Sources */, + C83974131BF77406004F02CC /* KVORepresentable+CoreGraphics.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 */, + 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 */, - 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 */, + 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 */, + 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 */, + C89464D71BC6C2B00055219D /* Range.swift in Sources */, + C803974A1BD3E9A6009D8B26 /* GitHubSearchRepositoriesAPI.swift in Sources */, + C84CC52E1BDC344100E06A64 /* ElementAt.swift in Sources */, C8297E431B6CF905000589EA /* GitHubAPI.swift in Sources */, - C86409AE1BA5909000D3C4E8 /* Buffer.swift in Sources */, + C8F6A1331BEF9DA3007DF367 /* ScheduledItem.swift in Sources */, + 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 */, - 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 */, + C83974231BF77413004F02CC /* NSObject+Rx+KVORepresentable.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 */, + C809E97B1BE6841C0058D948 /* Wireframe.swift in Sources */, + C89465811BC6C2BC0055219D /* ItemEvents.swift in Sources */, + 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 */, + C89464BE1BC6C2B00055219D /* Catch.swift in Sources */, + C89CDB721BCC45EE002063D9 /* SkipUntil.swift in Sources */, + B1604CCB1BE5BC45002E1279 /* UIImageView+DownloadableImage.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 */, + C89464F71BC6C2B00055219D /* ObserverBase.swift in Sources */, + C89465951BC6C2BC0055219D /* UIImageView+Rx.swift in Sources */, + C89464E31BC6C2B00055219D /* TakeUntil.swift in Sources */, + C89464FB1BC6C2B00055219D /* Rx.swift in Sources */, + C84CC5911BDD48B800E06A64 /* SubscriptionDisposable.swift in Sources */, + C8F6A1341BEF9DA3007DF367 /* SchedulerServices+Emulation.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 */, + C8F6A12A1BEF9DA3007DF367 /* ScheduledItemType.swift in Sources */, + C8F6A1351BEF9DA3007DF367 /* SerialDispatchQueueScheduler.swift in Sources */, C8297E4D1B6CF905000589EA /* RxTableViewSectionedReloadDataSource.swift in Sources */, - C86409A51BA5909000D3C4E8 /* Error.swift in Sources */, - C86409971BA5909000D3C4E8 /* Disposable.swift in Sources */, + C89465931BC6C2BC0055219D /* UIDatePicker+Rx.swift in Sources */, + C84CC58F1BDD486300E06A64 /* SynchronizedUnsubscribeType.swift in Sources */, C8297E4E1B6CF905000589EA /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */, - C84B3A3B1BA4345A001B7D88 /* KVOObservable.swift in Sources */, - C86409D51BA5909000D3C4E8 /* Timer.swift in Sources */, - C86409D31BA5909000D3C4E8 /* TakeWhile.swift in Sources */, - C8297E4F1B6CF905000589EA /* Wireframe.swift in Sources */, + 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 */, + 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 */, - 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 */, 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 */, + C80DDED91BCE9046006A1832 /* ObservableConvertibleType+Driver.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 */, + 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 */, - C86409CA1BA5909000D3C4E8 /* Sample.swift in Sources */, + C894659D1BC6C2BC0055219D /* UITableView+Rx.swift in Sources */, + C8F6A12C1BEF9DA3007DF367 /* ConcurrentMainScheduler.swift in Sources */, + C894657F1BC6C2BC0055219D /* RxCollectionViewReactiveArrayDataSource.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 */, + 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 */, + CB883B501BE3AC54000AC2EE /* BooleanDisposable.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; }; @@ -1745,10 +1973,12 @@ 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 */, 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 */, @@ -1761,12 +1991,15 @@ 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 */, + C809E97A1BE6841C0058D948 /* Wireframe.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 */, @@ -1778,15 +2011,19 @@ 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 */, + 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 */, 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 */, ); @@ -1796,18 +2033,33 @@ isa = PBXSourcesBuildPhase; 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 */, - C88BB8C61B07E6C90064D411 /* Wireframe.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; diff --git a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift new file mode 100644 index 00000000..115a3b95 --- /dev/null +++ b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesAPI.swift @@ -0,0 +1,264 @@ +// +// GitHubSearchRepositoriesAPI.swift +// RxExample +// +// Created by Krunoslav Zaher on 10/18/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE +import RxSwift +#endif + +/** + Parsed GitHub respository. +*/ +struct Repository: CustomDebugStringConvertible { + var name: String + var url: String + + init(name: String, url: String) { + self.name = name + self.url = url + } +} + +extension Repository { + var debugDescription: String { + return "\(name) | \(url)" + } +} + +/** +ServiceState state. +*/ +enum ServiceState { + case Online + case Offline +} + +/** + Raw response from GitHub API +*/ +enum SearchRepositoryResponse { + /** + 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(wireframe: DefaultWireframe()) + + let activityIndicator = ActivityIndicator() + + // 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=\"([^\"]*)\"" + 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) + // Here we go again + .startWith(RepositoriesState.empty) + } + + 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() + } + } + + /** + 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, httpResponse -> SearchRepositoryResponse in + if httpResponse.statusCode == 403 { + return .LimitExceeded + } + + 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 .Repositories(repositories: repositories, nextURL: nextURL) + } + .retryOnBecomesReachable(.ServiceOffline, reachabilityService: ReachabilityService.sharedReachabilityService) + } + + 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 3ec656d3..c41bfc90 100644 --- a/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift +++ b/RxExample/RxExample/Examples/AutoLoading/GitHubSearchRepositoriesViewController.swift @@ -12,160 +12,29 @@ 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)" - } +struct Colors { + static let OfflineColor = UIColor(red: 1.0, green: 0.6, blue: 0.6, alpha: 1.0) + static let OnlineColor = nil as UIColor? } -enum SearchRepositoryResponse { - case Repositories([Repository]) - case LimitExceeded -} +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 -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).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") + self.navigationBar.backgroundColor = isOffline + ? Colors.OfflineColor + : Colors.OnlineColor } - - 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) + case .Error(let error): + bindingErrorToInterface(error) + case .Completed: + break } - .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) } } } @@ -177,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() { @@ -193,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 @@ -210,10 +72,6 @@ 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)) - .addDisposableTo(disposeBag) let loadNextPageTrigger = tableView.rx_contentOffset .flatMap { offset in @@ -222,27 +80,32 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat : empty() } - searchBar.rx_text + let searchResult = searchBar.rx_text.asDriver() .throttle(0.3, $.mainScheduler) .distinctUntilChanged() - .map { query -> Observable in + .flatMapLatest { query -> Driver in if query.isEmpty { - return just(.Repositories([])) + return Drive.just(RepositoriesState.empty) } else { return GitHubSearchRepositoriesAPI.sharedAPI.search(query, loadNextPageTrigger: loadNextPageTrigger) - .retry(3) - .catchErrorJustReturn(.Repositories([])) + .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) @@ -258,6 +121,16 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat // so normal delegate customization can also be used tableView.rx_setDelegate(self) .addDisposableTo(disposeBag) + + // activity indicator in status bar + // { + GitHubSearchRepositoriesAPI.sharedAPI.activityIndicator + .distinctUntilChanged() + .driveNext { active in + UIApplication.sharedApplication().networkActivityIndicatorVisible = active + } + .addDisposableTo(disposeBag) + // } } // MARK: Table view delegate @@ -265,4 +138,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/Calculator/CalculatorViewController.swift b/RxExample/RxExample/Examples/Calculator/CalculatorViewController.swift index 9620320f..201e8327 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] = [ @@ -98,7 +98,7 @@ class CalculatorViewController: ViewController { ] commands - .asObservable() + .toObservable() .merge() .scan(CLEAR_STATE) { [unowned self] a, x in return self.tranformState(a, x) @@ -122,7 +122,7 @@ class CalculatorViewController: ViewController { self?.lastSignLabel.text = "" } } - .addDisposableTo(diposeBag) + .addDisposableTo(disposeBag) } func tranformState(a: CalState, _ x: Action) -> CalState { diff --git a/RxExample/RxExample/Examples/GitHubSignup/GitHubAPI/GitHubAPI.swift b/RxExample/RxExample/Examples/GitHubSignup/GitHubAPI/GitHubAPI.swift index b351f0f4..b310e6aa 100644 --- a/RxExample/RxExample/Examples/GitHubSignup/GitHubAPI/GitHubAPI.swift +++ b/RxExample/RxExample/Examples/GitHubSignup/GitHubAPI/GitHubAPI.swift @@ -58,13 +58,8 @@ class GitHubAPI { }) 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) @@ -73,8 +68,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/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift b/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift index 09eb1755..249338e2 100644 --- a/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift +++ b/RxExample/RxExample/Examples/GitHubSignup/Views/GitHubSignupViewController.swift @@ -13,12 +13,30 @@ 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 +// 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 @@ -30,7 +48,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)) } @@ -93,6 +111,15 @@ 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() + } var disposeBag = DisposeBag() @@ -108,9 +135,8 @@ class GitHubSignupViewController : ViewController { let validationColor: UIColor if let valid = v.valid { - validationColor = valid ? okColor : errorColor - } - else { + validationColor = valid ? ValidationColors.okColor : ValidationColors.errorColor + } else { validationColor = UIColor.grayColor() } @@ -128,44 +154,42 @@ class GitHubSignupViewController : ViewController { super.viewDidLoad() let tapBackground = UITapGestureRecognizer(target: self, action: Selector("dismissKeyboard:")) - tapBackground.numberOfTouchesRequired = 1 view.addGestureRecognizer(tapBackground) - self.disposeBag = DisposeBag() - let API = self.API let validationService = ValidationService(API: API) - - let username = usernameOutlet.rx_text - let password = passwordOutlet.rx_text - let repeatPassword = repeatedPasswordOutlet.rx_text - let signupSampler = self.signupOutlet.rx_tap + + // 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 - .map { username in + .flatMapLatest { username in return validationService.validateUsername(username) } - .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) 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) @@ -174,8 +198,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( @@ -220,15 +247,17 @@ class GitHubSignupViewController : ViewController { } } .addDisposableTo(disposeBag) + } // 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. + // 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") 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 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/ViewModels/SearchResultViewModel.swift b/RxExample/RxExample/Examples/WikipediaImageSearch/ViewModels/SearchResultViewModel.swift index 867cc668..e69aa90d 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 @@ -51,6 +51,7 @@ class SearchResultViewModel { return "\(searchResult.title) loading ..." } } + .retryOnBecomesReachable("⚠️ Service offline ⚠️", reachabilityService: ReachabilityService.sharedReachabilityService) } func configureImageURLs() -> Observable<[NSURL]> { @@ -64,7 +65,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..2f0f097a 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 @@ -31,13 +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 - .catchErrorJustReturn([]) + .asDriver(onErrorJustReturn: []) } - .switchLatest() .map { results in results.map { SearchResultViewModel( @@ -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/CollectionViewImageCell.swift b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/CollectionViewImageCell.swift index c004f398..daa97598 100644 --- a/RxExample/RxExample/Examples/WikipediaImageSearch/Views/CollectionViewImageCell.swift +++ b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/CollectionViewImageCell.swift @@ -17,15 +17,16 @@ 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? + .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 eeb6a509..4d0dfa62 100644 --- a/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchCell.swift +++ b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchCell.swift @@ -33,21 +33,16 @@ 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 - let loadingPlaceholder: UIImage? = nil - - cell.image = self.imageService.imageFromURL(URL) - .map { $0 as UIImage? } - .catchErrorJustReturn(nil) - .startWith(loadingPlaceholder) - } + .drive(self.imagesOutlet.rx_itemsWithCellIdentifier("ImageCell")) { [unowned self] (_, URL, cell: CollectionViewImageCell) in + cell.downloadableImage = self.imageService.imageFromURL(URL) + } .addDisposableTo(disposeBag) self.disposeBag = disposeBag @@ -62,4 +57,5 @@ public class WikipediaSearchCell: UITableViewCell { deinit { } + } diff --git a/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchViewController.swift b/RxExample/RxExample/Examples/WikipediaImageSearch/Views/WikipediaSearchViewController.swift index 77af4748..53ed6110 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() } @@ -61,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/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/RxExample/RxExample/Services/ActivityIndicator.swift b/RxExample/RxExample/Services/ActivityIndicator.swift new file mode 100644 index 00000000..f86c5b34 --- /dev/null +++ b/RxExample/RxExample/Services/ActivityIndicator.swift @@ -0,0 +1,85 @@ +// +// ActivityIndicator.swift +// RxExample +// +// Created by Krunoslav Zaher on 10/18/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation +#if !RX_NO_MODULE +import RxSwift +import RxCocoa +#endif + +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/Services/DownloadableImage.swift b/RxExample/RxExample/Services/DownloadableImage.swift new file mode 100644 index 00000000..5df0e019 --- /dev/null +++ b/RxExample/RxExample/Services/DownloadableImage.swift @@ -0,0 +1,23 @@ +// +// DownloadableImage.swift +// RxExample +// +// Created by Vodovozov Gleb on 31.10.2015. +// Copyright © 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 DownloadableImage{ + case Content(image:Image) + case OfflinePlaceholder + +} diff --git a/RxExample/RxExample/Services/ImageService.swift b/RxExample/RxExample/Services/ImageService.swift index eb5cc7e7..d8b4358e 100644 --- a/RxExample/RxExample/Services/ImageService.swift +++ b/RxExample/RxExample/Services/ImageService.swift @@ -19,7 +19,7 @@ import RxCocoa #endif protocol ImageService { - func imageFromURL(URL: NSURL) -> Observable + func imageFromURL(URL: NSURL) -> Observable } class DefaultImageService: ImageService { @@ -29,19 +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 @@ -49,41 +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(next: { data in - self.imageDataCache.setObject(data, forKey: URL) - }) - .flatMap(self.decodeImage) + 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(next: { image in - self.imageCache.setObject(image, forKey: URL) - }) - }.observeOn($.mainScheduler) + } + + /** + 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) + .startWith(.Content(image: Image())) } } 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 + } +} diff --git a/RxExample/RxExample/Services/ReachabilityService.swift b/RxExample/RxExample/Services/ReachabilityService.swift new file mode 100644 index 00000000..9a9225cc --- /dev/null +++ b/RxExample/RxExample/Services/ReachabilityService.swift @@ -0,0 +1,56 @@ +// +// ReachabilityService.swift +// RxExample +// +// Created by Vodovozov Gleb on 22.10.2015. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +#if !RX_NO_MODULE +import RxSwift +#endif + +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 + .filter { $0 == .Reachable } + .flatMap { _ in failWith(e) } + .startWith(valueOnFailure) + } + .retry() + } +} 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/UIImageView+DownloadableImage.swift b/RxExample/RxExample/Services/UIImageView+DownloadableImage.swift new file mode 100644 index 00000000..0a427599 --- /dev/null +++ b/RxExample/RxExample/Services/UIImageView+DownloadableImage.swift @@ -0,0 +1,54 @@ +// +// 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 + + guard let strongSelf = self else { return } + MainScheduler.ensureExecutingOnScheduler() + + switch event{ + case .Next(let value): + for subview in strongSelf.subviews { + subview.removeFromSuperview() + } + switch value{ + case .Content(let image): + strongSelf.rx_image.onNext(image) + case .OfflinePlaceholder: + let label = UILabel(frame: strongSelf.bounds) + label.textAlignment = .Center + label.font = UIFont.systemFontOfSize(35) + label.text = "⚠️" + strongSelf.addSubview(label) + } + case .Error(let error): + bindingErrorToInterface(error) + break + case .Completed: + break + } + } + } +} +#endif 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 c1f21804..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 @@ - + @@ -451,8 +451,8 @@ - (seed: A, _ accumulator: (A, E) throws -> A) -> 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/RxSwift/Observables/Observable+Binding.swift b/RxSwift/Observables/Observable+Binding.swift index 5bf5454b..c0912cd2 100644 --- a/RxSwift/Observables/Observable+Binding.swift +++ b/RxSwift/Observables/Observable+Binding.swift @@ -8,7 +8,7 @@ import Foundation -// multicast +// MARK: multicast extension ObservableType { @@ -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( @@ -48,7 +50,7 @@ extension ObservableType { } } -// publish +// MARK: publish extension ObservableType { @@ -59,12 +61,13 @@ 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()) } } -// replay +// MARK: replay extension ObservableType { @@ -76,13 +79,14 @@ 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)) } } -// refcount +// MARK: refcount extension ConnectableObservableType { @@ -91,12 +95,13 @@ 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) } } -// share +// MARK: share extension ObservableType { @@ -107,12 +112,13 @@ 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() } } -// shareReplay +// MARK: shareReplay extension ObservableType { @@ -124,8 +130,14 @@ 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 { - 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..2bf4de71 100644 --- a/RxSwift/Observables/Observable+Concurrency.swift +++ b/RxSwift/Observables/Observable+Concurrency.swift @@ -8,7 +8,7 @@ import Foundation -// observeOn +// MARK: observeOn extension ObservableType { @@ -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 { @@ -32,7 +33,7 @@ extension ObservableType { } } -// subscribeOn +// MARK: subscribeOn extension ObservableType { @@ -49,8 +50,9 @@ 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.asObservable(), scheduler: scheduler) + return SubscribeOn(source: self, scheduler: scheduler) } } \ No newline at end of file diff --git a/RxSwift/Observables/Observable+Creation.swift b/RxSwift/Observables/Observable+Creation.swift index 9fcb906b..b7dfc667 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. @@ -16,33 +16,36 @@ 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. */ -public func create(subscribe: (ObserverOf) -> Disposable) -> Observable { +@warn_unused_result(message="http://git.io/rxs.uo") +public func create(subscribe: (AnyObserver) -> Disposable) -> Observable { return AnonymousObservable(subscribe) } -// empty +// MARK: empty /** Returns an empty observable sequence, using the specified scheduler to send out the single `Completed` message. - returns: An observable sequence with no elements. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func empty() -> Observable { return Empty() } -// never +// MARK: never /** 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. */ +@warn_unused_result(message="http://git.io/rxs.uo") public func never() -> Observable { return Never() } -// just +// MARK: just /** Returns an observable sequence that contains a single element. @@ -50,26 +53,35 @@ 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) } -// of +/** +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. */ -public func sequenceOf(elements: E ...) -> Observable { - return AnonymousObservable { observer in - for element in elements { - observer.on(.Next(element)) - } - - observer.on(.Completed) - return NopDisposable.instance - } +@warn_unused_result(message="http://git.io/rxs.uo") +public func sequenceOf(elements: E ..., scheduler: ImmediateSchedulerType? = nil) -> Observable { + return Sequence(elements: elements, scheduler: scheduler) } @@ -79,30 +91,48 @@ 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) } } -// fail +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) + } +} + +// MARK: fail /** 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) } -// defer +// MARK: defer /** Returns an observable sequence that invokes the specified factory function whenever a new observer subscribes. @@ -110,6 +140,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 +156,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,15 +169,8 @@ 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") - } - - if start &+ (count - 1) < start { - rxFatalError("overflow of count") - } - return RangeProducer(start: start, count: count, scheduler: scheduler) } @@ -156,6 +181,19 @@ 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) } + +/** +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. +*/ +@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..41df23f8 100644 --- a/RxSwift/Observables/Observable+Debug.swift +++ b/RxSwift/Observables/Observable+Debug.swift @@ -8,7 +8,7 @@ import Foundation -// debug +// MARK: debug extension ObservableType { @@ -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 ccf30b25..a425d747 100644 --- a/RxSwift/Observables/Observable+Multiple.swift +++ b/RxSwift/Observables/Observable+Multiple.swift @@ -8,9 +8,9 @@ import Foundation -// combineLatest +// MARK: 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. @@ -18,14 +18,15 @@ extension CollectionType where Generator.Element : ObservableType { - 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) } } -// zip +// MARK: 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. @@ -33,14 +34,15 @@ extension CollectionType where Generator.Element : ObservableType { - 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) } } -// switch +// MARK: switch -extension ObservableType where E : ObservableType { +extension ObservableType where E : ObservableConvertibleType { /** Transforms an observable sequence of observable sequences into an observable sequence @@ -51,65 +53,83 @@ 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. */ - + @warn_unused_result(message="http://git.io/rxs.uo") public func switchLatest() -> Observable { - return Switch(sources: self.asObservable()) + return Switch(source: asObservable()) } } -// concat +// MARK: concat -extension SequenceType where Generator.Element : ObservableType { +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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func concat(second: O) -> Observable { + return [asObservable(), second.asObservable()].concat() + } +} + +extension SequenceType where Generator.Element : ObservableConvertibleType { /** 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. */ + @warn_unused_result(message="http://git.io/rxs.uo") public func concat() -> Observable { return Concat(sources: self) } } -extension ObservableType where E : ObservableType { +extension ObservableType where E : ObservableConvertibleType { /** Concatenates all inner observable sequences, as long as the previous observable sequence terminated successfully. - 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) + return merge(maxConcurrent: 1) } } -// merge +// MARK: 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. - - 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) + return Merge(source: asObservable()) } /** 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") public func merge(maxConcurrent maxConcurrent: Int) -> Observable { - return Merge(sources: self.asObservable(), maxConcurrent: maxConcurrent) + return MergeLimited(source: asObservable(), maxConcurrent: maxConcurrent) } } -// catch +// MARK: catch extension ObservableType { @@ -119,9 +139,10 @@ 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) + return Catch(source: asObservable(), handler: handler) } /** @@ -130,26 +151,28 @@ 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) }) + return Catch(source: asObservable(), handler: { _ in just(element) }) } } -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. - 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) } } -// takeUntil +// MARK: takeUntil extension ObservableType { @@ -159,13 +182,31 @@ 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()) + return TakeUntil(source: asObservable(), other: other.asObservable()) } } -// amb +// MARK: 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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func skipUntil(other: O) + -> Observable { + return SkipUntil(source: asObservable(), other: other.asObservable()) + } +} + +// MARK: amb extension ObservableType { @@ -175,24 +216,52 @@ 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 { - return Amb(left: self.asObservable(), right: right.asObservable()) + return Amb(left: asObservable(), right: right.asObservable()) } } -extension SequenceType where Generator.Element : ObservableType { +extension SequenceType where Generator.Element : ObservableConvertibleType { /** Propagates the observable sequence that reacts first. - 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 - return a.amb(o) + return a.amb(o.asObservable()) } } } + +// withLatestFrom + +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. + + - 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) throws -> ResultType) -> Observable { + return WithLatestFrom(first: 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: asObservable(), second: second.asObservable(), resultSelector: { $1 }) + } +} diff --git a/RxSwift/Observables/Observable+Single.swift b/RxSwift/Observables/Observable+Single.swift index 7b1efb9c..54df9070 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 { @@ -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,13 +56,14 @@ 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) } } -// do +// MARK: do extension ObservableType { @@ -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) @@ -77,27 +82,28 @@ 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) + @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 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?() } } } } -// startWith +// MARK: startWith extension ObservableType { @@ -107,13 +113,14 @@ 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) } } -// retry +// MARK: retry extension ObservableType { @@ -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,13 +144,40 @@ 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())) } + + /** + 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) + } + + /** + 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) + } } -// scan +// MARK: scan extension ObservableType { @@ -155,6 +190,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 021f6c65..755a2007 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 { @@ -18,13 +18,14 @@ 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) + return Filter(source: asObservable(), predicate: predicate) } } -// takeWhile +// MARK: takeWhile extension ObservableType { @@ -34,9 +35,10 @@ 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) + @warn_unused_result(message="http://git.io/rxs.uo") + public func takeWhile(predicate: (E) throws -> Bool) -> Observable { - return TakeWhile(source: self.asObservable(), predicate: predicate) + return TakeWhile(source: asObservable(), predicate: predicate) } /** @@ -47,13 +49,14 @@ 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) + @warn_unused_result(message="http://git.io/rxs.uo") + public func takeWhileWithIndex(predicate: (E, Int) throws -> Bool) -> Observable { - return TakeWhile(source: self.asObservable(), predicate: predicate) + return TakeWhile(source: asObservable(), predicate: predicate) } } -// take +// MARK: take extension ObservableType { @@ -63,18 +66,39 @@ 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 { return empty() } else { - return TakeCount(source: self.asObservable(), count: count) + return TakeCount(source: asObservable(), count: count) } } } + +// MARK: takeLast + +extension ObservableType { -// skip + /** + Returns a specified number of contiguous elements from the end of an observable 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 { + return TakeLast(source: asObservable(), count: count) + } +} + + +// MARK: skip extension ObservableType { @@ -84,13 +108,42 @@ 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) + return SkipCount(source: asObservable(), count: count) } } -// map aka select +// MARK: 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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func skipWhile(predicate: (E) throws -> Bool) -> Observable { + return SkipWhile(source: 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. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func skipWhileWithIndex(predicate: (E, Int) throws -> Bool) -> Observable { + return SkipWhile(source: asObservable(), predicate: predicate) + } +} + +// MARK: map aka select extension ObservableType { @@ -100,9 +153,10 @@ 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) + return self.asObservable().composeMap(selector) } /** @@ -111,13 +165,14 @@ 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) + return MapWithIndex(source: asObservable(), selector: selector) } } -// flatMap +// MARK: flatMap extension ObservableType { @@ -127,9 +182,10 @@ 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) + @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) } /** @@ -138,8 +194,94 @@ 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) + @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) } +} + +// 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 FlatMapFirst(source: asObservable(), selector: selector) + } +} + +// 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 { + + /** + 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: 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: 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: asObservable(), predicate: predicate) + } + } \ No newline at end of file diff --git a/RxSwift/Observables/Observable+Time.swift b/RxSwift/Observables/Observable+Time.swift index b086b8c6..5413105b 100644 --- a/RxSwift/Observables/Observable+Time.swift +++ b/RxSwift/Observables/Observable+Time.swift @@ -8,7 +8,7 @@ import Foundation -// throttle +// MARK: throttle extension ObservableType { /** @@ -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,13 +35,14 @@ 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) + @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) } } -// sample +// MARK: sample extension ObservableType { @@ -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,13 +72,14 @@ 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) } } -// 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. @@ -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, @@ -92,7 +97,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. @@ -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( @@ -127,7 +134,7 @@ public func timer(dueTime: S.TimeInterval, _ scheduler: S) ) } -// take +// MARK: take extension ObservableType { @@ -138,13 +145,14 @@ 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) } } -// skip +// MARK: skip extension ObservableType { @@ -155,14 +163,32 @@ 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) } } +// MARK: ignoreElements -// delaySubscription +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 extension ObservableType { @@ -173,13 +199,14 @@ 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) } } -// buffer +// MARK: buffer extension ObservableType { @@ -193,8 +220,28 @@ 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) } -} \ No newline at end of file +} + +// 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/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/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 fac004fc..38ce0032 100644 --- a/RxSwift/Observers/ObserverBase.swift +++ b/RxSwift/Observers/ObserverBase.swift @@ -11,21 +11,17 @@ import Foundation class ObserverBase : Disposable, ObserverType { typealias E = ElementType - var lock = SpinLock() - 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 } @@ -34,10 +30,10 @@ class ObserverBase : Disposable, ObserverType { } func onCore(event: Event) { - return abstractMethod() + abstractMethod() } 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 218e58e4..6f4a85f6 100644 --- a/RxSwift/Observers/TailRecursiveSink.swift +++ b/RxSwift/Observers/TailRecursiveSink.swift @@ -8,88 +8,89 @@ 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] = [] - var disposed: Bool = false - var subscription = SerialDisposable() + var _generators:[S.Generator] = [] + var _disposed = false + var _subscription = SerialDisposable() // this is thread safe object - var gate: AsyncLock = AsyncLock() + var _gate = AsyncLock>>() - override init(observer: O, cancel: Disposable) { - super.init(observer: observer, cancel: cancel) + override init(observer: O) { + super.init(observer: observer) } func run(sources: S.Generator) -> Disposable { - self.generators.append(sources) + _generators.append(sources) + + schedule(.MoveNext) - scheduleMoveNext() - - let disposeSinkStack = AnonymousDisposable { - self.schedule { - self.disposePrivate() - } - } - - return StableCompositeDisposable.create(self.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) { - self.gate.wait(action) + func schedule(command: TailRecursiveSinkCommand) { + _gate.invoke(InvocableScheduledItem(invocable: self, state: command)) } func done() { - observer?.on(.Completed) - self.dispose() + forwardOn(.Completed) + dispose() } func extract(observable: Observable) -> S.Generator? { - return abstractMethod() - } - - func on(event: Event) { - return abstractMethod() + abstractMethod() } // should be done on gate locked - private func moveNext() { - var next: Observable? = nil; + private func moveNextCommand() { + var next: Observable? = 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 @@ -101,13 +102,24 @@ class TailRecursiveSink) -> Disposable { + abstractMethod() } - -} \ No newline at end of file + + func disposeCommand() { + _disposed = true + _generators.removeAll(keepCapacity: false) + } + + override func dispose() { + super.dispose() + + _subscription.dispose() + + schedule(.Dispose) + } +} diff --git a/RxSwift/Rx.swift b/RxSwift/Rx.swift index 1362e196..dccb10ae 100644 --- a/RxSwift/Rx.swift +++ b/RxSwift/Rx.swift @@ -19,17 +19,33 @@ 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) } +func incrementChecked(inout i: Int) throws -> Int { + if i == Int.max { + throw RxError.Overflow + } + let result = i + i += 1 + return result +} + +func decrementChecked(inout i: Int) throws -> Int { + if i == Int.min { + throw RxError.Overflow + } + let result = i + i -= 1 + return result +} + extension NSObject { func rx_synchronized(@noescape action: () -> T) -> T { objc_sync_enter(self) 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/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/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/ConcurrentDispatchQueueScheduler.swift b/RxSwift/Schedulers/ConcurrentDispatchQueueScheduler.swift index 00991c40..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. */ @@ -17,7 +17,7 @@ public class ConcurrentDispatchQueueScheduler: SchedulerType { public typealias TimeInterval = NSTimeInterval public typealias Time = NSDate - private let queue : dispatch_queue_t + private let _queue : dispatch_queue_t public var now : NSDate { get { @@ -26,7 +26,7 @@ public class ConcurrentDispatchQueueScheduler: SchedulerType { } // leeway for scheduling timers - var leeway: Int64 = 0 + private var _leeway: Int64 = 0 /** Constructs new `ConcurrentDispatchQueueScheduler` that wraps `queue`. @@ -34,7 +34,7 @@ public class ConcurrentDispatchQueueScheduler: SchedulerType { - parameter queue: Target dispatch queue. */ public init(queue: dispatch_queue_t) { - self.queue = queue + _queue = queue } /** @@ -77,7 +77,7 @@ public class ConcurrentDispatchQueueScheduler: SchedulerType { func scheduleInternal(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/ConcurrentMainScheduler.swift b/RxSwift/Schedulers/ConcurrentMainScheduler.swift new file mode 100644 index 00000000..046d460b --- /dev/null +++ b/RxSwift/Schedulers/ConcurrentMainScheduler.swift @@ -0,0 +1,92 @@ +// +// ConcurrentMainScheduler.swift +// Rx +// +// Created by Krunoslav Zaher on 10/17/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +/** +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. + +This scheduler is optimized for `subscribeOn` operator. If you want to observe observable sequence elements on main thread using `observeOn` operator, +`MainScheduler` is more suitable for that purpose. +*/ +public final class ConcurrentMainScheduler : SchedulerType { + public typealias TimeInterval = NSTimeInterval + public typealias Time = NSDate + + private let _mainScheduler: MainScheduler + private let _mainQueue: dispatch_queue_t + + /** + - returns: Current time. + */ + public var now : NSDate { + get { + return _mainScheduler.now + } + } + + private init(mainScheduler: MainScheduler) { + _mainQueue = dispatch_get_main_queue() + _mainScheduler = mainScheduler + } + + /** + Singleton instance of `ConcurrentMainScheduler` + */ + public static let sharedInstance = ConcurrentMainScheduler(mainScheduler: MainScheduler.sharedInstance) + + /** + Schedules an action to be executed immediatelly. + + - parameter state: State passed to the action to be executed. + - parameter action: Action to be executed. + - returns: The disposable object used to cancel the scheduled action (best effort). + */ + public func schedule(state: StateType, action: (StateType) -> Disposable) -> Disposable { + if NSThread.currentThread().isMainThread { + return action(state) + } + + let cancel = SingleAssignmentDisposable() + + dispatch_async(_mainQueue) { + if cancel.disposed { + return + } + + cancel.disposable = action(state) + } + + return cancel + } + + /** + Schedules an action to be executed. + + - parameter state: State passed to the action to be executed. + - parameter dueTime: Relative time after which to execute the action. + - parameter action: Action to be executed. + - returns: The disposable object used to cancel the scheduled action (best effort). + */ + public final func scheduleRelative(state: StateType, dueTime: NSTimeInterval, action: (StateType) -> Disposable) -> Disposable { + return _mainScheduler.scheduleRelative(state, dueTime: dueTime, action: action) + } + + /** + Schedules a periodic piece of work. + + - parameter state: State passed to the action to be executed. + - parameter startAfter: Period after which initial work should be run. + - parameter period: Period for running the work periodically. + - parameter action: Action to be executed. + - 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 { + return _mainScheduler.schedulePeriodic(state, startAfter: startAfter, period: period, action: action) + } +} \ No newline at end of file diff --git a/RxSwift/Schedulers/CurrentThreadScheduler.swift b/RxSwift/Schedulers/CurrentThreadScheduler.swift index 89779860..e2d5b2b4 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 + + let disposable = action(state) + + defer { + CurrentThreadScheduler.isScheduleRequired = true + CurrentThreadScheduler.queue = nil } - latest.invoke() + + guard let queue = CurrentThreadScheduler.queue else { + return disposable + } + + while let latest = queue.value.tryDequeue() { + if latest.disposed { + continue + } + latest.invoke() + } + + return disposable } - - 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) + queue.value.enqueue(scheduledItem) + return scheduledItem } } \ No newline at end of file diff --git a/RxSwift/Schedulers/ImmediateScheduler.swift b/RxSwift/Schedulers/ImmediateScheduler.swift new file mode 100644 index 00000000..fc54fd51 --- /dev/null +++ b/RxSwift/Schedulers/ImmediateScheduler.swift @@ -0,0 +1,39 @@ +// +// ImmediateScheduler.swift +// Rx +// +// Created by Krunoslav Zaher on 10/17/15. +// Copyright © 2015 Krunoslav Zaher. All rights reserved. +// + +import Foundation + +/** +Represents an object that schedules units of work to run immediately on the current thread. +*/ +private class ImmediateScheduler : ImmediateSchedulerType { + + private let _asyncLock = AsyncLock() + + /** + Schedules an action to be executed immediatelly. + + In case `schedule` is called recursively from inside of `action` callback, scheduled `action` will be enqueued + and executed after current `action`. (`AsyncLock` behavior) + + - parameter state: State passed to the action to be executed. + - parameter action: Action to be executed. + - returns: The disposable object used to cancel the scheduled action (best effort). + */ + func schedule(state: StateType, action: (StateType) -> Disposable) -> Disposable { + let disposable = SingleAssignmentDisposable() + _asyncLock.invoke(AnonymousInvocable { + if disposable.disposed { + return + } + disposable.disposable = action(state) + }) + + return disposable + } +} \ No newline at end of file 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/MainScheduler.swift b/RxSwift/Schedulers/MainScheduler.swift index d800e91f..ce88153c 100644 --- a/RxSwift/Schedulers/MainScheduler.swift +++ b/RxSwift/Schedulers/MainScheduler.swift @@ -14,17 +14,25 @@ Abstracts work that needs to be performed on `MainThread`. In case `schedule` me This scheduler is usually used to perform UI work. Main scheduler is a specialization of `SerialDispatchQueueScheduler`. + +This scheduler is optimized for `observeOn` operator. To ensure observable sequence is subscribed on main thread using `subscribeOn` +operator please use `ConcurrentMainScheduler` because it is more optimized for that purpose. */ public final class MainScheduler : SerialDispatchQueueScheduler { - + + private let _mainQueue: dispatch_queue_t + + var numberEnqueued: Int32 = 0 + private init() { - super.init(serialQueue: dispatch_get_main_queue()) + _mainQueue = dispatch_get_main_queue() + super.init(serialQueue: _mainQueue) } /** Singleton instance of `MainScheduler` */ - public static let sharedInstance: MainScheduler = MainScheduler() + public static let sharedInstance = MainScheduler() /** In case this method is called on a background thread it will throw an exception. @@ -36,10 +44,25 @@ public final class MainScheduler : SerialDispatchQueueScheduler { } override func scheduleInternal(state: StateType, action: StateType -> Disposable) -> Disposable { - if NSThread.currentThread().isMainThread { - return action(state) + let currentNumberEnqueued = OSAtomicIncrement32(&numberEnqueued) + + if NSThread.currentThread().isMainThread && currentNumberEnqueued == 1 { + let disposable = action(state) + OSAtomicDecrement32(&numberEnqueued) + return disposable } - - return super.scheduleInternal(state, action: action) + + let cancel = SingleAssignmentDisposable() + + dispatch_async(_mainQueue) { + if !cancel.disposed { + action(state) + } + + OSAtomicDecrement32(&self.numberEnqueued) + } + + return cancel } } + 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`. */ diff --git a/RxSwift/Schedulers/RecursiveScheduler.swift b/RxSwift/Schedulers/RecursiveScheduler.swift index 38e9b898..5e9d0a76 100644 --- a/RxSwift/Schedulers/RecursiveScheduler.swift +++ b/RxSwift/Schedulers/RecursiveScheduler.swift @@ -8,48 +8,48 @@ import Foundation -class RecursiveScheduler: RecursiveSchedulerOf { - let scheduler: S +class RecursiveScheduler: AnyRecursiveScheduler { + 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) } } /** Type erased recursive scheduler. */ -public class RecursiveSchedulerOf { - typealias Action = (state: State, scheduler: RecursiveSchedulerOf) -> Void +class AnyRecursiveScheduler { + typealias Action = (state: State, scheduler: AnyRecursiveScheduler) -> Void - let lock = NSRecursiveLock() + private let _lock = NSRecursiveLock() // state - let group = CompositeDisposable() + private let _group = CompositeDisposable() - var action: Action? + private var _action: Action? init(action: Action) { - self.action = action + _action = action } // 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() } /** @@ -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 @@ -66,19 +66,19 @@ public class RecursiveSchedulerOf { 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 + 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 { @@ -88,9 +88,9 @@ public class RecursiveSchedulerOf { return NopDisposable.instance } - lock.performLocked { + _lock.performLocked { if !isDone { - removeKey = group.addDisposable(d) + removeKey = _group.addDisposable(d) isAdded = true } } @@ -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 @@ -109,19 +109,19 @@ public class RecursiveSchedulerOf { 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 + 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 { @@ -131,37 +131,37 @@ public class RecursiveSchedulerOf { return NopDisposable.instance } - lock.performLocked { + _lock.performLocked { if !isDone { - removeKey = group.addDisposable(d) + removeKey = _group.addDisposable(d) isAdded = true } } } func dispose() { - self.lock.performLocked { - self.action = nil + _lock.performLocked { + _action = nil } - self.group.dispose() + _group.dispose() } } /** Type erased recursive scheduler. */ -public class RecursiveImmediateSchedulerOf { +class RecursiveImmediateScheduler { typealias Action = (state: State, recurse: State -> Void) -> Void - var lock = SpinLock() - let group = CompositeDisposable() + private var _lock = SpinLock() + 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 @@ -171,27 +171,27 @@ 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 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 + 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 { @@ -201,18 +201,18 @@ public class RecursiveImmediateSchedulerOf { return NopDisposable.instance } - lock.performLocked { + _lock.performLocked { if !isDone { - removeKey = group.addDisposable(d) + removeKey = _group.addDisposable(d) isAdded = true } } } func dispose() { - self.lock.performLocked { - self.action = nil + _lock.performLocked { + _action = nil } - self.group.dispose() + _group.dispose() } } \ 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/RxSwift/Schedulers/SchedulerServices+Emulation.swift b/RxSwift/Schedulers/SchedulerServices+Emulation.swift index 9fab3e25..d503522c 100644 --- a/RxSwift/Schedulers/SchedulerServices+Emulation.swift +++ b/RxSwift/Schedulers/SchedulerServices+Emulation.swift @@ -11,52 +11,54 @@ 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 - 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: self.tick) } 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) + scheduler.schedule(.Tick, dueTime: _period) - if OSAtomicIncrement32(&pendingTickCount) == 1 { + // 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: - if OSAtomicDecrement32(&pendingTickCount) > 0 { + _state = _action(_state) + // 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/RxSwift/Schedulers/SerialDispatchQueueScheduler.swift b/RxSwift/Schedulers/SerialDispatchQueueScheduler.swift index 88dc79fd..cfd9019a 100644 --- a/RxSwift/Schedulers/SerialDispatchQueueScheduler.swift +++ b/RxSwift/Schedulers/SerialDispatchQueueScheduler.swift @@ -29,8 +29,11 @@ public class SerialDispatchQueueScheduler: SchedulerType { public typealias TimeInterval = NSTimeInterval public typealias Time = NSDate - private let serialQueue : dispatch_queue_t - + private let _serialQueue : dispatch_queue_t + + /** + - returns: Current time. + */ public var now : NSDate { get { return NSDate() @@ -38,10 +41,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 +132,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 +153,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 +185,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) @@ -205,4 +208,4 @@ public class SerialDispatchQueueScheduler: SchedulerType { return cancel } -} \ No newline at end of file +} diff --git a/RxSwift/Subjects/BehaviorSubject.swift b/RxSwift/Subjects/BehaviorSubject.swift index 6d6a6970..7c6284fc 100644 --- a/RxSwift/Subjects/BehaviorSubject.swift +++ b/RxSwift/Subjects/BehaviorSubject.swift @@ -8,51 +8,33 @@ import Foundation -private class BehaviorSubjectSubscription : Disposable { - typealias Parent = BehaviorSubject - typealias DisposeKey = Bag>.KeyType - - let parent: Parent - var disposeKey: DisposeKey? - - init(parent: BehaviorSubject, disposeKey: DisposeKey) { - self.parent = parent - self.disposeKey = disposeKey - } - - func dispose() { - self.parent.lock.performLocked { - if let disposeKey = disposeKey { - self.parent.observers.removeKey(disposeKey) - self.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 + , SynchronizedUnsubscribeType + , Disposable { public typealias SubjectObserverType = BehaviorSubject + typealias DisposeKey = Bag>.KeyType - let lock = NSRecursiveLock() + 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 _disposed - } + return _disposed } /** @@ -61,7 +43,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 } /** @@ -70,19 +52,19 @@ 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 + throw RxError.Disposed(object: self) } - if let error = stoppedEvent?.error { + if let error = _stoppedEvent?.error { // intentionally throw exception throw error } else { return _value } - } + //} } /** @@ -91,22 +73,23 @@ 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 { - return - } - - switch event { - case .Next(let value): - self._value = value - case .Error: - self.stoppedEvent = event - case .Completed: - self.stoppedEvent = event - } - - self.observers.forEach { $0.on(event) } + _lock.lock(); defer { _lock.unlock() } + _synchronized_on(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) } /** @@ -116,22 +99,38 @@ 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) + _lock.lock(); defer { _lock.unlock() } + return _synchronized_subscribe(observer) + } + + func _synchronized_subscribe(observer: O) -> Disposable { + if _disposed { + observer.on(.Error(RxError.Disposed(object: self))) + 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 synchronizedUnsubscribe(disposeKey: DisposeKey) { + _lock.lock(); defer { _lock.unlock() } + _synchronized_unsubscribe(disposeKey) + } + + func _synchronized_unsubscribe(disposeKey: DisposeKey) { + if _disposed { + return + } + + _ = _observers.removeKey(disposeKey) } /** @@ -145,10 +144,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 diff --git a/RxSwift/Subjects/PublishSubject.swift b/RxSwift/Subjects/PublishSubject.swift index 143d1869..2ed8f704 100644 --- a/RxSwift/Subjects/PublishSubject.swift +++ b/RxSwift/Subjects/PublishSubject.swift @@ -8,63 +8,35 @@ 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) { - self.key = key - self.subject = subject - } - - func dispose() { - lock.performLocked { - guard let subject = subject else { - return - } - - guard let key = key else { - return - } - - self.subject = nil - self.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 { +final public class PublishSubject + : Observable + , SubjectType + , Cancelable + , ObserverType + , SynchronizedUnsubscribeType { public typealias SubjectObserverType = PublishSubject - typealias DisposeKey = Bag>.KeyType + typealias DisposeKey = Bag>.KeyType - private let lock = NSRecursiveLock() + private var _lock = NSRecursiveLock() // state - var _disposed = false - var observers = Bag>() - var stoppedEvent = nil as Event? + private var _disposed = false + private var _observers = Bag>() + private var _stopped = false + private var _stoppedEvent = nil as Event? /** Indicates whether the subject has been disposed. */ public var disposed: Bool { get { - return self.lock.calculateLocked { - return _disposed - } + return _disposed } } @@ -81,20 +53,24 @@ 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 { - self.stoppedEvent = event - observers.forEach { $0.on(event) } - self.observers.removeAll() - } + _lock.lock(); defer { _lock.unlock() } + _synchronized_on(event) + } + + func _synchronized_on(event: Event) { + switch event { + case .Next(_): + if _disposed || _stopped { + return + } + + _observers.on(event) + case .Completed, .Error: + if _stoppedEvent == nil { + _stoppedEvent = event + _stopped = true + _observers.on(event) + _observers.removeAll() } } } @@ -106,26 +82,32 @@ 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) - } + _lock.lock(); defer { _lock.unlock() } + return _synchronized_subscribe(observer) } - func unsubscribe(key: DisposeKey) { - self.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.Disposed(object: self))) + return NopDisposable.instance + } + + let key = _observers.insert(observer.asObserver()) + 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) } /** @@ -139,10 +121,13 @@ public class PublishSubject : Observable, SubjectType, Cancela Unsubscribe all observers and release resources. */ public func dispose() { - self.lock.performLocked { - _disposed = true - self.observers.removeAll() - self.stoppedEvent = nil - } + _lock.lock(); defer { _lock.unlock() } + _synchronized_dispose() + } + + final 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 7fcf3f32..818521e8 100644 --- a/RxSwift/Subjects/ReplaySubject.swift +++ b/RxSwift/Subjects/ReplaySubject.swift @@ -13,13 +13,17 @@ 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 + typealias DisposeKey = Bag>.KeyType func unsubscribe(key: DisposeKey) { - return abstractMethod() + abstractMethod() } /** @@ -28,7 +32,7 @@ public class ReplaySubject : Observable, SubjectType, Observer - parameter event: Event to send to the observers. */ public func on(event: Event) { - return abstractMethod() + abstractMethod() } /** @@ -60,103 +64,113 @@ public class ReplaySubject : Observable, SubjectType, Observer } } -class ReplayBufferBase : ReplaySubject { - let lock = NSRecursiveLock() +class ReplayBufferBase + : ReplaySubject + , SynchronizedUnsubscribeType { + private var _lock = NSRecursiveLock() + // state - var disposed = false - var stoppedEvent = nil as Event? - var observers = Bag>() - - override init() { - - } + private var _disposed = false + private var _stoppedEvent = nil as Event? + private var _observers = Bag>() func trim() { - return abstractMethod() + abstractMethod() } func addValueToBuffer(value: Element) { - return abstractMethod() + abstractMethod() } - func replayBuffer(observer: ObserverOf) { - return abstractMethod() + func replayBuffer(observer: AnyObserver) { + abstractMethod() } override func on(event: Event) { - lock.performLocked { - if self.disposed { - return - } - - if self.stoppedEvent != nil { - return - } - - switch event { - case .Next(let value): - addValueToBuffer(value) - trim() - self.observers.forEach { $0.on(event) } - case .Error, .Completed: - stoppedEvent = event - trim() - self.observers.forEach { $0.on(event) } - observers.removeAll() - } - + _lock.lock(); defer { _lock.unlock() } + _synchronized_on(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 self.disposed { - observer.on(.Error(RxError.DisposedError)) - return NopDisposable.instance - } - - let observerOf = observer.asObserver() - - replayBuffer(observerOf) - if let stoppedEvent = self.stoppedEvent { - observer.on(stoppedEvent) - return NopDisposable.instance - } - else { - let key = self.observers.insert(observerOf) - return ReplaySubscription(subject: self, disposeKey: key) - } - } + _lock.lock(); defer { _lock.unlock() } + return _synchronized_subscribe(observer) } - - override func unsubscribe(key: DisposeKey) { - lock.performLocked { - if self.disposed { - return - } - - _ = self.observers.removeKey(key) + + func _synchronized_subscribe(observer: O) -> Disposable { + if _disposed { + observer.on(.Error(RxError.Disposed(object: self))) + 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 synchronizedUnsubscribe(disposeKey: DisposeKey) { + _lock.lock(); defer { _lock.unlock() } + _synchronized_unsubscribe(disposeKey) + } + + func _synchronized_unsubscribe(disposeKey: DisposeKey) { + if _disposed { + return + } + + _ = _observers.removeKey(disposeKey) } override func dispose() { super.dispose() - - lock.performLocked { - self.lockedDispose() - } + + synchronizedDispose() + } + + func synchronizedDispose() { + _lock.lock(); defer { _lock.unlock() } + _synchronized_dispose() + } + + func _synchronized_dispose() { + _disposed = true + _stoppedEvent = nil + _observers.removeAll() } } -class ReplayOne : ReplayBufferBase { - var value: Element? +final class ReplayOne : ReplayBufferBase { + private var _value: Element? override init() { super.init() @@ -167,62 +181,61 @@ class ReplayOne : ReplayBufferBase { } override func addValueToBuffer(value: Element) { - self.value = value + _value = value } - override func replayBuffer(observer: ObserverOf) { - if let value = self.value { + override func replayBuffer(observer: AnyObserver) { + if let value = _value { observer.on(.Next(value)) } } - - override func lockedDispose() { - super.lockedDispose() - - value = nil + + override func _synchronized_dispose() { + super._synchronized_dispose() + _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: ObserverOf) { - for item in queue { + override func replayBuffer(observer: AnyObserver) { + for item in _queue { observer.on(.Next(item)) } } - - override func lockedDispose() { - super.lockedDispose() - self.queue = Queue(capacity: 0) + + override func _synchronized_dispose() { + super._synchronized_dispose() + _queue = Queue(capacity: 0) } } -class ReplayMany : ReplayManyBase { - let bufferSize: Int +final class ReplayMany : ReplayManyBase { + 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() } } } -class ReplayAll : ReplayManyBase { +final class ReplayAll : ReplayManyBase { init() { super.init(queueSize: 0) } @@ -230,34 +243,4 @@ class ReplayAll : ReplayManyBase { override func trim() { } -} - -class ReplaySubscription : Disposable { - typealias Subject = ReplaySubject - typealias DisposeKey = ReplayBufferBase.DisposeKey - - var lock = SpinLock() - - // state - var subject: Subject? - var disposeKey: DisposeKey? - - init(subject: Subject, disposeKey: DisposeKey) { - self.subject = subject - self.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 diff --git a/RxSwift/Subjects/Variable.swift b/RxSwift/Subjects/Variable.swift index c8f810ed..e9c04472 100644 --- a/RxSwift/Subjects/Variable.swift +++ b/RxSwift/Subjects/Variable.swift @@ -16,9 +16,9 @@ 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() + private var _lock = SpinLock() // state private var _value: E @@ -32,15 +32,15 @@ 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 - } - self.subject.on(.Next(newValue)) + _lock.lock() + _value = newValue + _lock.unlock() + + _subject.on(.Next(newValue)) } } @@ -50,8 +50,8 @@ public class Variable : ObservableType { - parameter value: Initial variable value. */ public init(_ value: Element) { - self._value = value - self.subject = BehaviorSubject(value: value) + _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 diff --git a/RxTests/PerformanceTests/main.swift b/RxTests/PerformanceTests/main.swift index 53ce2b89..d4240578 100644 --- a/RxTests/PerformanceTests/main.swift +++ b/RxTests/PerformanceTests/main.swift @@ -9,70 +9,56 @@ import Foundation import RxSwift import RxCocoa +import AppKit +import CoreLocation -let NumberOfIterations = 1000 +let bechmarkTime = true -func approxValuePerIteration(total: Int) -> UInt64 { - return UInt64(round(Double(total) / Double(NumberOfIterations))) +func allocation() { + } -func approxValuePerIteration(total: UInt64) -> UInt64 { - return UInt64(round(Double(total) / Double(NumberOfIterations))) -} +repeat { +compareTwoImplementations(benchmarkTime: true, benchmarkMemory: false, first: { + let publishSubject = PublishSubject() -func measureTime(@noescape work: () -> ()) -> UInt64 { - var timebaseInfo: mach_timebase_info = mach_timebase_info() - let res = mach_timebase_info(&timebaseInfo) + //let a = just(1) - assert(res == 0) + //combineLatest(a, + 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 } + .map { $0 } + .map { $0 }*/ + /*.filter { _ in true }//){ x, _ in x } + .map { $0 } + .flatMap { just($0) }*/ + .subscribeNext { _ in - let start = mach_absolute_time() - for _ in 0 ..< NumberOfIterations { - work() + } + + + for i in 0..<100 { + publishSubject.on(.Next(i)) } - 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)) -} - -func compareTwoImplementations(@noescape first first: () -> (), @noescape second: () -> ()) { - // first warm up to keep it fair - first() - second() - - let time1 = measureTime(first) - let time2 = measureTime(second) - - 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 - ])) -} - -compareTwoImplementations(first: { }, second: { }) +} while true \ No newline at end of file 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..59061274 --- /dev/null +++ b/RxTests/RxCocoaTests/Control+RxTests+UIKit.swift @@ -0,0 +1,399 @@ +// +// 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 + + +// 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 } + } + + 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() + } +} + +// 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 { + 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() + } +} + +// 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) } + } +} + +// 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 } + } +} + +// UISegmentedControl +extension ControlTests { + func testSegmentedControl_DelegateEventCompletesOnDealloc() { + let createView: () -> UISegmentedControl = { UISegmentedControl(items: ["a", "b", "c"]) } + ensurePropertyDeallocated(createView, 1) { (view: UISegmentedControl) 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 } + } +} + + +#if os(iOS) + +// 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 } + } +} + +// 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 } + } +} + + +// 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 } + } +} + +// 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 } + } +} + + +// 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 diff --git a/RxTests/RxCocoaTests/Control+RxTests.swift b/RxTests/RxCocoaTests/Control+RxTests.swift new file mode 100644 index 00000000..33988d84 --- /dev/null +++ b/RxTests/RxCocoaTests/Control+RxTests.swift @@ -0,0 +1,101 @@ +// +// 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) { + return ensureEventDeallocated({ () -> (C, Disposable) in (createControl(), NopDisposable.instance) }, eventSelector) + } + + func ensureEventDeallocated(createControl: () -> (C, Disposable), _ eventSelector: C -> ControlEvent) { + var completed = false + var deallocated = false + let outerDisposable = SingleAssignmentDisposable() + + autoreleasepool { + let (control, disposable) = createControl() + let eventObservable = eventSelector(control) + + _ = eventObservable.subscribe(onNext: { n in + + }, onCompleted: { + completed = true + }) + + _ = control.rx_deallocated.subscribeNext { _ in + deallocated = true + } + + outerDisposable.disposable = disposable + } + + outerDisposable.dispose() + 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) + } +} diff --git a/RxTests/RxCocoaTests/KVOObservableTests.swift b/RxTests/RxCocoaTests/KVOObservableTests.swift index 4ee76757..353ce0a6 100644 --- a/RxTests/RxCocoaTests/KVOObservableTests.swift +++ b/RxTests/RxCocoaTests/KVOObservableTests.swift @@ -28,11 +28,11 @@ class Parent : NSObject { var disposeBag: DisposeBag! = DisposeBag() dynamic var val: String = "" - + 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) } @@ -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() } } @@ -101,7 +140,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 +174,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 +208,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 +305,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 +335,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 +369,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 +418,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 +446,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 +495,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 +534,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 +577,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 +621,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 +652,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 } @@ -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) @@ -683,8 +722,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 +758,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 +794,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 +830,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 +867,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 +899,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 +931,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 +964,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) @@ -948,19 +987,20 @@ extension KVOObservableTests { XCTAssertTrue(latest.value == nil) XCTAssertTrue(rootDeallocated) } - + func testObserveWeak_PropertyDoesntExist() { var root: HasStrongProperty! = HasStrongProperty() var lastError: ErrorType? = nil - _ = (root.rx_observeWeakly("notExist") as Observable) + _ = root.rx_observeWeakly(NSNumber.self, "notExist") .subscribeError { error in lastError = error } XCTAssertTrue(lastError != nil) - + lastError = nil + var rootDeallocated = false _ = root @@ -979,7 +1019,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 } @@ -1004,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 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/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/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/BackgroundThreadPrimitiveHotObservable.swift b/RxTests/RxSwiftTests/TestImplementations/Mocks/BackgroundThreadPrimitiveHotObservable.swift new file mode 100644 index 00000000..9a733e44 --- /dev/null +++ b/RxTests/RxSwiftTests/TestImplementations/Mocks/BackgroundThreadPrimitiveHotObservable.swift @@ -0,0 +1,18 @@ +// +// BackgroundThreadPrimitiveHotObservable.swift +// RxTests +// +// Created by Krunoslav Zaher on 10/19/15. +// +// + +import Foundation +import RxSwift +import XCTest + +class BackgroundThreadPrimitiveHotObservable : 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/Mocks/ColdObservable.swift b/RxTests/RxSwiftTests/TestImplementations/Mocks/ColdObservable.swift index 7d0e360a..3b25e1a6 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Mocks/ColdObservable.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Mocks/ColdObservable.swift @@ -8,46 +8,39 @@ import RxSwift -class ColdObservable: Observable { +class ColdObservable : ObservableType, ObservableConvertibleType { + typealias E = Element + typealias Events = Recorded - typealias Observer = ObserverOf + typealias Observer = AnyObserver let testScheduler: TestScheduler 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() - - super.init() } - override func subscribe(observer: O) -> Disposable { - let key = observers.insert(ObserverOf(observer)) + func subscribe(observer: O) -> Disposable { 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/TestImplementations/Mocks/HotObservable.swift b/RxTests/RxSwiftTests/TestImplementations/Mocks/HotObservable.swift index b79d11f2..bbaea4d2 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Mocks/HotObservable.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Mocks/HotObservable.swift @@ -9,16 +9,17 @@ import Foundation import RxSwift -class HotObservable : Observable { +class HotObservable : ObservableType, ObservableConvertibleType { + 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 @@ -27,18 +28,16 @@ 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) } + self.observers.on(recordedEvent.event) return NopDisposable.instance } } } - override func subscribe(observer: O) -> Disposable { - let key = observers.insert(ObserverOf(observer)) + func subscribe(observer: O) -> Disposable { + 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/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/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/TestImplementations/Mocks/MySubject.swift b/RxTests/RxSwiftTests/TestImplementations/Mocks/MySubject.swift index 5c66e331..8b403855 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Mocks/MySubject.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Mocks/MySubject.swift @@ -9,18 +9,15 @@ import Foundation import RxSwift -class MySubject : Observable, SubjectType, ObserverType { +class MySubject : SubjectType, ObserverType { + typealias E = Element typealias SubjectObserverType = MySubject var _disposeOn: [Element : Disposable] = [:] - var _observer: ObserverOf! = nil + var _observer: AnyObserver! = nil var _subscribeCount: Int = 0 var _disposed: Bool = false - override init() { - super.init() - } - var subscribeCount: Int { get { return _subscribeCount @@ -48,12 +45,12 @@ class MySubject : Observable, Subject } } - override func subscribe(observer: O) -> Disposable { + 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 c110dc6f..a5d47e02 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Mocks/PrimitiveHotObservable.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Mocks/PrimitiveHotObservable.swift @@ -12,31 +12,39 @@ 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 + typealias Observer = AnyObserver var subscriptions: [Subscription] - var observers: Bag> + var observers: Bag> + + let lock = NSRecursiveLock() - override init() { + init() { self.subscriptions = [] self.observers = Bag() - - super.init() } func on(event: Event) { - observers.forEach { $0.on(event) } + observers.on(event) } - override func subscribe(observer: O) -> Disposable { - let key = observers.insert(ObserverOf(observer)) + func subscribe(observer: O) -> Disposable { + lock.lock() + defer { lock.unlock() } + + let key = observers.insert(AnyObserver(observer)) subscriptions.append(SubscribedToHotObservable) let i = self.subscriptions.count - 1 return AnonymousDisposable { + self.lock.lock() + defer { self.lock.unlock() } + let removed = self.observers.removeKey(key) assert(removed != nil) 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..b7cdb388 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Observable+Extensions.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Observable+Extensions.swift @@ -8,7 +8,32 @@ import Foundation import RxSwift +import RxCocoa 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 +} + +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..a3d9a74b 100644 --- a/RxTests/RxSwiftTests/TestImplementations/Recorded.swift +++ b/RxTests/RxSwiftTests/TestImplementations/Recorded.swift @@ -8,8 +8,9 @@ import Foundation import RxSwift +import Swift -struct Recorded : CustomStringConvertible, Equatable { +struct Recorded : CustomDebugStringConvertible, Equatable { let time: Time let event: Event @@ -30,8 +31,10 @@ struct Recorded : CustomStringConvertible, Equatable { } } } - - var description: String { +} + +extension Recorded { + var debugDescription: String { get { return "\(event) @ \(time)" } @@ -43,14 +46,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/Schedulers/VirtualTimeSchedulerBase.swift b/RxTests/RxSwiftTests/TestImplementations/Schedulers/VirtualTimeSchedulerBase.swift index 9ffd556a..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) { @@ -104,32 +101,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 @@ -175,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/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/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/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/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.. Observable in - return xs + return xs.asObservable() } XCTAssertEqual(res.messages, [ @@ -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.retain() + let d2 = r.retain() + + 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.retain(); + d3.dispose() + } + + func testRefCountDisposable_PrimaryDisposesFirst() { + let d = BooleanDisposable() + let r = RefCountDisposable(disposable: d) + + XCTAssertEqual(r.disposed, false) + + let d1 = r.retain() + let d2 = r.retain() + + 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 diff --git a/RxTests/RxSwiftTests/Tests/Driver+Test.swift b/RxTests/RxSwiftTests/Tests/Driver+Test.swift new file mode 100644 index 00000000..bbd41db7 --- /dev/null +++ b/RxTests/RxSwiftTests/Tests/Driver+Test.swift @@ -0,0 +1,711 @@ +// +// 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(ConcurrentMainScheduler.sharedInstance) +// * events are observed on main thread - observeOn(MainScheduler.sharedInstance) +// * it can't error out - it needs to have catch somewhere +extension DriverTest { + + func subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(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 + } +} + +// MARK: conversions +extension DriverTest { + func testAsDriver_onErrorJustReturn() { + let hotObservable = BackgroundThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorJustReturn: -1) + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(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 = BackgroundThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorDriveWith: Drive.just(-1)) + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(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 = BackgroundThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver { e in + return Drive.empty() + } + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(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]) + } +} + +// MARK: map +extension DriverTest { + func testAsDriver_map() { + let hotObservable = BackgroundThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorJustReturn: -1).map { (n: Int) -> Int in + XCTAssertTrue(NSThread.isMainThread()) + return n + 1 + } + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(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]) + } + +} + +// MARK: filter +extension DriverTest { + func testAsDriver_filter() { + let hotObservable = BackgroundThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorJustReturn: -1).filter { n in + XCTAssertTrue(NSThread.isMainThread()) + return n % 2 == 0 + } + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(1)) + hotObservable.on(.Next(2)) + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [2]) + } +} + + +// MARK: switch latest +extension DriverTest { + func testAsDriver_switchLatest() { + let hotObservable = BackgroundThreadPrimitiveHotObservable>() + let hotObservable1 = MainThreadPrimitiveHotObservable() + let hotObservable2 = MainThreadPrimitiveHotObservable() + + let driver = hotObservable.asDriver(onErrorJustReturn: hotObservable1.asDriver(onErrorJustReturn: -1)).switchLatest() + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(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 + ]) + } +} + +// 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() + + var events = [Event]() + + let driver = hotObservable.asDriver(onErrorJustReturn: -1).doOn { e in + XCTAssertTrue(NSThread.isMainThread()) + + events.append(e) + } + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(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, ==) + } +} + +// MARK: distinct until change +extension DriverTest { + func testAsDriver_distinctUntilChanged1() { + let hotObservable = BackgroundThreadPrimitiveHotObservable() + + let driver = hotObservable.asDriver(onErrorJustReturn: -1).distinctUntilChanged() + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(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 = BackgroundThreadPrimitiveHotObservable() + + let driver = hotObservable.asDriver(onErrorJustReturn: -1).distinctUntilChanged({ $0 }) + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(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 = BackgroundThreadPrimitiveHotObservable() + + let driver = hotObservable.asDriver(onErrorJustReturn: -1).distinctUntilChanged({ $0 == $1 }) + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(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 = BackgroundThreadPrimitiveHotObservable() + + let driver = hotObservable.asDriver(onErrorJustReturn: -1).distinctUntilChanged({ $0 }) { $0 == $1 } + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(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]) + } + +} + +// MARK: flat map +extension DriverTest { + func testAsDriver_flatMap() { + let hotObservable = BackgroundThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorJustReturn: -1).flatMap { (n: Int) -> Driver in + XCTAssertTrue(NSThread.isMainThread()) + return Drive.just(n + 1) + } + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(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]) + } + +} + +// MARK: merge +extension DriverTest { + func testAsDriver_merge() { + let hotObservable = BackgroundThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorJustReturn: -1).map { (n: Int) -> Driver in + XCTAssertTrue(NSThread.isMainThread()) + return Drive.just(n + 1) + }.merge() + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(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 = BackgroundThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorJustReturn: -1).map { (n: Int) -> Driver in + XCTAssertTrue(NSThread.isMainThread()) + return Drive.just(n + 1) + }.merge(maxConcurrent: 1) + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(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]) + } +} + +// MARK: debounce +extension DriverTest { + func testAsDriver_debounce() { + let hotObservable = BackgroundThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorJustReturn: -1).debounce(0.0, MainScheduler.sharedInstance) + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [-1]) + } + + func testAsDriver_throttle() { + let hotObservable = BackgroundThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorJustReturn: -1).throttle(0.0, MainScheduler.sharedInstance) + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(1)) + hotObservable.on(.Next(2)) + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [-1]) + } + +} + +// MARK: scan +extension DriverTest { + func testAsDriver_scan() { + let hotObservable = BackgroundThreadPrimitiveHotObservable() + let driver = hotObservable.asDriver(onErrorJustReturn: -1).scan(0) { (a: Int, n: Int) -> Int in + XCTAssertTrue(NSThread.isMainThread()) + return a + n + } + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(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]) + } + +} + +// MARK: concat +extension DriverTest { + func testAsDriver_concat() { + let hotObservable1 = BackgroundThreadPrimitiveHotObservable() + let hotObservable2 = MainThreadPrimitiveHotObservable() + + let driver = [hotObservable1.asDriver(onErrorJustReturn: -1), hotObservable2.asDriver(onErrorJustReturn: -2)].concat() + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(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]) + } +} + +// MARK: combine latest +extension DriverTest { + func testAsDriver_combineLatest_array() { + let hotObservable1 = BackgroundThreadPrimitiveHotObservable() + let hotObservable2 = BackgroundThreadPrimitiveHotObservable() + + let driver = [hotObservable1.asDriver(onErrorJustReturn: -1), hotObservable2.asDriver(onErrorJustReturn: -2)].combineLatest { a in a.reduce(0, combine: +) } + + 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, [5, 6, 7, 4, -3]) + } + + func testAsDriver_combineLatest() { + let hotObservable1 = BackgroundThreadPrimitiveHotObservable() + let hotObservable2 = BackgroundThreadPrimitiveHotObservable() + + let driver = combineLatest(hotObservable1.asDriver(onErrorJustReturn: -1), hotObservable2.asDriver(onErrorJustReturn: -2), resultSelector: +) + + 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, [5, 6, 7, 4, -3]) + } +} + +// MARK: zip +extension DriverTest { + func testAsDriver_zip_array() { + let hotObservable1 = BackgroundThreadPrimitiveHotObservable() + let hotObservable2 = BackgroundThreadPrimitiveHotObservable() + + let driver = [hotObservable1.asDriver(onErrorJustReturn: -1), hotObservable2.asDriver(onErrorJustReturn: -2)].zip { a in a.reduce(0, combine: +) } + + 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, [5, 7, -3]) + } + + func testAsDriver_zip() { + let hotObservable1 = BackgroundThreadPrimitiveHotObservable() + let hotObservable2 = BackgroundThreadPrimitiveHotObservable() + + let driver = zip(hotObservable1.asDriver(onErrorJustReturn: -1), hotObservable2.asDriver(onErrorJustReturn: -2), resultSelector: +) + + 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, [5, 7, -3]) + } +} + +// MARK: 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+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) } 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+BlockingTest.swift b/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift index 11358e16..378ee1d7 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+BlockingTest.swift @@ -12,35 +12,31 @@ import RxBlocking import XCTest class ObservableBlockingTest : RxTest { - override func tearDown() { - sleep(0.1) - super.tearDown() - } } // toArray extension ObservableBlockingTest { func testToArray_empty() { - XCTAssert(try! (empty() as Observable).toArray() == []) + XCTAssert(try! (empty() as Observable).toBlocking().toArray() == []) } func testToArray_return() { - XCTAssert(try! just(42).toArray() == [42]) + XCTAssert(try! just(42).toBlocking().toArray() == [42]) } func testToArray_fail() { do { - try (failWith(testError) as Observable).toArray() + try (failWith(testError) as Observable).toBlocking().toArray() XCTFail("It should fail") } - catch { - + catch let e { + XCTAssertTrue(e as NSError === testError) } } func testToArray_someData() { - XCTAssert(try! sequenceOf(42, 43, 44, 45).toArray() == [42, 43, 44, 45]) + XCTAssert(try! sequenceOf(42, 43, 44, 45).toBlocking().toArray() == [42, 43, 44, 45]) } func testToArray_withRealScheduler() { @@ -48,6 +44,7 @@ extension ObservableBlockingTest { let array = try! interval(0.001, scheduler) .take(10) + .toBlocking() .toArray() XCTAssert(array == Array(0..<10)) @@ -58,25 +55,25 @@ extension ObservableBlockingTest { extension ObservableBlockingTest { func testFirst_empty() { - XCTAssert(try! (empty() as Observable).first() == nil) + XCTAssert(try! (empty() as Observable).toBlocking().first() == nil) } func testFirst_return() { - XCTAssert(try! just(42).first() == 42) + XCTAssert(try! just(42).toBlocking().first() == 42) } func testFirst_fail() { do { - try (failWith(testError) as Observable).first() + try (failWith(testError) as Observable).toBlocking().first() XCTFail() } - catch { - + catch let e { + XCTAssertTrue(e as NSError === testError) } } func testFirst_someData() { - XCTAssert(try! sequenceOf(42, 43, 44, 45).first() == 42) + XCTAssert(try! sequenceOf(42, 43, 44, 45).toBlocking().first() == 42) } func testFirst_withRealScheduler() { @@ -84,6 +81,7 @@ extension ObservableBlockingTest { let array = try! interval(0.001, scheduler) .take(10) + .toBlocking() .first() XCTAssert(array == 0) @@ -94,25 +92,25 @@ extension ObservableBlockingTest { extension ObservableBlockingTest { func testLast_empty() { - XCTAssert(try! (empty() as Observable).last() == nil) + XCTAssert(try! (empty() as Observable).toBlocking().last() == nil) } func testLast_return() { - XCTAssert(try! just(42).last() == 42) + XCTAssert(try! just(42).toBlocking().last() == 42) } func testLast_fail() { do { - try (failWith(testError) as Observable).last() + try (failWith(testError) as Observable).toBlocking().last() XCTFail() } - catch { - + catch let e { + XCTAssertTrue(e as NSError === testError) } } func testLast_someData() { - XCTAssert(try! sequenceOf(42, 43, 44, 45).last() == 45) + XCTAssert(try! sequenceOf(42, 43, 44, 45).toBlocking().last() == 45) } func testLast_withRealScheduler() { @@ -120,6 +118,7 @@ extension ObservableBlockingTest { let array = try! interval(0.001, scheduler) .take(10) + .toBlocking() .last() XCTAssert(array == 9) @@ -127,3 +126,158 @@ 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) + } + + + 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, scheduler: CurrentThreadScheduler.instance) 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) + } +} diff --git a/RxTests/RxSwiftTests/Tests/Observable+ConcurrencyTest.swift b/RxTests/RxSwiftTests/Tests/Observable+ConcurrencyTest.swift index cbea87f2..f2876c7b 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() } } @@ -321,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) } @@ -373,7 +370,7 @@ class ObservableConcurrentSchedulerConcurrencyTest: ObservableConcurrencyTestBas scheduler.schedule((), action: concurrent) - try! stop.last() + try! stop.toBlocking().last() XCTAssertEqual(events, ["Started", "Started", "Ended", "Ended"]) } diff --git a/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift b/RxTests/RxSwiftTests/Tests/Observable+CreationTest.swift index a042c81b..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) @@ -91,7 +259,7 @@ extension ObservableCreationTests { return x + 1 } .take(4) - .subscribe(next: { x in + .subscribe(onNext: { x in elements.append(x) }) @@ -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) @@ -148,4 +316,201 @@ extension ObservableCreationTests { next(206, 42) ]) } +} + +// MARK: using +extension ObservableCreationTests { + func testUsing_Complete() { + 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) + ]) + + XCTAssertEqual(disposable.ticks, [ + 200, + 400 + ]) + } + + func testUsing_Error() { + 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), + 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! + 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), + 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! + + 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/RxSwiftTests/Tests/Observable+MultipleTest.swift b/RxTests/RxSwiftTests/Tests/Observable+MultipleTest.swift index ccdcee51..d34c0231 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) @@ -42,7 +42,7 @@ extension ObservableMultipleTest { let res = scheduler.start { o1.catchError { e in handlerCalled = scheduler.clock - return o2 + return o2.asObservable() } } @@ -387,7 +387,7 @@ extension ObservableMultipleTest { } } -// switch +// MARK: switch extension ObservableMultipleTest { func testSwitch_Data() { @@ -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) @@ -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 @@ -1170,7 +1468,7 @@ extension ObservableMultipleTest { ]) let res = scheduler.start { - [xs1, xs2, xs3, xs2].concat() + [xs1.asObservable(), xs2.asObservable(), xs3.asObservable(), xs2.asObservable()].concat() } let messages: [Recorded] = [ @@ -1205,8 +1503,7 @@ extension ObservableMultipleTest { } } -// merge - +// MARK: merge extension ObservableMultipleTest { func testMerge_DeadlockSimple() { var nEvents = 0 @@ -1376,7 +1673,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 +1743,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 +1816,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 +1878,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 +1944,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 +2022,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 +2100,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 +2178,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 +2256,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 +2329,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 +2402,7 @@ extension ObservableMultipleTest { completed(300) ]) - let xs: HotObservable> = scheduler.createHotObservable([ + let xs: HotObservable> = scheduler.createHotObservable([ next(210, ys1), next(260, ys2), next(270, ys3), @@ -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) @@ -3597,4 +3889,688 @@ 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") + } +} + + +// 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) { 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_TwoObservablesWithImmediateValues() { + let xs = BehaviorSubject(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) + + 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) { 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) { 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) { 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) { 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) + ]) + } + + 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 diff --git a/RxTests/RxSwiftTests/Tests/Observable+SingleTest.swift b/RxTests/RxSwiftTests/Tests/Observable+SingleTest.swift index ba7d391a..68dee9cd 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+SingleTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+SingleTest.swift @@ -766,8 +766,427 @@ 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) + + 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 empty + } + } + + let correct: [Recorded] = [ + completed(250) + ] + + XCTAssertEqual(res.messages, correct) + + XCTAssertEqual(xs.subscriptions, [ + 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 + } + } + + let correct: [Recorded] = [ + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5) + ] + + XCTAssertEqual(res.messages, correct) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 250) + ]) + } + + 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 + } + } + + 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 + } + } + + 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 testError1 + } + return a + } + } + } + + let correct: [Recorded] = [ + next(210, 1), + next(220, 2), + next(240, 1), + next(250, 2), + error(260, testError1) + ] + + 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 + } + } + + let correct: [Recorded] = [ + next(210, 1), + next(220, 2) + ] + + XCTAssertEqual(res.messages, correct) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 230) + ]) + } + + + func testRetryWhen_Incremental_BackOff() { + + 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.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(375, 1), + next(535, 1), + error(540, testError) + ] + + XCTAssertEqual(res.messages, correct) + + XCTAssertEqual(xs.subscriptions, [ + 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 + } + } +} + + + // scan extension ObservableSingleTest { diff --git a/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift b/RxTests/RxSwiftTests/Tests/Observable+StandardSequenceOperatorsTest.swift index 5a27aff3..46383f41 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() } @@ -36,9 +36,8 @@ func isPrime(i: Int) -> Bool { return true } -// where - -extension ObservableStandardSequenceOperators { +// MARK: where +extension ObservableStandardSequenceOperatorsTest { func test_filterComplete() { let scheduler = TestScheduler(initialClock: 0) @@ -210,8 +209,8 @@ extension ObservableStandardSequenceOperators { } } -// takeWhile -extension ObservableStandardSequenceOperators { +// MARK: takeWhile +extension ObservableStandardSequenceOperatorsTest { func testTakeWhile_Complete_Before() { let scheduler = TestScheduler(initialClock: 0) @@ -504,6 +503,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) @@ -524,9 +568,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 +602,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 +638,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, [ @@ -618,11 +656,52 @@ 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 -// these test are not port from Rx -extension ObservableStandardSequenceOperators { +// MARK: map +extension ObservableStandardSequenceOperatorsTest { func testMap_Never() { let scheduler = TestScheduler(initialClock: 0) @@ -962,8 +1041,830 @@ extension ObservableStandardSequenceOperators { } } -// flatMap -extension ObservableStandardSequenceOperators { +// 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 { + + 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) + ]) + } + + + 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 +extension ObservableStandardSequenceOperatorsTest { func testFlatMap_Complete() { let scheduler = TestScheduler(initialClock: 0) @@ -1033,6 +1934,7 @@ extension ObservableStandardSequenceOperators { Subscription(200, 900) ]) + XCTAssertEqual(xs.recordedEvents[2].value.subscriptions, [ Subscription(300, 760) ]) @@ -1523,7 +2425,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 @@ -2192,7 +3094,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 @@ -2272,9 +3174,9 @@ extension ObservableStandardSequenceOperators { } -// take +// MARK: take -extension ObservableStandardSequenceOperators { +extension ObservableStandardSequenceOperatorsTest { func testTake_Complete_After() { let scheduler = TestScheduler(initialClock: 0) @@ -2736,8 +3638,262 @@ extension ObservableStandardSequenceOperators { } } -// skip -extension ObservableStandardSequenceOperators { +// MARK: takeLast + +extension ObservableStandardSequenceOperatorsTest { + 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, [ + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 1000) + ]) + } + + 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) + + var elements = [Bool]() + _ = k.takeLast(1).subscribeNext { n in + elements.append(n) + k.on(.Next(!n)) + } + + k.on(.Completed) + + XCTAssertEqual(elements, [false]) + } +} + +// MARK: skip +extension ObservableStandardSequenceOperatorsTest { func testSkip_Complete_After() { let scheduler = TestScheduler(initialClock: 0) @@ -3128,4 +4284,913 @@ extension ObservableStandardSequenceOperators { Subscription(200, 400) ]) } -} \ No newline at end of file +} + +// MARK: SkipWhile +extension ObservableStandardSequenceOperatorsTest { + + 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.skipWhileWithIndex { 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.skipWhileWithIndex { 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.skipWhileWithIndex { x, i in + if i < 5 { + return true + } + throw testError + } + } + + XCTAssertEqual(res.messages, [ + error(350, testError) + ]) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 350) + ]) + } +} + +// MARK: elementAt +extension ObservableStandardSequenceOperatorsTest { + + 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, [ + error(320, RxError.ArgumentOutOfRange) + ]) + + 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) + ]) + } +} + + +// 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) + ]) + } + +} 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/RxSwiftTests/Tests/Observable+TimeTest.swift b/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift index 15a043b5..1a624d41 100644 --- a/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift +++ b/RxTests/RxSwiftTests/Tests/Observable+TimeTest.swift @@ -16,18 +16,9 @@ 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 - +// MARK: Throttle extension ObservableTimeTest { func test_ThrottleTimeSpan_AllPass() { let scheduler = TestScheduler(initialClock: 0) @@ -270,8 +261,9 @@ 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() let end = NSDate() @@ -281,8 +273,7 @@ extension ObservableTimeTest { } } -// sample - +// MARK: Sample extension ObservableTimeTest { func testSample_Sampler_SamplerThrows() { let scheduler = TestScheduler(initialClock: 0) @@ -717,8 +708,7 @@ extension ObservableTimeTest { } } -// interval - +// MARK: Interval extension ObservableTimeTest { func testInterval_TimeSpan_Basic() { @@ -772,9 +762,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) }) @@ -818,6 +808,7 @@ extension ObservableTimeTest { let a = try! interval(1, scheduler) .take(2) + .toBlocking() .toArray() let end = NSDate() @@ -827,8 +818,7 @@ extension ObservableTimeTest { } } -// take - +// MARK: Take extension ObservableTimeTest { func testTake_TakeZero() { @@ -957,7 +947,7 @@ extension ObservableTimeTest { ]) let res = scheduler.start { - xs.take(35, scheduler) + xs.take(55, scheduler).take(35, scheduler) } XCTAssertEqual(res.messages, [ @@ -1003,8 +993,7 @@ extension ObservableTimeTest { } -// take - +// MARK: Delay Subscription extension ObservableTimeTest { func testDelaySubscription_TimeSpan_Simple() { @@ -1079,7 +1068,7 @@ extension ObservableTimeTest { } } -// skip +// MARK: Skip extension ObservableTimeTest { func testSkip_Zero() { let scheduler = TestScheduler(initialClock: 0) @@ -1189,9 +1178,35 @@ extension ObservableTimeTest { } } -// +// MARK: IgnoreElements + extension ObservableTimeTest { - func bufferWithTimeOrCount_Basic() { + 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() { let scheduler = TestScheduler(initialClock: 0) let xs = scheduler.createHotObservable([ @@ -1227,7 +1242,7 @@ extension ObservableTimeTest { ]) } - func bufferWithTimeOrCount_Error() { + func testBufferWithTimeOrCount_Error() { let scheduler = TestScheduler(initialClock: 0) let xs = scheduler.createHotObservable([ @@ -1262,7 +1277,7 @@ extension ObservableTimeTest { ]) } - func bufferWithTimeOrCount_Disposed() { + func testBufferWithTimeOrCount_Disposed() { let scheduler = TestScheduler(initialClock: 0) let xs = scheduler.createHotObservable([ @@ -1293,14 +1308,222 @@ extension ObservableTimeTest { ]) } - func bufferWithTimeOrCount_Default() { + func testBufferWithTimeOrCount_Default() { let backgroundScheduler = SerialDispatchQueueScheduler(globalConcurrentQueuePriority: .Default) let result = try! range(1, 10, backgroundScheduler) .buffer(timeSpan: 1000, count: 3, scheduler: backgroundScheduler) .skip(1) + .toBlocking() .first() 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 diff --git a/RxTests/RxSwiftTests/Tests/ObserverTests.swift b/RxTests/RxSwiftTests/Tests/ObserverTests.swift index f3da5516..8dea21e9 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! + var observer: AnyObserver! 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! + var observer: AnyObserver! 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! + var observer: AnyObserver! 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.onCompleted() + observer.onNext(1) XCTAssertEqual(elements, [0]) } -} \ 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/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? diff --git a/RxTests/RxTest.swift b/RxTests/RxTest.swift index 6939dc30..008437a7 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) @@ -53,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)) } @@ -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 < 10; ++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 2362c406..ac7ce0d5 100644 --- a/RxTests/RxTests.xcodeproj/project.pbxproj +++ b/RxTests/RxTests.xcodeproj/project.pbxproj @@ -7,11 +7,17 @@ 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 */; }; 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 */; }; @@ -46,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 */; }; @@ -79,14 +86,19 @@ 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 */; }; + C8941BD91BD4F58C00A0E874 /* BackgroundThreadPrimitiveHotObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8941BD81BD4F58C00A0E874 /* BackgroundThreadPrimitiveHotObservable.swift */; }; + C8941BDA1BD4F58C00A0E874 /* BackgroundThreadPrimitiveHotObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8941BD81BD4F58C00A0E874 /* BackgroundThreadPrimitiveHotObservable.swift */; }; + C8941BDB1BD4F58C00A0E874 /* BackgroundThreadPrimitiveHotObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8941BD81BD4F58C00A0E874 /* BackgroundThreadPrimitiveHotObservable.swift */; }; C897EC3B1B10E000009C2CB0 /* BehaviorSubjectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C897EC3A1B10E000009C2CB0 /* BehaviorSubjectTest.swift */; }; 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 */; }; @@ -105,6 +117,16 @@ 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 */; }; + C8E9D2BE1BD422D80079D0DB /* Control+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E9D2BC1BD422D80079D0DB /* Control+RxTests.swift */; }; + C8E9D2BF1BD422D80079D0DB /* Control+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E9D2BC1BD422D80079D0DB /* Control+RxTests.swift */; }; + C8E9D2C41BD452650079D0DB /* Control+RxTests+Cocoa.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E9D2C01BD4525B0079D0DB /* Control+RxTests+Cocoa.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 */; }; + 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 */; }; @@ -112,8 +134,11 @@ 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 */; }; + 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 */; }; @@ -128,7 +153,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 */; }; @@ -160,9 +184,11 @@ /* 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 = ""; }; + 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 = ""; }; @@ -178,7 +204,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 = ""; }; @@ -197,6 +222,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 = ""; }; @@ -204,9 +231,11 @@ C868D10B1BB950D4003D1474 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; C868D1121BB950D4003D1474 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C88BB8B71B07E64B0064D411 /* RxTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RxTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + C8941BD81BD4F58C00A0E874 /* BackgroundThreadPrimitiveHotObservable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundThreadPrimitiveHotObservable.swift; sourceTree = ""; }; 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 = ""; }; @@ -217,7 +246,12 @@ 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 = ""; }; + 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; }; /* End PBXFileReference section */ @@ -236,6 +270,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + C84CC4EB1BDAEDF700E06A64 /* RxCocoa.framework in Frameworks */, + C84CC4E91BDADA0B00E06A64 /* RxSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -265,6 +301,8 @@ C81108151AF50DDA001C13E4 = { isa = PBXGroup; children = ( + C84CC4EA1BDAEDF700E06A64 /* RxCocoa.framework */, + C84CC4E71BDAD9FF00E06A64 /* RxBlocking.framework */, C8A468CE1B8A897D00BF917B /* RxBlocking.framework */, C8A468CC1B8A897800BF917B /* RxCocoa.framework */, C836EA001B8A76A900AB941D /* RxSwift.framework */, @@ -308,12 +346,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; @@ -334,9 +374,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 = ""; @@ -354,6 +394,9 @@ C81108371AF50E2A001C13E4 /* TestObserver.swift */, C8E381271B207D03008CDC33 /* PrimitiveHotObservable.swift */, C8E3812A1B2083C2008CDC33 /* PrimitiveMockObserver.swift */, + C80DDEDF1BCEE898006A1832 /* MainThreadPrimitiveHotObservable.swift */, + D2AF91961BD2EBB900A008C1 /* MockDisposable.swift */, + C8941BD81BD4F58C00A0E874 /* BackgroundThreadPrimitiveHotObservable.swift */, ); path = Mocks; sourceTree = ""; @@ -394,6 +437,9 @@ C811084B1AF50E2A001C13E4 /* QueueTests.swift */, C814CEA21AF5622600E98087 /* VariableTest.swift */, C69B64FF1BA88FAC00A7FA73 /* ObserverTests.swift */, + C89CDB951BCDA1F1002063D9 /* Observable+SubscriptionTest.swift */, + C80DDEDB1BCE9A03006A1832 /* Driver+Test.swift */, + C8F6A1401BEFE04F007DF367 /* SubjectConcurrencyTest.swift */, ); path = Tests; sourceTree = ""; @@ -555,13 +601,16 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C8941BD91BD4F58C00A0E874 /* BackgroundThreadPrimitiveHotObservable.swift in Sources */, 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 */, 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 */, @@ -578,6 +627,8 @@ 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 */, C8FDC5F81B2B5B7E0065F8D9 /* ElementIndexPair.swift in Sources */, @@ -585,19 +636,21 @@ 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 */, C801EB5A1B97951100C4D8C4 /* Observable+CreationTest.swift in Sources */, C81108591AF50E2A001C13E4 /* TestScheduler.swift in Sources */, 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 */, @@ -630,17 +683,22 @@ C88BB8991B07E64B0064D411 /* Observable+BindingTest.swift in Sources */, C88BB89A1B07E64B0064D411 /* NSNotificationCenterTests.swift in Sources */, C88BB89B1B07E64B0064D411 /* Observable+MultipleTest+CombineLatest.swift in Sources */, + C8941BDA1BD4F58C00A0E874 /* BackgroundThreadPrimitiveHotObservable.swift in Sources */, 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 */, + C8E9D2C41BD452650079D0DB /* Control+RxTests+Cocoa.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 */, 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 */, @@ -655,10 +713,13 @@ C88BB8A81B07E64B0064D411 /* TestScheduler.swift in Sources */, 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 */, - C88BB8AB1B07E64B0064D411 /* TestExtensions.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 */, @@ -670,13 +731,16 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C8941BDB1BD4F58C00A0E874 /* BackgroundThreadPrimitiveHotObservable.swift in Sources */, 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 */, 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 */, @@ -693,18 +757,22 @@ 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 */, 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 */, 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 */, @@ -712,9 +780,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 */, ); @@ -966,6 +1034,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; @@ -1010,9 +1079,11 @@ 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; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Release; }; @@ -1053,9 +1124,11 @@ 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; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = "Release-Tests"; }; 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 @@ , 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,121 @@ 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)) +} + +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 + + let time1: UInt64 + let time2: UInt64 + + if benchmarkTime { + first() + second() + + time1 = measureTime(first) + time2 = measureTime(second) + } + else { + time1 = 0 + time2 = 0 + } + + let memory1: (bytesAllocated: UInt64, allocations: UInt64) + let memory2: (bytesAllocated: UInt64, allocations: UInt64) + if benchmarkMemory { + + registerMallocHooks() + + 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, + memory1.allocations, + time1 + ])) + print(String(format: "#2 implementation %8d bytes %4d allocations %5d useconds", arguments: [ + memory2.bytesAllocated, + memory2.allocations, + time2 + ])) +} 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 81771800..d8ae809f 100755 --- a/scripts/pre-release-tests.sh +++ b/scripts/pre-release-tests.sh @@ -3,7 +3,7 @@ TV_OS=0 RELEASE_TEST=0 -if [ `xcodebuild -showsdks | grep tvOS | wc -l` -ge 4 ]; then +if [ `xcodebuild -showsdks | grep tvOS | wc -l` -gt 0 ]; then printf "${GREEN}tvOS found${RESET}\n" TV_OS=1 fi @@ -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 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