Merge branch 'develop' into swift-3.0

# Conflicts:
#	Documentation/DesignRationale.md
#	Rx.playground/Pages/Introduction.xcplaygroundpage/Contents.swift
#	RxCocoa/Common/DelegateProxyType.swift
#	RxCocoa/iOS/UICollectionView+Rx.swift
#	RxCocoa/iOS/UIControl+Rx.swift
#	RxCocoa/iOS/UIScrollView+Rx.swift
#	RxCocoa/iOS/UITableView+Rx.swift
#	RxExample/RxDataSources/DataSources+Rx/ObservableConvertibleType+Differentiator.swift
#	RxSwift/Concurrency/Lock.swift
#	RxSwift/Disposables/CompositeDisposable.swift
#	RxSwift/Event.swift
#	RxSwift/Extensions/String+Rx.swift
#	RxSwift/Schedulers/OperationQueueScheduler.swift
This commit is contained in:
Jamie Pinkham 2016-06-17 21:14:06 -07:00
commit 0e38ecbc87
134 changed files with 3417 additions and 2215 deletions

View File

@ -3,7 +3,36 @@ All notable changes to this project will be documented in this file.
---
## [2.4](https://github.com/ReactiveX/RxSwift/releases/tag/2.4)
## Master
#### Features
* Adds `rx_title` to `UIViewController`.
## [2.5.0](https://github.com/ReactiveX/RxSwift/releases/tag/2.5.0)
#### Features
* Exposes `installForwardDelegate`.
* Adds `proxyForObject` as protocol extension and deprecates global function version.
* Improves `installForwardDelegate` assert messaging.
* Improves gesture recognizer extensions to use typed gesture recognizers in `rx_event`.
* Adds `RxTextInput` protocol to enable creating reactive extensions for `UITextInput/NSTextInput`.
* Adds `rx_willDisplayCell` and `rx_didEndDisplayingCell` extensions to `UITableView`.
* Improves playgrounds.
#### Anomalies
* Fixes in documentation.
* Turns off Bitcode for `RxTests` CocoaPods integration.
* Fixes `UITextField.rx_text` and `UITextView.rx_text` integrations to be more robust when used with two way binding.
* Fixes two way binding example code so it now properly handles IME used in Asian cultures and adds explanations how to properly perform two way bindings. https://github.com/ReactiveX/RxSwift/issues/649
* Removes `distinctUntilChanged` from control extensions. https://github.com/ReactiveX/RxSwift/issues/626
## [2.4.0](https://github.com/ReactiveX/RxSwift/releases/tag/2.4)
#### Features

74
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,74 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting one of the project maintainers https://github.com/ReactiveX/RxSwift/graphs/contributors. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@ -3,7 +3,7 @@ API
## RxSwift supported operators
In some cases there are multiple aliases for the same operator, because on different platforms / implementations, the same operation is sometimes called differently. Sometimes this is because historical reasons, sometimes because of reserved language keywords.
In some cases there are multiple aliases for the same operator, because on different platforms / implementations, the same operation is sometimes named differently. Sometimes this is because of historical reasons, while sometimes because of reserved language keywords.
When lacking a strong community consensus, RxSwift will usually include multiple aliases.
@ -26,6 +26,7 @@ Operators are stateless by default.
* [`timer`](http://reactivex.io/documentation/operators/timer.html)
#### Transforming Observables
* [`buffer`](http://reactivex.io/documentation/operators/buffer.html)
* [`flatMap`](http://reactivex.io/documentation/operators/flatmap.html)
* [`flatMapFirst`](http://reactivex.io/documentation/operators/flatmap.html)
@ -35,6 +36,7 @@ Operators are stateless by default.
* [`window`](http://reactivex.io/documentation/operators/window.html)
#### Filtering Observables
* [`debounce` / `throttle`](http://reactivex.io/documentation/operators/debounce.html)
* [`distinctUntilChanged`](http://reactivex.io/documentation/operators/distinct.html)
* [`elementAt`](http://reactivex.io/documentation/operators/elementat.html)
@ -71,6 +73,7 @@ Operators are stateless by default.
* 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)
@ -406,6 +409,13 @@ extension UIActivityIndicatorView {
}
```
```swift
extension UINavigationItem {
public var rx_title: AnyObserver<String?> {}
}
```
**OSX**
```swift

View File

@ -2,18 +2,18 @@
RxSwift is somewhat similar to ReactiveCocoa since ReactiveCocoa borrows a large number of concepts from Rx.
One of the main goals of this project was to create a significantly simpler interface that is more aligned with other Rx implementations, offers richer concurrency model, offers more optimization opportunities and is more aligned with built-in Swift error handling mechanisms.
One of the main goals of this project was to create a significantly simpler interface that is more aligned with other Rx implementations, offers a richer concurrency model, offers more optimization opportunities and is more aligned with built-in Swift error handling mechanisms.
We've also decided to only rely on Swift/llvm compiler and not introduce any external dependencies.
We've also decided to only rely on the Swift/llvm compiler and not introduce any external dependencies.
Probably the main difference between these projects is the difference of approach in building abstractions.
Probably the main difference between these projects is in their approach in building abstractions.
The main goal of RxSwift project is to provide environment agnostic compositional computation glue abstracted in the form of observable sequences.
We then aim to improve the experience of using RxSwift on specific platforms. To do this RxCocoa uses those generic computations to build more practical abstractions and wrap Foundation/Cocoa/UKit frameworks. That means that other libraries give context and semantics to the generic computation engine RxSwift provides such as `Driver`, `ControlProperty`, `ControlEvent`s and more.
We then aim to improve the experience of using RxSwift on specific platforms. To do this, RxCocoa uses generic computations to build more practical abstractions and wrap Foundation/Cocoa/UKit frameworks. That means that other libraries give context and semantics to the generic computation engine RxSwift provides such as `Driver`, `ControlProperty`, `ControlEvent`s and more.
One of the benefits to representing all these abstractions as a single concept; _observable sequences_, is that all computation abstractions built on top of them are also composable in the same fundamental way. They all follow the same contract and implement the same interface.
One of the benefits to representing all of these abstractions as a single concept - _observable sequences_ - is that all computation abstractions built on top of them are also composable in the same fundamental way. They all follow the same contract and implement the same interface.
It is also easy to create flexible subscription (resource) sharing strategies or use one of the built-in ones: `share`, `shareReplay`, `publish`, `multicast`, `shareReplayLatestWhileConnected`...
This library also offers fine-tunable concurrency model. In case concurrent schedulers are used, observable sequence operators will preserve sequence properties. The same observable sequence operators will also know how to detect and optimally use known serial schedulers. ReactiveCocoa has a more limited concurrency model and only allows serial schedulers.
This library also offers a fine-tunable concurrency model. If concurrent schedulers are used, observable sequence operators will preserve sequence properties. The same observable sequence operators will also know how to detect and optimally use known serial schedulers. ReactiveCocoa has a more limited concurrency model and only allows serial schedulers.
Multithreaded programming is really hard and detecting non trivial loops is even harder. That's why all operators are built in a fault tolerant way. Even in case element generation occurs during element processing (recursion), operators will try to handle that situation and prevent deadlocks. That means that in worst possible case programming error will cause stack overflow, but users won't have to manually kill the app, and you will get crash report in error reporting systems so you can find and fix the problem.
Multithreaded programming is really hard and detecting non trivial loops is even harder. That's why all operators are built in a fault tolerant way. Even if element generation occurs during element processing (recursion), operators will try to handle that situation and prevent deadlocks. This means that in the worst possible case programming error will cause stack overflow, but users won't have to manually kill the app, and you will get a crash report in error reporting systems so you can find and fix the problem.

View File

@ -6,34 +6,34 @@ Design Rationale
```Swift
enum Event<Element> {
case Next(Element) // next element of a sequence
case Error(ErrorProtocol) // sequence failed with error
case Error(ErrorType) // sequence failed with error
case Completed // sequence terminated successfully
}
```
Let's discuss pros and cons of `ErrorProtocol` being generic.
Let's discuss the pros and cons of `ErrorType` being generic.
If you have generic error type you create additional impedance mismatch between two observables.
If you have a generic error type, you create additional impedance mismatch between two observables.
Let's say you have:
`Observable<String, E1>` and `Observable<String, E2>`
There isn't much you can do with them without figuring out what will be the resulting error type.
There isn't much you can do with them without figuring out what the resulting error type will be.
Will it be `E1`, `E2` or some new `E3` maybe? So you need a new set of operators just to solve that impedance mismatch.
Will it be `E1`, `E2` or some new `E3` maybe? So, you would need a new set of operators just to solve that impedance mismatch.
This for sure hurts composition properties, and Rx really doesn't care about why sequence fails, it just usually forwards failure further down the observable chain.
This hurts composition properties, and Rx isn't concerned with why a sequence fails, it just usually forwards failures further down the observable chain.
There is additional problem that maybe in some cases operators will fail for some internal error, and in that case you won't be able to construct resulting error and report failure.
There is an additional problem that, in some cases, operators might fail due to some internal error, in which case you wouldn't be able to construct a resulting error and report failure.
But ok, let's ignore that and assume we can use that to model sequences that don't error out. It looks like it could be useful for that purpose?
But OK, let's ignore that and assume we can use that to model sequences that don't error out. Could it be useful for that purpose?
Well yes, it potentially could be, but lets consider why would you want to use sequences that don't error out.
Well yes, it potentially could be, but let's consider why you would want to use sequences that don't error out.
One obvious application would be for permanent streams in UI layer that drive entire UI. But when you consider that case, it's not really only sufficient to use compiler to prove that sequences don't error out, you also need to prove other properties. Like that elements are observed on `MainScheduler`.
One obvious application would be for permanent streams in the UI layer that drive the entire UI. When you consider that case, it's not really sufficient to only use the compiler to prove that sequences don't error out, you also need to prove other properties. For instance, that elements are observed on `MainScheduler`.
What you really need is a generic way to prove traits for sequences (`Observables`). And you could be interested in a lot of properties. For example:
What you really need is a generic way to prove traits for observable sequences. There are a lot of properties you could be interested in. For example:
* sequence terminates in finite time (server side)
* sequence contains only one element (if you are running some computation)
@ -41,19 +41,19 @@ What you really need is a generic way to prove traits for sequences (`Observable
* sequence doesn't error out, never terminates and elements are delivered on main scheduler, and has refcounted sharing (UI)
* sequence doesn't error out, never terminates and elements are delivered on specific background scheduler (audio engine)
What you really want is a general compiler enforced system of traits for observable sequences, and a set of invariant operators for those wanted properties.
What you really want is a general compiler-enforced system of traits for observable sequences, and a set of invariant operators for those wanted properties.
A good analogy IMHO would be
A good analogy would be:
```
1, 3.14, e, 2.79, 1 + 1i <-> Observable<E>
1m/s, 1T, 5kg, 1.3 pounds <-> Errorless observable, UI observable, Finite observable ...
```
There are many ways how to do that in Swift by either using composition or inheritance of observables.
There are many ways to do such a thing in Swift by either using composition or inheritance of observables.
Additional benefit of using unit system is that you can prove that UI code is executing on same scheduler and thus use lockless operators for all transformations.
An additional benefit of using a unit system is that you can prove that UI code is executing on the same scheduler and thus use lockless operators for all transformations.
Since Rx already doesn't have locks for single sequence operations, and all of the remaining locks are in statefull components (aka UI), that would practically remove all of the remaining locks out of Rx code and create compiler enforced lockless Rx code.
Since RxSwift already doesn't have locks for single sequence operations, and all of the locks that do exist are in stateful components (e.g. UI), there are practically no locks in RxSwift code, which allows for such details to be compiler enforced.
So IMHO, there really is no benefit of using typed Errors that couldn't be achieved cleaner in other ways while preserving Rx compositional semantics. And other ways also have huge other benefits.
There really is no benefit to using typed Errors that couldn't be achieved in other cleaner ways while preserving Rx compositional semantics.

View File

@ -9,92 +9,82 @@ Examples
## Calculated variable
Let's first start with some imperative swift code.Sw
The purpose of example is to bind identifier `c` to a value calculated from `a` and `b` if some condition is satisfied.
First, let's start with some imperative code.
The purpose of this example is to bind the identifier `c` to a value calculated from `a` and `b` if some condition is satisfied.
Here is the imperative swift code that calculates the value of `c`:
Here is the imperative code that calculates the value of `c`:
```swift
// this is usual imperative code
// this is standard imperative code
var c: String
var a = 1 // this will only assign value `1` to `a` once
var b = 2 // this will only assign value `2` to `b` once
var a = 1 // this will only assign the value `1` to `a` once
var b = 2 // this will only assign the value `2` to `b` once
if a + b >= 0 {
c = "\(a + b) is positive" // this will only assign value to `c` once
c = "\(a + b) is positive" // this will only assign the value to `c` once
}
```
The value of `c` is now `3 is positive`. But if we change the value of `a` to `4`, `c` will still contain the old value.
The value of `c` is now `3 is positive`. However, if we change the value of `a` to `4`, `c` will still contain the old value.
```swift
a = 4 // c will still be equal "3 is positive" which is not good
// c should be equal to "6 is positive" because 4 + 2 = 6
a = 4 // `c` will still be equal to "3 is positive" which is not good
// we want `c` to be equal to "6 is positive" since 4 + 2 = 6
```
This is not the wanted behavior.
This is not the desired behavior.
To integrate RxSwift framework into your project just include framework in your project and write `import RxSwift`.
This is the same logic using RxSwift.
This is the improved logic using RxSwift:
```swift
let a /*: Observable<Int>*/ = Variable(1) // a = 1
let b /*: Observable<Int>*/ = Variable(2) // b = 2
// This will "bind" rx variable `c` to definition
// if a + b >= 0 {
// c = "\(a + b) is positive"
// }
// combines latest values of variables `a` and `b` using `+`
let c = Observable.combineLatest(a.asObservable(), b.asObservable()) { $0 + $1 }
.filter { $0 >= 0 } // if `a + b >= 0` is true, `a + b` is passed to map operator
.filter { $0 >= 0 } // if `a + b >= 0` is true, `a + b` is passed to the map operator
.map { "\($0) is positive" } // maps `a + b` to "\(a + b) is positive"
// Since initial values are a = 1, b = 2
// 1 + 2 = 3 which is >= 0, `c` is intially equal to "3 is positive"
// Since the initial values are a = 1 and b = 2
// 1 + 2 = 3 which is >= 0, so `c` is initially equal to "3 is positive"
// 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".
// To pull values out of the Rx `Observable` `c`, subscribe to values from `c`.
// `subscribeNext` means subscribe to the next (fresh) values of `c`.
// That also includes the initial value "3 is positive".
c.subscribeNext { print($0) } // prints: "3 is positive"
// Now let's increase the value of `a`
// a = 4 is in RxSwift
// Now, let's increase the value of `a`
a.value = 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`.
// The sum of the latest values, `4` and `2`, is now `6`.
// Since this is `>= 0`, the `map` operator produces "6 is positive"
// and that result is "assigned" to `c`.
// Since the value of `c` changed, `{ print($0) }` will get called,
// and "6 is positive" is printed.
// and "6 is positive" will be printed.
// Now let's change the value of `b`
// b = -8 is in RxSwift
// Now, let's change the value of `b`
b.value = -8 // doesn't print anything
// Sum of latest values is `4 + (-8)`, `-4` is not >= 0, map doesn't
// 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,
// The sum of the latest values, `4 + (-8)`, is `-4`.
// Since this is not `>= 0`, `map` doesn't get executed.
// This means that `c` still contains "6 is positive"
// Since `c` hasn't been updated, a new "next" value hasn't been produced,
// and `{ print($0) }` won't be called.
// ...
```
## Simple UI bindings
* instead of binding to variables, let's bind to text field values (rx_text)
* next, parse that into an int and calculate if the number is prime using an async API (map)
* if text field value is changed before async call completes, new async call will be enqueued (concat)
* bind results to label (bindTo(resultLabel.rx_text))
* Instead of binding to variables, let's bind to `UITextField` values using the `rx_text` property
* Next, `map` the `String` into an `Int` and determine if the number is prime using an async API
* If the text is changed before the async call completes, a new async call will replace it via `concat`
* Bind the results to a `UILabel`
```swift
let subscription/*: Disposable */ = primeTextField.rx_text // type is Observable<String>
.map { WolframAlphaIsPrime(Int($0) ?? 0) } // type is Observable<Observable<Prime>>
.map { WolframAlphaIsPrime(Int($0) ?? 0) } // type is Observable<Observable<Prime>>
.concat() // type is Observable<Prime>
.map { "number \($0.n) is prime? \($0.isPrime)" } // type is Observable<String>
.bindTo(resultLabel.rx_text) // return Disposable that can be used to unbind everything
// This will set resultLabel.text to "number 43 is prime? true" after
// This will set `resultLabel.text` to "number 43 is prime? true" after
// server call completes.
primeTextField.text = "43"
@ -104,13 +94,13 @@ primeTextField.text = "43"
subscription.dispose()
```
All of the operators used in this example are the same operators used in the first example with variables. Nothing special about it.
All of the operators used in this example are the same operators used in the first example with variables. There's nothing special about it.
## Autocomplete
If you are new to Rx, next example will probably be a little overwhelming, but it's here to demonstrate how RxSwift code looks like in real world examples.
If you are new to Rx, the next example will probably be a little overwhelming at first. However, it's here to demonstrate how RxSwift code looks in the real-world.
The third example is a real world, complex UI async validation logic, with progress notifications.
This example contains complex async UI validation logic with progress notifications.
All operations are cancelled the moment `disposeBag` is deallocated.
Let's give it a shot.
@ -124,25 +114,25 @@ self.usernameOutlet.rx_text
// synchronous validation, nothing special here
if username.isEmpty {
// Convenience for constructing synchronous result.
// In case there is mixed synchronous and asychronous code inside the same
// method, this will construct an async result that is resolved immediatelly.
// In case there is mixed synchronous and asynchronous code inside the same
// method, this will construct an async result that is resolved immediately.
return Observable.just((valid: false, message: "Username can't be empty."))
}
...
// ...
// Every user interface probably shows some state while async operation
// is executing.
// Let's assume that we want to show "Checking availability" while waiting for result.
// valid parameter can be
// User interfaces should probably show some state while async operations
// are executing.
// Let's assume that we want to show "Checking availability" while waiting for a result.
// Valid parameters can be:
// * true - is valid
// * false - not valid
// * false - is not valid
// * nil - validation pending
typealias LoadingInfo = (valid : String?, message: String?)
typealias LoadingInfo = (valid: String?, message: String?)
let loadingValue : LoadingInfo = (valid: nil, message: "Checking availability ...")
// This will fire a server call to check if the username already exists.
// Guess what, its type is `Observable<ValidationResult>`
// Its type is `Observable<ValidationResult>`
return API.usernameAvailable(username)
.map { available in
if available {
@ -156,26 +146,25 @@ self.usernameOutlet.rx_text
.startWith(loadingValue)
}
// Since we now have `Observable<Observable<ValidationResult>>`
// we somehow need to return to normal `Observable` world.
// We could use `concat` operator from second example, but we really
// want to cancel pending asynchronous operation if new username is
// provided.
// That's what `switchLatest` does
// we need to somehow return to a simple `Observable<ValidationResult>`.
// We could use the `concat` operator from the second example, but we really
// want to cancel pending asynchronous operations if a new username is provided.
// That's what `switchLatest` does.
.switchLatest()
// Now we need to bind that to the user interface somehow.
// Good old `subscribeNext` can do that
// Good old `subscribeNext` can do that.
// That's the end of `Observable` chain.
// This will produce a `Disposable` object that can unbind everything and cancel
// pending async operations.
.subscribeNext { valid in
errorLabel.textColor = validationColor(valid)
errorLabel.text = valid.message
}
// Why would we do it manually, that's tedious,
// let's dispose everything automagically on view controller dealloc.
// This will produce a `Disposable` object that can unbind everything and cancel
// pending async operations.
// Instead of doing it manually, which is tedious,
// let's dispose everything automagically upon view controller dealloc.
.addDisposableTo(disposeBag)
```
Can't get any simpler than this. There are [more examples](../RxExample) in the repository, so feel free to check them out.
It doesn't get any simpler than that. There are [more examples](../RxExample) in the repository, so feel free to check them out.
They include examples on how to use it in the context of MVVM pattern or without it.
They include examples on how to use Rx in the context of MVVM pattern or without it.

View File

@ -26,11 +26,13 @@ This project tries to be consistent with [ReactiveX.io](http://reactivex.io/). T
# Observables aka Sequences
## Basics
The [Equivalence](MathBehindRx.md) of observer patterns (`Observable<Element>`) and sequences (`Generator`s)
is one of the most important things to understand about Rx.
The [equivalence](MathBehindRx.md) of observer pattern (`Observable<Element>` sequence) and normal sequences (`SequenceType`) is the most important thing to understand about Rx.
The observer pattern is needed because we want to model asynchronous behavior.
That equivalence enables the implementation of high level sequence operations as operators on `Observable`s.
**Every `Observable` sequence is just a sequence. The key advantage for an `Observable` vs Swift's `SequenceType` is that it can also receive elements asynchronously. This is the kernel of the RxSwift, documentation from here is about ways that we expand on that idea.**
* `Observable`(`ObservableType`) is equivalent to `SequenceType`
* `ObservableType.subscribe` method is equivalent to `SequenceType.generate` method.
* Observer (callback) needs to be passed to `ObservableType.subscribe` method to receive sequence elements instead of calling `next()` on the returned generator.
Sequences are a simple, familiar concept that is **easy to visualize**.

View File

@ -1,6 +1,6 @@
**Please copy the following template [here](https://github.com/ReactiveX/RxSwift/issues/new) and fill in the missing fields so we can help you as soon as possible.**
**If you don't have something to report in the following format, maybe you don't really have an issue an it's easier and faster to resolve doubts in [slack channel](http://http://slack.rxswift.org/) first.**
**If you don't have something to report in the following format, it will probably be easier and faster to ask in the [slack channel](http://http://slack.rxswift.org/) first.**
```
*Short description*:
@ -17,31 +17,29 @@
*Expected outcome*:
what do you expect to happen goes here
what you expect to happen goes here
*What actually happens*:
what actually happens goes here
// filling additional information below is optional, but resolving your issue could be potentially a lot faster
// filling in additional information below is optional, but resolving your issue could potentially be a lot faster
*Integration method*:
(so we don't lose time investigating wrong integration)
*Installation method*:
(so we don't waste time investigating an incorrect integration)
* CocoaPods
* Carthage
* Git submodules
*I have multiple versions of Xcode installed*:
(so we can understand can this cause your issue)
(so we can know if this is a potential cause of your issue)
* yes (which ones)
* no
*How long have I been using this project*:
*Level of RxSwift knowledge*:
(this is so we can understand your level of knowledge
and formulate the response in an appropriate manner)
* just starting
* I have a small code base
* I have a significant code base
```

View File

@ -16,10 +16,10 @@ let package = Package(
)
```
What does work:
What works:
* Distribution using Swift Package Manager
* Single Threaded mode (CurrentThreadScheduler)
* Half of unit tests are passing.
* Half of the unit tests are passing.
* Projects that can be compiled and "used":
* RxSwift
* RxBlocking

View File

@ -3,11 +3,11 @@ Math Behind Rx
## Duality between Observer and Iterator / Enumerator / Generator / Sequences
There is a duality between the observer and generator patterns. This is what enables us to transition from the async callback world to synchronous world of sequence transformations.
There is a duality between the observer and generator patterns. This is what enables us to transition from the async callback world to the synchronous world of sequence transformations.
In short, the enumerator and observer patterns both describe sequences. It's fairly obvious why the enumerator defines a sequence, but the observer is slightly more complicated.
In short, the enumerator and observer patterns both describe sequences. It's fairly obvious why the enumerator defines a sequence, but the observer is slightly more complicated.
There is, however, a pretty simple example that doesn't require a lot of mathematical knowledge. Assume that you are observing the position of your mouse cursor on screen at given time periods. Over time, these mouse positions form a sequence. This is, in essence, an observer sequence.
There is, however, a pretty simple example that doesn't require a lot of mathematical knowledge. Assume that you are observing the position of your mouse cursor on screen at given times. Over time, these mouse positions form a sequence. This is, in essence, an observable sequence.
There are two basic ways elements of a sequence can be accessed:

View File

@ -1,26 +1,26 @@
# Migration from RxSwift 1.9 to RxSwift 2.0 version
# Migration from RxSwift 1.9 to RxSwift 2.0
The migration should be pretty straightforward. The changes are mostly cosmetic, so all features are still there.
The migration should be pretty straightforward. Changes are mostly cosmetic, so all features are still there.
* Find replace all `>- ` to `.`
* Find replace all "variable" to "shareReplay(1)"
* Find replace all "catch" to "catchErrorJustReturn"
* Find replace all "returnElement" to "Observable.just"
* Find replace all "failWith" to "Observable.error"
* Find replace all "never" to "Observable.never"
* Find replace all "empty" to "Observable.empty"
* Since we've moved from `>-` to `.`, free functions are now methods, so it's `.switchLatest()`, `.distinctUntilChanged()`, ... instead of `>- switchLatest`, `>- distinctUntilChanged`
* we've moved from free functions to extensions so it's now `[a, b, c].concat()`, `.merge()`, ... instead of `concat([a, b, c])`, `merge(sequences)`
* Now it's `subscribe { n in ... }.addDisposableTo(disposeBag)` instead of `>- disposeBag.addDisposable`
* Method `next` on `Variable` is now `value` setter
* If you want to use `tableViews`/`collectionViews`, this is the basic use case now
* Find replace all `variable` to `shareReplay(1)`
* Find replace all `catch` to `catchErrorJustReturn`
* Find replace all `returnElement` to `Observable.just`
* Find replace all `failWith` to `Observable.error`
* Find replace all `never` to `Observable.never`
* Find replace all `empty` to `Observable.empty`
* Since we've moved from `>-` to `.`, free functions are now methods, so use `.switchLatest()`, `.distinctUntilChanged()`, ... instead of `>- switchLatest`, `>- distinctUntilChanged`
* We've moved from free functions to extensions so it's now `[a, b, c].concat()`, `.merge()`, ... instead of `concat([a, b, c])`, `merge(sequences)`
* Similarly, it's now `subscribe { n in ... }.addDisposableTo(disposeBag)` instead of `>- disposeBag.addDisposable`
* The method `next` on `Variable` is now `value` setter
* If you want to use `UITableView` and/or `UICollectionView`, this is the basic use case now:
```swift
viewModel.rows
.bindTo(resultsTableView.rx_itemsWithCellIdentifier("WikipediaSearchCell", cellType: WikipediaSearchCell.self)) { (_, viewModel, cell) in
cell.viewModel = viewModel
}
.addDisposableTo(disposeBag)
.bindTo(resultsTableView.rx_itemsWithCellIdentifier("WikipediaSearchCell", cellType: WikipediaSearchCell.self)) { (_, viewModel, cell) in
cell.viewModel = viewModel
}
.addDisposableTo(disposeBag)
```
If you have any more doubts how to write some concept in RxSwift 2.0 version, check out [Example app](../RxExample) or playgrounds.
If you have any doubts about how some concept in RxSwift 2.0 works, check out the [Example app](../RxExample) or playgrounds.

View File

@ -3,6 +3,6 @@
To use playgrounds:
* Open `Rx.xcworkspace`
* Build `RxSwift-OSX` scheme
* And then open `Rx` playground in `Rx.xcworkspace` tree view.
* Choose `View > Show Debug Area`
* Build the `RxSwift-OSX` scheme
* Open `Rx` playground in the `Rx.xcworkspace` tree view.
* Choose `View > Debug Area > Show Debug Area`

View File

@ -3,7 +3,7 @@ Unit Tests
## Testing custom operators
Library uses `RxTests` for all of RxSwift operator tests so you can take a look at AllTests-* target inside the project `Rx.xcworkspace`.
RxSwift uses `RxTests` for all operator tests, located in the AllTests-* target inside the project `Rx.xcworkspace`.
This is an example of a typical `RxSwift` operator unit test:
@ -12,16 +12,16 @@ func testMap_Range() {
// Initializes test scheduler.
// Test scheduler implements virtual time that is
// detached from local machine clock.
// That enables running the simulation as fast as possible
// This enables running the simulation as fast as possible
// and proving that all events have been handled.
let scheduler = TestScheduler(initialClock: 0)
// Creates a mock hot observable sequence.
// The sequence will emit events at following
// times no matter is there some observer subscribed.
// The sequence will emit events at desginated
// times, no matter if there are observers subscribed or not.
// (that's what hot means).
// This observable sequence will also record all subscriptions
// made during it's lifetime (`subscriptions` property).
// made during its lifetime (`subscriptions` property).
let xs = scheduler.createHotObservable([
next(150, 1), // first argument is virtual time, second argument is element value
next(210, 0),
@ -32,10 +32,10 @@ func testMap_Range() {
])
// `start` method will by default:
// * run the simulation and record all events
// * Run the simulation and record all events
// using observer referenced by `res`.
// * subscribe at virtual time 200
// * dispose subscription at virtual time 1000
// * Subscribe at virtual time 200
// * Dispose subscription at virtual time 1000
let res = scheduler.start { xs.map { $0 * 2 } }
let correctMessages = [
@ -57,9 +57,9 @@ func testMap_Range() {
## Testing operator compositions (view models, components)
Examples how to test operator compositions are contained inside `Rx.xcworkspace` > `RxExample-iOSTests` target.
Examples of how to test operator compositions are contained inside `Rx.xcworkspace` > `RxExample-iOSTests` target.
It easy to define `RxTests` extensions so you can write your tests in a readable way. Provided examples inside `RxExample-iOSTests` are just a tip how you can write those extensions, but there is a lot of possibilities how to write those tests.
It's easy to define `RxTests` extensions so you can write your tests in a readable way. Provided examples inside `RxExample-iOSTests` are just suggestions on how you can write those extensions, but there are a lot of possibilities on how to write those tests.
```swift
// expected events and test data

View File

@ -1,42 +1,42 @@
Units
=====
This document will try to describe what units are, why they are a useful concept, how to use and create them.
This document will try to describe what units are, why they are a useful concept, and how to use and create them.
* [Why](#why)
* [How do they work](#how-do-they-work)
* [Why are they named Units](#why-are-they-named-units)
* [How they work](#how-they-work)
* [Why they are named Units](#why-they-are-named-units)
* [RxCocoa units](#rxcocoa-units)
* [Driver unit](#driver-unit)
* [Why was it named Driver](#why-was-it-named-driver)
* [Why it's named Driver](#why-its-named-driver)
* [Practical usage example](#practical-usage-example)
## Why
Swift has a powerful type system that can be used to improve correctness and stability of applications and make using Rx a more intuitive and straightforward experience.
**Units are so far specific only to the [RxCocoa](https://github.com/ReactiveX/RxSwift/tree/master/RxCocoa) project, but the same principles could be implemented easily in other Rx implementations if necessary. There is no private API magic needed.**
**Units are specific only to the [RxCocoa](https://github.com/ReactiveX/RxSwift/tree/master/RxCocoa) project. However, the same principles could easily be implemented in other Rx implementations, if necessary. There is no private API magic needed.**
**Units are totally optional, you can use raw observable sequences everywhere in your program and all RxCocoa APIs work with observable sequences.**
**Units are totally optional. You can use raw observable sequences everywhere in your program and all RxCocoa APIs work with observable sequences.**
Units also help communicate and ensure observable sequence properties across interface boundaries.
Here are some of the properties that are important when writing Cocoa/UIKit applications.
* can't error out
* observe on main scheduler
* subscribe on main scheduler
* sharing side effects
* Can't error out
* Observe on main scheduler
* Subscribe on main scheduler
* Sharing side effects
## How do they work
## How they work
In its core it's just a struct with a reference to observable sequence.
At its core, it's just a struct with a reference to observable sequence.
You can think of them as a kind of builder pattern for observable sequences. When sequence is built, calling `.asObservable()` will transform a unit into a vanilla observable sequence.
You can think of them as a kind of builder pattern for observable sequences. When a sequence is built, calling `.asObservable()` will transform a unit into a vanilla observable sequence.
## Why are they named Units
## Why they are named Units
Analogies help reason about unfamiliar concepts, here are some ideas how units in physics and RxCocoa (rx units) are similar.
Using a couple analogies will help us reason about unfamiliar concepts. Here are some analogies showing how units in physics and RxCocoa (Rx units) are similar.
Analogies:
@ -45,14 +45,14 @@ Analogies:
| number (one value) | observable sequence (sequence of values) |
| dimensional unit (m, s, m/s, N ...) | Swift struct (Driver, ControlProperty, ControlEvent, Variable, ...) |
Physical unit is a pair of a number and a corresponding dimensional unit.<br/>
Rx unit is a pair of an observable sequence and a corresponding struct that describes observable sequence properties.
A physical unit is a pair of a number and a corresponding dimensional unit.<br/>
An Rx unit is a pair of an observable sequence and a corresponding struct that describes observable sequence properties.
Numbers are the basic composition glue when working with physical units: usually real or complex numbers.<br/>
Observable sequences are the basic composition glue when working with rx units.
Numbers are the basic compositional glue when working with physical units: usually real or complex numbers.<br/>
Observable sequences are the basic compositional glue when working with Rx units.
Physical units and [dimensional analysis](https://en.wikipedia.org/wiki/Dimensional_analysis#Checking_equations_that_involve_dimensions) can alleviate certain class of errors during complex calculations.<br/>
Type checking rx units can alleviate certain class of logic errors when writing reactive programs.
Physical units and [dimensional analysis](https://en.wikipedia.org/wiki/Dimensional_analysis#Checking_equations_that_involve_dimensions) can alleviate certain classes of errors during complex calculations.<br/>
Type checking Rx units can alleviate certain classes of logic errors when writing reactive programs.
Numbers have operators: `+`, `-`, `*`, `/`.<br/>
Observable sequences also have operators: `map`, `filter`, `flatMap` ...
@ -62,25 +62,25 @@ Physics units define operations by using corresponding number operations. E.g.
`/` operation on physical units is defined using `/` operation on numbers.
11 m / 0.5 s = ...
* first convert unit to **numbers** and **apply** `/` **operator** `11 / 0.5 = 22`
* then calculate unit (m / s)
* combine the result = 22 m / s
* First, convert the unit to **numbers** and **apply** `/` **operator** `11 / 0.5 = 22`
* Then, calculate the unit (m / s)
* Lastly, combine the result = 22 m / s
Rx units define operations by using corresponding observable sequence operations (this is how operators work internally). E.g.
The `map` operation on `Driver` is defined using the `map` operation on its observable sequence.
```swift
let d: Driver<Int> = Drive.just(11)
let d: Driver<Int> = Driver.just(11)
driver.map { $0 / 0.5 } = ...
```
* first convert driver to **observable sequence** and **apply** `map` **operator**
* First, convert `Driver` to **observable sequence** and **apply** `map` **operator**
```swift
let mapped = driver.asObservable().map { $0 / 0.5 } // this `map` is defined on observable sequence
```
* then combine that to get the unit value
* Then, combine that to get the unit value
```swift
let result = Driver(mapped)
```
@ -88,10 +88,10 @@ let result = Driver(mapped)
There is a set of basic units in physics [(`m`, `kg`, `s`, `A`, `K`, `cd`, `mol`)](https://en.wikipedia.org/wiki/SI_base_unit) that is orthogonal.<br/>
There is a set of basic interesting properties for observable sequences in `RxCocoa` that is orthogonal.
* can't error out
* observe on main scheduler
* subscribe on main scheduler
* sharing side effects
* Can't error out
* Observe on main scheduler
* Subscribe on main scheduler
* Sharing side effects
Derived units in physics sometimes have special names.<br/>
E.g.
@ -109,16 +109,16 @@ ControlProperty = (sharing side effects) * (subscribe on main scheduler)
Variable = (can't error out) * (sharing side effects)
```
Conversion between different units in physics is done with a help of operators defined on numbers `*`, `/`.<br/>
Conversion between different rx units in done with a help of observable sequence operators.
Conversion between different units in physics is done with the help of operators defined on numbers `*`, `/`.<br/>
Conversion between different Rx units in done with the help of observable sequence operators.
E.g.
```
can't error out = catchError
observe on main scheduler = observeOn(MainScheduler.instance)
subscribe on main scheduler = subscribeOn(MainScheduler.instance)
sharing side effects = share* (one of the `share` operators)
Can't error out = catchError
Observe on main scheduler = observeOn(MainScheduler.instance)
Subscribe on main scheduler = subscribeOn(MainScheduler.instance)
Sharing side effects = share* (one of the `share` operators)
```
@ -126,29 +126,29 @@ sharing side effects = share* (one of the `share` operators)
### Driver unit
* can't error out
* observe on main scheduler
* sharing side effects (`shareReplayLatestWhileConnected`)
* Can't error out
* Observe on main scheduler
* Sharing side effects (`shareReplayLatestWhileConnected`)
### ControlProperty / ControlEvent
* can't error out
* subscribe on main scheduler
* observe on main scheduler
* sharing side effects
* Can't error out
* Subscribe on main scheduler
* Observe on main scheduler
* Sharing side effects
### Variable
* can't error out
* sharing side effects
* Can't error out
* Sharing side effects
## Driver
This is the most elaborate unit. It's intention is to provide an intuitive way to write reactive code in UI layer.
This is the most elaborate unit. Its intention is to provide an intuitive way to write reactive code in the UI layer.
### Why was it named Driver
### Why it's named Driver
It's intended use case was to model sequences that drive your application.
Its intended use case was to model sequences that drive your application.
E.g.
* Drive UI from CoreData model
@ -156,9 +156,9 @@ E.g.
...
Like normal operating system drivers, in case one of those sequence errors out your application will stop responding to user input.
Like normal operating system drivers, in case a sequence errors out, your application will stop responding to user input.
It is also extremely important that those elements are observed on main thread because UI elements and application logic are usually not thread safe.
It is also extremely important that those elements are observed on the main thread because UI elements and application logic are usually not thread safe.
Also, `Driver` unit builds an observable sequence that shares side effects.
@ -167,7 +167,7 @@ E.g.
### Practical usage example
This is an typical beginner example.
This is a typical beginner example.
```swift
let results = query.rx_text
@ -189,14 +189,14 @@ results
```
The intended behavior of this code was to:
* throttle user input
* contact server and fetch a list of user results (once per query)
* then bind the results to two UI elements, results table view and a label that displays number of results
* Throttle user input
* Contact server and fetch a list of user results (once per query)
* Bind the results to two UI elements: results table view and a label that displays the number of results
So what are the problems with this code:
* in case the `fetchAutoCompleteItems` observable sequence errors out (connection failed, or parsing error), this error would unbind everything and UI wouldn't respond any more to new queries.
* in case `fetchAutoCompleteItems` returns results on some background thread, results would be bound to UI elements from a background thread and that could cause non deterministic crashes.
* results are bound to two UI elements, which means that for each user query two HTTP requests would be made, one for each UI element, which is not intended behavior.
So, what are the problems with this code?:
* If the `fetchAutoCompleteItems` observable sequence errors out (connection failed or parsing error), this error would unbind everything and the UI wouldn't respond any more to new queries.
* If `fetchAutoCompleteItems` returns results on some background thread, results would be bound to UI elements from a background thread which could cause non-deterministic crashes.
* Results are bound to two UI elements, which means that for each user query, two HTTP requests would be made, one for each UI element, which is not the intended behavior.
A more appropriate version of the code would look like this:
@ -205,11 +205,11 @@ let results = query.rx_text
.throttle(0.3, scheduler: MainScheduler.instance)
.flatMapLatest { query in
fetchAutoCompleteItems(query)
.observeOn(MainScheduler.instance) // results are returned on MainScheduler
.catchErrorJustReturn([]) // in worst case, errors are handled
.observeOn(MainScheduler.instance) // results are returned on MainScheduler
.catchErrorJustReturn([]) // in the worst case, errors are handled
}
.shareReplay(1) // HTTP requests are shared and results replayed
// to all UI elements
.shareReplay(1) // HTTP requests are shared and results replayed
// to all UI elements
results
.map { "\($0.count)" }
@ -228,17 +228,17 @@ Making sure all of these requirements are properly handled in large systems can
The following code looks almost the same:
```swift
let results = query.rx_text.asDriver() // This converts normal sequence into `Driver` sequence.
let results = query.rx_text.asDriver() // This converts a normal sequence into a `Driver` sequence.
.throttle(0.3, scheduler: MainScheduler.instance)
.flatMapLatest { query in
fetchAutoCompleteItems(query)
.asDriver(onErrorJustReturn: []) // Builder just needs info what to return in case of error.
.asDriver(onErrorJustReturn: []) // Builder just needs info about what to return in case of error.
}
results
.map { "\($0.count)" }
.drive(resultCount.rx_text) // If there is `drive` method available instead of `bindTo`,
.addDisposableTo(disposeBag) // that means that compiler has proved all properties
.drive(resultCount.rx_text) // If there is a `drive` method available instead of `bindTo`,
.addDisposableTo(disposeBag) // that means that the compiler has proven that all properties
// are satisfied.
results
.drive(resultTableView.rx_itemsWithCellIdentifier("Cell")) { (_, result, cell) in
@ -249,37 +249,37 @@ results
So what is happening here?
This first `asDriver` method converts `ControlProperty` unit to `Driver` unit.
This first `asDriver` method converts the `ControlProperty` unit to a `Driver` unit.
```swift
query.rx_text.asDriver()
```
Notice that there wasn't anything special that needed to be done. `Driver` has all of the properties of the `ControlProperty` unit plus some more. The underlying observable sequence is just wrapped as `Driver` unit, and that's it.
Notice that there wasn't anything special that needed to be done. `Driver` has all of the properties of the `ControlProperty` unit, plus some more. The underlying observable sequence is just wrapped as a `Driver` unit, and that's it.
The second change is
The second change is:
```swift
.asDriver(onErrorJustReturn: [])
.asDriver(onErrorJustReturn: [])
```
Any observable sequence can be converted to `Driver` unit, it just needs to satisfy 3 properties:
* can't error out
* observe on main scheduler
* sharing side effects (`shareReplayLatestWhileConnected`)
Any observable sequence can be converted to `Driver` unit, as long as it satisfies 3 properties:
* Can't error out
* Observe on main scheduler
* Sharing side effects (`shareReplayLatestWhileConnected`)
So how to make sure those properties are satisfied? Just use normal Rx operators. `asDriver(onErrorJustReturn: [])` is equivalent to following code.
So how do you make sure those properties are satisfied? Just use normal Rx operators. `asDriver(onErrorJustReturn: [])` is equivalent to following code.
```
let safeSequence = xs
.observeOn(MainScheduler.instance) // observe events on main scheduler
.observeOn(MainScheduler.instance) // observe events on main scheduler
.catchErrorJustReturn(onErrorJustReturn) // can't error out
.shareReplayLatestWhileConnected // side effects sharing
return Driver(raw: safeSequence) // wrap it up
```
The final piece is `drive` instead of using `bindTo`.
The final piece is using `drive` instead of using `bindTo`.
`drive` is defined only on `Driver` unit. It means that if you see `drive` somewhere in code, observable sequence that can never error out and observes elements on main thread is being bound to UI element. Which is exactly what is wanted.
`drive` is defined only on the `Driver` unit. This means that if you see `drive` somewhere in code, that observable sequence can never error out and it observes on the main thread, which is safe for binding to a UI element.
Theoretically, somebody could define `drive` method to work on `ObservableType` or some other interface, so creating a temporary definition with `let results: Driver<[Results]> = ...` before binding to UI elements would be necessary for complete proof, but we'll leave it up for reader to decide whether that is a realistic scenario.
Note however that, theoretically, someone could still define a `drive` method to work on `ObservableType` or some other interface, so to be extra safe, creating a temporary definition with `let results: Driver<[Results]> = ...` before binding to UI elements would be necessary for complete proof. However, we'll leave it up to the reader to decide whether this is a realistic scenario or not.

View File

@ -5,7 +5,7 @@ Warnings
The following is valid for the `subscribe*`, `bind*` and `drive*` family of functions that return `Disposable`.
Warning is probably presented in a context similar to this one:
You will receive a warning for doing something such as this:
```Swift
let xs: Observable<E> ....
@ -18,10 +18,10 @@ xs
...
}, onError: {
...
})
})
```
The `subscribe` function returns a subscription `Disposable` that can be used to cancel computation and free resources.
The `subscribe` function returns a subscription `Disposable` that can be used to cancel computation and free resources. However, not using it (and thus not disposing it) will result in an error.
The preferred way of terminating these fluent calls is by using a `DisposeBag`, either through chaining a call to `.addDisposableTo(disposeBag)` or by adding the disposable directly to the bag.
@ -41,11 +41,11 @@ xs
.addDisposableTo(disposeBag) // <--- note `addDisposableTo`
```
When `disposeBag` gets deallocated, the disposables contained in it will be automatically disposed.
When `disposeBag` gets deallocated, the disposables contained within it will be automatically disposed as well.
In the case where `xs` terminates in a predictable way with either a `Completed` or `Error` message, not handling the subscription `Disposable` won't leak any resources. However, even in this case, using a dispose bag is still the preferred way to handle subscription disposables. It ensures that element computation is always terminated at a predictable moment, and makes your code robust and future proof because resources will be properly disposed even if the implementation of `xs` changes.
Another way to make sure subscriptions and resources are tied with the lifetime of some object is by using the `takeUntil` operator.
Another way to make sure subscriptions and resources are tied to the lifetime of some object is by using the `takeUntil` operator.
```Swift
let xs: Observable<E> ....
@ -63,7 +63,7 @@ _ = xs
})
```
If ignoring the subscription `Disposable` is desired behavior, this is how to silence the compiler warning.
If ignoring the subscription `Disposable` is the desired behavior, this is how to silence the compiler warning.
```Swift
let xs: Observable<E> ....
@ -81,7 +81,7 @@ _ = xs // <-- note the underscore
### <a name="unused-observable"></a>Unused observable sequence (unused-observable)
Warning is probably presented in a context similar to this one:
You will receive a warning for doing something such as this:
```Swift
let xs: Observable<E> ....
@ -105,7 +105,7 @@ let ys = xs // <--- names definition as `ys`
.map { ... }
```
... or start computation based on that definition
... or start computation based on that definition
```Swift
let xs: Observable<E> ....
@ -114,7 +114,7 @@ let disposeBag = DisposeBag()
xs
.filter { ... }
.map { ... }
.subscribeNext { nextElement in // <-- note the `subscribe*` method
.subscribeNext { nextElement in // <-- note the `subscribe*` method
// use the element
print(nextElement)
}

View File

@ -6,40 +6,40 @@
```swift
Observable.combineLatest(firstName.rx_text, lastName.rx_text) { $0 + " " + $1 }
.map { "Greeting \($0)" }
.bindTo(greetingLabel.rx_text)
.map { "Greetings, \($0)" }
.bindTo(greetingLabel.rx_text)
```
This also works with `UITableView`s and `UICollectionView`s.
```swift
viewModel
.rows
.bindTo(resultsTableView.rx_itemsWithCellIdentifier("WikipediaSearchCell", cellType: WikipediaSearchCell.self)) { (_, viewModel, cell) in
cell.title = viewModel.title
cell.url = viewModel.url
}
.addDisposableTo(disposeBag)
.rows
.bindTo(resultsTableView.rx_itemsWithCellIdentifier("WikipediaSearchCell", cellType: WikipediaSearchCell.self)) { (_, viewModel, cell) in
cell.title = viewModel.title
cell.url = viewModel.url
}
.addDisposableTo(disposeBag)
```
**Official suggestion is to always use `.addDisposableTo(disposeBag)` even though that's not necessary for simple bindings.**
### Retries
It would be great if APIs wouldn't fail, but unfortunately they do. Let's say there is an API method
It would be great if APIs wouldn't fail, but unfortunately they do. Let's say there is an API method:
```swift
func doSomethingIncredible(forWho: String) throws -> IncredibleThing
```
If you are using this function as it is, it's really hard to do retries in case it fails. Not to mention complexities modeling [exponential backoffs](https://en.wikipedia.org/wiki/Exponential_backoff). Sure it's possible, but code would probably contain a lot of transient states that you really don't care about, and it won't be reusable.
If you are using this function as it is, it's really hard to do retries in case it fails. Not to mention complexities modeling [exponential backoffs](https://en.wikipedia.org/wiki/Exponential_backoff). Sure it's possible, but the code would probably contain a lot of transient states that you really don't care about, and it wouldn't be reusable.
You would ideally want to capture the essence of retrying, and to be able to apply it to any operation.
Ideally, you would want to capture the essence of retrying, and to be able to apply it to any operation.
This is how you can do simple retries with Rx
```swift
doSomethingIncredible("me")
doSomethingIncredible("me")
.retry(3)
```
@ -47,11 +47,11 @@ You can also easily create custom retry operators.
### Delegates
Instead of doing the tedious and non-expressive
Instead of doing the tedious and non-expressive:
```swift
public func scrollViewDidScroll(scrollView: UIScrollView) { // what scroll view is this bound to?
self.leftPositionConstraint.constant = scrollView.contentOffset.x
public func scrollViewDidScroll(scrollView: UIScrollView) { [weak self] // what scroll view is this bound to?
self?.leftPositionConstraint.constant = scrollView.contentOffset.x
}
```
@ -59,14 +59,14 @@ public func scrollViewDidScroll(scrollView: UIScrollView) { // what scroll view
```swift
self.resultsTableView
.rx_contentOffset
.map { $0.x }
.bindTo(self.leftPositionConstraint.rx_constant)
.rx_contentOffset
.map { $0.x }
.bindTo(self.leftPositionConstraint.rx_constant)
```
### KVO
Instead of
Instead of:
```
`TickTock` was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object.
@ -104,37 +104,38 @@ someSuspiciousViewController
### Notifications
Instead of using
Instead of using:
```swift
@available(iOS 4.0, *)
public func addObserverForName(name: String?, object obj: AnyObject?, queue: NSOperationQueue?, usingBlock block: (NSNotification) -> Void) -> NSObjectProtocol
@available(iOS 4.0, *)
public func addObserverForName(name: String?, object obj: AnyObject?, queue: NSOperationQueue?, usingBlock block: (NSNotification) -> Void) -> NSObjectProtocol
```
... just write
```swift
NSNotificationCenter.defaultCenter()
.rx_notification(UITextViewTextDidBeginEditingNotification, object: myTextView)
.map { /*do something with data*/ }
....
NSNotificationCenter.defaultCenter()
.rx_notification(UITextViewTextDidBeginEditingNotification, object: myTextView)
.map { /*do something with data*/ }
....
```
### Transient state
There is also a lot of problems with transient state when writing async programs. Typical example is autocomplete search box.
There are also a lot of problems with transient state when writing async programs. A typical example is an autocomplete search box.
If you were to write the autocomplete code without Rx, first problem that probably needs to be solved is when `c` in `abc` is typed, and there is a pending request for `ab`, pending request gets cancelled. Ok, that shouldn't be too hard to solve, you just create additional variable to hold reference to pending request.
If you were to write the autocomplete code without Rx, the first problem that probably needs to be solved is when `c` in `abc` is typed, and there is a pending request for `ab`, the pending request gets cancelled. OK, that shouldn't be too hard to solve, you just create an additional variable to hold reference to the pending request.
The next problem is if the request fails, you need to do that messy retry logic. But ok, a couple of more fields that capture number of retries that need to be cleaned up.
The next problem is if the request fails, you need to do that messy retry logic. But OK, a couple more fields that capture the number of retries that need to be cleaned up.
It would be great if program would wait for some time before firing that request to server, after all, we don't want to spam our servers in case somebody is in the process of fast typing something very long. Additional timer field maybe?
It would be great if the program would wait for some time before firing a request to the server. After all, we don't want to spam our servers in case somebody is in the process of typing something very long. An additional timer field maybe?
There is also a question of what needs to be shown on screen while that search is executing, and also what needs to be shown in case we fail even with all of the retries.
Writing all of this and properly testing it would be tedious. This is that same logic written with Rx.
```swift
searchTextField.rx_text
searchTextField.rx_text
.throttle(0.3, scheduler: MainScheduler.instance)
.distinctUntilChanged()
.flatMapLatest { query in
@ -148,22 +149,22 @@ Writing all of this and properly testing it would be tedious. This is that same
}
```
There is no additional flags or fields required. Rx takes care of all that transient mess.
There are no additional flags or fields required. Rx takes care of all that transient mess.
### Compositional disposal
Lets assume that there is a scenario where you want to display blurred images in a table view. The images should be first fetched from URL, then decoded and then blurred.
Let's assume that there is a scenario where you want to display blurred images in a table view. First, the images should be fetched from a URL, then decoded and then blurred.
It would also be nice if that entire process could be cancelled if a cell exits the visible table view area because bandwidth and processor time for blurring are expensive.
It would also be nice if that entire process could be cancelled if a cell exits the visible table view area since bandwidth and processor time for blurring are expensive.
It would also be nice if we didn't just immediately start to fetch image once the cell enters visible area because if user swipes really fast there could be a lot of requests fired and cancelled.
It would also be nice if we didn't just immediately start to fetch an image once the cell enters the visible area since, if user swipes really fast, there could be a lot of requests fired and cancelled.
It would be also nice if we could limit the number of concurrent image operations because blurring images is an expensive operation.
It would be also nice if we could limit the number of concurrent image operations because, again, blurring images is an expensive operation.
This is how we can do it using Rx.
This is how we can do it using Rx:
```swift
// this is conceptual solution
// this is a conceptual solution
let imageSubscription = imageURLs
.throttle(0.2, scheduler: MainScheduler.instance)
.flatMapLatest { imageURL in
@ -180,39 +181,39 @@ let imageSubscription = imageURLs
.addDisposableTo(reuseDisposeBag)
```
This code will do all that, and when `imageSubscription` is disposed it will cancel all dependent async operations and make sure no rogue image is bound to UI.
This code will do all that and, when `imageSubscription` is disposed, it will cancel all dependent async operations and make sure no rogue image is bound to the UI.
### Aggregating network requests
What if you need to fire two requests, and aggregate results when they have both finished?
What if you need to fire two requests and aggregate results when they have both finished?
Well, there is of course `zip` operator
Well, there is of course the `zip` operator
```swift
let userRequest: Observable<User> = API.getUser("me")
let friendsRequest: Observable<Friends> = API.getFriends("me")
let userRequest: Observable<User> = API.getUser("me")
let friendsRequest: Observable<Friends> = API.getFriends("me")
Observable.zip(userRequest, friendsRequest) { user, friends in
return (user, friends)
}
.subscribeNext { user, friends in
// bind them to user interface
}
Observable.zip(userRequest, friendsRequest) { user, friends in
return (user, friends)
}
.subscribeNext { user, friends in
// bind them to the user interface
}
```
So what if those APIs return results on a background thread, and binding has to happen on main UI thread? There is `observeOn`.
So what if those APIs return results on a background thread, and binding has to happen on the main UI thread? There is `observeOn`.
```swift
let userRequest: Observable<User> = API.getUser("me")
let friendsRequest: Observable<[Friend]> = API.getFriends("me")
let userRequest: Observable<User> = API.getUser("me")
let friendsRequest: Observable<[Friend]> = API.getFriends("me")
Observable.zip(userRequest, friendsRequest) { user, friends in
return (user, friends)
}
.observeOn(MainScheduler.instance)
.subscribeNext { user, friends in
// bind them to user interface
}
Observable.zip(userRequest, friendsRequest) { user, friends in
return (user, friends)
}
.observeOn(MainScheduler.instance)
.subscribeNext { user, friends in
// bind them to the user interface
}
```
There are many more practical use cases where Rx really shines.
@ -221,19 +222,19 @@ There are many more practical use cases where Rx really shines.
Languages that allow mutation make it easy to access global state and mutate it. Uncontrolled mutations of shared global state can easily cause [combinatorial explosion] (https://en.wikipedia.org/wiki/Combinatorial_explosion#Computing).
But on the other hand, when used in smart way, imperative languages can enable writing more efficient code closer to hardware.
But on the other hand, when used in a smart way, imperative languages can enable writing more efficient code closer to hardware.
The usual way to battle combinatorial explosion is to keep state as simple as possible, and use [unidirectional data flows](https://developer.apple.com/videos/play/wwdc2014-229) to model derived data.
This is what Rx really shines at.
This is where Rx really shines.
Rx is that sweet spot between functional and imperative world. It enables you to use immutable definitions and pure functions to process snapshots of mutable state in a reliable composable way.
Rx is that sweet spot between functional and imperative worlds. It enables you to use immutable definitions and pure functions to process snapshots of mutable state in a reliable composable way.
So what are some of the practical examples?
So what are some practical examples?
### Easy integration
And what if you need to create your own observable? It's pretty easy. This code is taken from RxCocoa and that's all you need to wrap HTTP requests with `NSURLSession`
What if you need to create your own observable? It's pretty easy. This code is taken from RxCocoa and that's all you need to wrap HTTP requests with `NSURLSession`
```swift
extension NSURLSession {
@ -266,30 +267,30 @@ extension NSURLSession {
### Benefits
In short using Rx will make your code:
In short, using Rx will make your code:
* composable <- because Rx is composition's nick name
* reusable <- because it's composable
* declarative <- because definitions are immutable and only data changes
* understandable and concise <- raising level of abstraction and removing transient states
* stable <- because Rx code is thoroughly unit tested
* less stateful <- because you are modeling application as unidirectional data flows
* without leaks <- because resource management is easy
* Composable <- Because Rx is composition's nickname
* Reusable <- Because it's composable
* Declarative <- Because definitions are immutable and only data changes
* Understandable and concise <- Raising the level of abstraction and removing transient states
* Stable <- Because Rx code is thoroughly unit tested
* Less stateful <- Because you are modeling applications as unidirectional data flows
* Without leaks <- Because resource management is easy
### It's not all or nothing
It is usually a good idea to model as much of your application as possible using Rx.
But what if you don't know all of the operators and does there even exist some operator that models your particular case?
But what if you don't know all of the operators and whether or not there even exists some operator that models your particular case?
Well, all of the Rx operators are based on math and should be intuitive.
The good news is that about 10-15 operators cover most typical use cases. And that list already includes some of the familiar ones like `map`, `filter`, `zip`, `observeOn` ...
The good news is that about 10-15 operators cover most typical use cases. And that list already includes some of the familiar ones like `map`, `filter`, `zip`, `observeOn`, ...
There is a huge list of [all Rx operators](http://reactivex.io/documentation/operators.html) and list of all of the [currently supported RxSwift operators](API.md).
For each operator there is [marble diagram](http://reactivex.io/documentation/operators/retry.html) that helps to explain how does it work.
For each operator, there is a [marble diagram](http://reactivex.io/documentation/operators/retry.html) that helps to explain how it works.
But what if you need some operator that isn't on that list? Well, you can make your own operator.
What if creating that kind of operator is really hard for some reason, or you have some legacy stateful piece of code that you need to work with? Well, you've got yourself in a mess, but you can [jump out of Rx monad](GettingStarted.md#life-happens) easily, process the data, and return back into it.
What if creating that kind of operator is really hard for some reason, or you have some legacy stateful piece of code that you need to work with? Well, you've got yourself in a mess, but you can [jump out of Rx monads](GettingStarted.md#life-happens) easily, process the data, and return back into it.

View File

@ -39,7 +39,7 @@ KVO observing, async operations and streams are all unified under [abstraction o
###### ... hack around
* with example app. [Running Example App](Documentation/ExampleApp.md)
* with the example app. [Running Example App](Documentation/ExampleApp.md)
* with operators in playgrounds. [Playgrounds](Documentation/Playgrounds.md)
###### ... interact
@ -110,15 +110,15 @@ searchResults
Rx doesn't contain any external dependencies.
These are currently supported options:
These are currently the supported options:
### Manual
Open Rx.xcworkspace, choose `RxExample` and hit run. This method will build everything and run sample app
Open Rx.xcworkspace, choose `RxExample` and hit run. This method will build everything and run the sample app
### [CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html)
**:warning: IMPORTANT! For tvOS support CocoaPods `0.39` is required. :warning:**
**:warning: IMPORTANT! For tvOS support, CocoaPods `0.39` is required. :warning:**
```
# Podfile
@ -129,14 +129,14 @@ target 'YOUR_TARGET_NAME' do
pod 'RxCocoa', '~> 2.0'
end
# RxTests and RxBlocking have most sense in the context of unit/integration tests
# RxTests and RxBlocking make the most sense in the context of unit/integration tests
target 'YOUR_TESTING_TARGET' do
pod 'RxBlocking', '~> 2.0'
pod 'RxTests', '~> 2.0'
end
```
replace `YOUR_TARGET_NAME`, then type in the `Podfile` directory:
Replace `YOUR_TARGET_NAME` and then, in the `Podfile` directory, type:
```
$ pod install

View File

@ -1,290 +0,0 @@
/*:
> # IMPORTANT: To use `Rx.playground`, please:
1. Open `Rx.xcworkspace`
2. Build `RxSwift-OSX` scheme
3. And then open `Rx` playground in `Rx.xcworkspace` tree view.
4. Choose `View > Show Debug Area`
*/
//: [<< Previous](@previous) - [Index](Index)
import RxSwift
/*:
## Combination operators
Operators that work with multiple source Observables to create a single Observable.
*/
/*:
### `startWith`
emit a specified sequence of items before beginning to emit the items from the source Observable
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/startwith.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/startwith.html )
*/
example("startWith") {
let subscription = Observable.of(4, 5, 6, 7, 8, 9)
.startWith(3)
.startWith(2)
.startWith(1)
.startWith(0)
.subscribe {
print($0)
}
}
/*:
### `combineLatest`
when an item is emitted by either of two Observables, combine the latest item emitted by each Observable via a specified function and emit items based on the results of this function
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/combinelatest.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/combinelatest.html )
*/
example("combineLatest 1") {
let intOb1 = PublishSubject<String>()
let intOb2 = PublishSubject<Int>()
_ = Observable.combineLatest(intOb1, intOb2) {
"\($0) \($1)"
}
.subscribe {
print($0)
}
intOb1.on(.Next("A"))
intOb2.on(.Next(1))
intOb1.on(.Next("B"))
intOb2.on(.Next(2))
}
//: To produce output, at least one element has to be received from each sequence in arguements.
example("combineLatest 2") {
let intOb1 = Observable.just(2)
let intOb2 = Observable.of(0, 1, 2, 3, 4)
_ = Observable.combineLatest(intOb1, intOb2) {
$0 * $1
}
.subscribe {
print($0)
}
}
//: Combine latest has versions with more than 2 arguments.
example("combineLatest 3") {
let intOb1 = Observable.just(2)
let intOb2 = Observable.of(0, 1, 2, 3)
let intOb3 = Observable.of(0, 1, 2, 3, 4)
_ = Observable.combineLatest(intOb1, intOb2, intOb3) {
($0 + $1) * $2
}
.subscribe {
print($0)
}
}
//: Combinelatest version that allows combining sequences with different types.
example("combineLatest 4") {
let intOb = Observable.just(2)
let stringOb = Observable.just("a")
_ = Observable.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 = Observable.just(2)
let intOb2 = Observable.of(0, 1, 2, 3)
let intOb3 = Observable.of(0, 1, 2, 3, 4)
_ = [intOb1, intOb2, intOb3].combineLatest { intArray -> Int in
Int((intArray[0] + intArray[1]) * intArray[2])
}
.subscribe { (event: Event<Int>) -> Void in
print(event)
}
}
/*:
### `zip`
combine the emissions of multiple Observables together via a specified function and emit single items for each combination based on the results of this function
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/zip.png)
[More info in reactive.io website](http://reactivex.io/documentation/operators/zip.html)
*/
example("zip 1") {
let intOb1 = PublishSubject<String>()
let intOb2 = PublishSubject<Int>()
_ = Observable.zip(intOb1, intOb2) {
"\($0) \($1)"
}
.subscribe {
print($0)
}
intOb1.on(.Next("A"))
intOb2.on(.Next(1))
intOb1.on(.Next("B"))
intOb1.on(.Next("C"))
intOb2.on(.Next(2))
}
example("zip 2") {
let intOb1 = Observable.just(2)
let intOb2 = Observable.of(0, 1, 2, 3, 4)
_ = Observable.zip(intOb1, intOb2) {
$0 * $1
}
.subscribe {
print($0)
}
}
example("zip 3") {
let intOb1 = Observable.of(0, 1)
let intOb2 = Observable.of(0, 1, 2, 3)
let intOb3 = Observable.of(0, 1, 2, 3, 4)
_ = Observable.zip(intOb1, intOb2, intOb3) {
($0 + $1) * $2
}
.subscribe {
print($0)
}
}
/*:
### `merge`
combine multiple Observables into one by merging their emissions
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/merge.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/merge.html )
*/
example("merge 1") {
let subject1 = PublishSubject<Int>()
let subject2 = PublishSubject<Int>()
_ = Observable.of(subject1, subject2)
.merge()
.subscribeNext { int in
print(int)
}
subject1.on(.Next(20))
subject1.on(.Next(40))
subject1.on(.Next(60))
subject2.on(.Next(1))
subject1.on(.Next(80))
subject1.on(.Next(100))
subject2.on(.Next(1))
}
example("merge 2") {
let subject1 = PublishSubject<Int>()
let subject2 = PublishSubject<Int>()
_ = Observable.of(subject1, subject2)
.merge(maxConcurrent: 2)
.subscribe {
print($0)
}
subject1.on(.Next(20))
subject1.on(.Next(40))
subject1.on(.Next(60))
subject2.on(.Next(1))
subject1.on(.Next(80))
subject1.on(.Next(100))
subject2.on(.Next(1))
}
/*:
### `switchLatest`
convert an Observable that emits Observables into a single Observable that emits the items emitted by the most-recently-emitted of those Observables
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/switch.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/switch.html )
*/
example("switchLatest") {
let var1 = Variable(0)
let var2 = Variable(200)
// var3 is like an Observable<Observable<Int>>
let var3 = Variable(var1.asObservable())
let d = var3
.asObservable()
.switchLatest()
.subscribe {
print($0)
}
var1.value = 1
var1.value = 2
var1.value = 3
var1.value = 4
var3.value = var2.asObservable()
var2.value = 201
var1.value = 5
var1.value = 6
var1.value = 7
}
//: [Index](Index) - [Next >>](@next)

View File

@ -0,0 +1,160 @@
/*:
> # IMPORTANT: To use **Rx.playground**:
1. Open **Rx.xcworkspace**.
1. Build the **RxSwift-OSX** scheme (**Product** **Build**).
1. Open **Rx** playground in the **Project navigator**.
1. Show the Debug Area (**View** **Debug Area** **Show Debug Area**).
----
[Previous](@previous) - [Table of Contents](Table_of_Contents)
*/
import RxSwift
/*:
# Combination Operators
Operators that combine multiple source `Observable`s into a single `Observable`.
## `startWith`
Emits the specified sequence of elements before beginning to emit the elements from the source `Observable`. [More info](http://reactivex.io/documentation/operators/startwith.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/startwith.png)
*/
example("startWith") {
let disposeBag = DisposeBag()
Observable.of("🐶", "🐱", "🐭", "🐹")
.startWith("1")
.startWith("2")
.startWith("3", "🅰️", "🅱️")
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
/*:
> As this example demonstrates, `startWith` can be chained on a last-in-first-out basis, i.e., each successive `startWith`'s elements will be prepended before the prior `startWith`'s elements.
----
## `merge`
Combines elements from source `Observable` sequences into a single new `Observable` sequence, and will emit each element as it is emitted by each source `Observable` sequence. [More info](http://reactivex.io/documentation/operators/merge.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/merge.png)
*/
example("merge") {
let disposeBag = DisposeBag()
let subject1 = PublishSubject<String>()
let subject2 = PublishSubject<String>()
Observable.of(subject1, subject2)
.merge()
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
subject1.onNext("🅰️")
subject1.onNext("🅱️")
subject2.onNext("")
subject2.onNext("")
subject1.onNext("🆎")
subject2.onNext("")
}
/*:
----
## `zip`
Combines up to 8 source `Observable` sequences into a single new `Observable` sequence, and will emit from the combined `Observable` sequence the elements from each of the source `Observable` sequences at the corresponding index. [More info](http://reactivex.io/documentation/operators/zip.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/zip.png)
*/
example("zip") {
let disposeBag = DisposeBag()
let stringSubject = PublishSubject<String>()
let intSubject = PublishSubject<Int>()
Observable.zip(stringSubject, intSubject) { stringElement, intElement in
"\(stringElement) \(intElement)"
}
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
stringSubject.onNext("🅰️")
stringSubject.onNext("🅱️")
intSubject.onNext(1)
intSubject.onNext(2)
stringSubject.onNext("🆎")
intSubject.onNext(3)
}
/*:
----
## `combineLatest`
Combines up to 8 source `Observable` sequences into a single new `Observable` sequence, and will begin emitting from the combined `Observable` sequence the latest elements of each source `Observable` sequence once all source sequences have emitted at least one element, and also when any of the source `Observable` sequences emits a new element. [More info](http://reactivex.io/documentation/operators/combinelatest.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/combinelatest.png)
*/
example("combineLatest") {
let disposeBag = DisposeBag()
let stringSubject = PublishSubject<String>()
let intSubject = PublishSubject<Int>()
Observable.combineLatest(stringSubject, intSubject) { stringElement, intElement in
"\(stringElement) \(intElement)"
}
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
stringSubject.onNext("🅰️")
stringSubject.onNext("🅱️")
intSubject.onNext(1)
intSubject.onNext(2)
stringSubject.onNext("🆎")
}
//: There is also a `combineLatest` extension on `Array`:
example("Array.combineLatest") {
let disposeBag = DisposeBag()
let stringObservable = Observable.just("❤️")
let fruitObservable = ["🍎", "🍐", "🍊"].toObservable()
let animalObservable = Observable.of("🐶", "🐱", "🐭", "🐹")
[stringObservable, fruitObservable, animalObservable].combineLatest {
"\($0[0]) \($0[1]) \($0[2])"
}
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
/*:
> The `combineLatest` extension on `Array` requires that all source `Observable` sequences are of the same type.
----
## `switchLatest`
Transforms the elements emitted by an `Observable` sequence into `Observable` sequences, and emits elements from the most recent inner `Observable` sequence. [More info](http://reactivex.io/documentation/operators/switch.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/switch.png)
*/
example("switchLatest") {
let disposeBag = DisposeBag()
let subject1 = BehaviorSubject(value: "⚽️")
let subject2 = BehaviorSubject(value: "🍎")
let variable = Variable(subject1)
variable.asObservable()
.switchLatest()
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
subject1.onNext("🏈")
subject1.onNext("🏀")
variable.value = subject2
subject1.onNext("⚾️")
subject2.onNext("🍐")
}
/*:
> In this example, adding onto `subject1` after setting `variable.value` to `subject2` has no effect, because only the most recent inner `Observable` sequence (`subject2`) will emit elements.
*/
//: [Next](@next) - [Table of Contents](Table_of_Contents)

View File

@ -1,82 +0,0 @@
/*:
> # IMPORTANT: To use `Rx.playground`, please:
1. Open `Rx.xcworkspace`
2. Build `RxSwift-OSX` scheme
3. And then open `Rx` playground in `Rx.xcworkspace` tree view.
4. Choose `View > Show Debug Area`
*/
//: [<< Previous](@previous) - [Index](Index)
import Cocoa
import RxSwift
/*:
## Conditional and Boolean Operators
Operators that evaluate one or more Observables or items emitted by Observables.
*/
/*:
### `takeUntil`
Discard any items emitted by an Observable after a second Observable emits an item or terminates.
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/takeuntil.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/takeuntil.html )
*/
example("takeUntil") {
let originalSequence = PublishSubject<Int>()
let whenThisSendsNextWorldStops = PublishSubject<Int>()
_ = originalSequence
.takeUntil(whenThisSendsNextWorldStops)
.subscribe {
print($0)
}
originalSequence.on(.Next(1))
originalSequence.on(.Next(2))
originalSequence.on(.Next(3))
originalSequence.on(.Next(4))
whenThisSendsNextWorldStops.on(.Next(1))
originalSequence.on(.Next(5))
}
/*:
### `takeWhile`
Mirror items emitted by an Observable until a specified condition becomes false
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/takewhile.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/takewhile.html )
*/
example("takeWhile") {
let sequence = PublishSubject<Int>()
_ = sequence
.takeWhile { int in
int < 4
}
.subscribe {
print($0)
}
sequence.on(.Next(1))
sequence.on(.Next(2))
sequence.on(.Next(3))
sequence.on(.Next(4))
sequence.on(.Next(5))
}
//: [Index](Index) - [Next >>](@next)

View File

@ -1,221 +0,0 @@
/*:
> # IMPORTANT: To use `Rx.playground`, please:
1. Open `Rx.xcworkspace`
2. Build `RxSwift-OSX` scheme
3. And then open `Rx` playground in `Rx.xcworkspace` tree view.
4. Choose `View > Show Debug Area`
*/
//: [<< Previous](@previous) - [Index](Index)
import RxSwift
/*:
## Below every example there is a commented method call that runs that example. To run the example just uncomment that part.
E.g. `//sampleWithoutConnectableOperators()`
*/
/*:
## Connectable Observable Operators
A Connectable Observable resembles an ordinary Observable, except that it does not begin emitting items when it is subscribed to, but only when its connect() method is called. In this way you can wait for all intended Subscribers to subscribe to the Observable before the Observable begins emitting items.
Specialty Observables that have more precisely-controlled subscription dynamics.
*/
func sampleWithoutConnectableOperators() {
let int1 = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
_ = int1
.subscribe {
print("first subscription \($0)")
}
delay(5) {
_ = int1
.subscribe {
print("second subscription \($0)")
}
}
}
//sampleWithoutConnectableOperators()
/*:
### `multicast`
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/publishconnect.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/publish.html )
*/
func sampleWithMulticast() {
let subject1 = PublishSubject<Int64>()
_ = subject1
.subscribe {
print("Subject \($0)")
}
let int1 = Observable<Int64>.interval(1, scheduler: MainScheduler.instance)
.multicast(subject1)
_ = int1
.subscribe {
print("first subscription \($0)")
}
delay(2) {
int1.connect()
}
delay(4) {
_ = int1
.subscribe {
print("second subscription \($0)")
}
}
delay(6) {
_ = int1
.subscribe {
print("third subscription \($0)")
}
}
}
// sampleWithMulticast()
/*:
### `replay`
Ensure that all observers see the same sequence of emitted items, even if they subscribe after the Observable has begun emitting items.
publish = multicast + replay subject
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/replay.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/replay.html )
*/
func sampleWithReplayBuffer0() {
let int1 = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
.replay(0)
_ = int1
.subscribe {
print("first subscription \($0)")
}
delay(2) {
int1.connect()
}
delay(4) {
_ = int1
.subscribe {
print("second subscription \($0)")
}
}
delay(6) {
_ = int1
.subscribe {
print("third subscription \($0)")
}
}
}
// sampleWithReplayBuffer0()
func sampleWithReplayBuffer2() {
print("--- sampleWithReplayBuffer2 ---\n")
let int1 = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
.replay(2)
_ = int1
.subscribe {
print("first subscription \($0)")
}
delay(2) {
int1.connect()
}
delay(4) {
_ = int1
.subscribe {
print("second subscription \($0)")
}
}
delay(6) {
_ = int1
.subscribe {
print("third subscription \($0)")
}
}
}
// sampleWithReplayBuffer2()
/*:
### `publish`
Convert an ordinary Observable into a connectable Observable.
publish = multicast + publish subject
so publish is basically replay(0)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/publish.html )
*/
func sampleWithPublish() {
let int1 = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
.publish()
_ = int1
.subscribe {
print("first subscription \($0)")
}
delay(2) {
int1.connect()
}
delay(4) {
_ = int1
.subscribe {
print("second subscription \($0)")
}
}
delay(6) {
_ = int1
.subscribe {
print("third subscription \($0)")
}
}
}
// sampleWithPublish()
playgroundShouldContinueIndefinitely()
//: [Index](Index)

View File

@ -0,0 +1,133 @@
/*:
> # IMPORTANT: To use **Rx.playground**:
1. Open **Rx.xcworkspace**.
1. Build the **RxSwift-OSX** scheme (**Product** **Build**).
1. Open **Rx** playground in the **Project navigator**.
1. Show the Debug Area (**View** **Debug Area** **Show Debug Area**).
----
[Previous](@previous) - [Table of Contents](Table_of_Contents)
*/
import RxSwift
playgroundShouldContinueIndefinitely()
/*:
## Connectable Operators
Connectable `Observable` sequences resembles ordinary `Observable` sequences, except that they not begin emitting elements when subscribed to, but instead, only when their `connect()` method is called. In this way, you can wait for all intended subscribers to subscribe to a connectable `Observable` sequence before it begins emitting elements.
> Within each example on this page is a commented-out method. Uncomment that method to run the example, and then comment it out again to stop running the example.
#
Before learning about connectable operators, let's take a look at an example of a non-connectable operator:
*/
func sampleWithoutConnectableOperators() {
printExampleHeader(#function)
let interval = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
_ = interval
.subscribeNext { print("Subscription: 1, Event: \($0)") }
delay(5) {
_ = interval
.subscribeNext { print("Subscription: 2, Event: \($0)") }
}
}
// sampleWithoutConnectableOperators() // Uncomment to run this example; comment to stop running
/*:
> `interval` creates an `Observable` sequence that emits elements after each `period`, on the specified scheduler. [More info](http://reactivex.io/documentation/operators/interval.html)
![](http://reactivex.io/documentation/operators/images/interval.c.png)
----
## `publish`
Converts the source `Observable` sequence into a connectable sequence. [More info](http://reactivex.io/documentation/operators/publish.html)
![](http://reactivex.io/documentation/operators/images/publishConnect.c.png)
*/
func sampleWithPublish() {
printExampleHeader(#function)
let intSequence = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
.publish()
_ = intSequence
.subscribeNext { print("Subscription 1:, Event: \($0)") }
delay(2) { intSequence.connect() }
delay(4) {
_ = intSequence
.subscribeNext { print("Subscription 2:, Event: \($0)") }
}
delay(6) {
_ = intSequence
.subscribeNext { print("Subscription 3:, Event: \($0)") }
}
}
//sampleWithPublish() // Uncomment to run this example; comment to stop running
//: > Schedulers are an abstraction of mechanisms for performing work, such as on specific threads or dispatch queues. [More info](https://github.com/ReactiveX/RxSwift/blob/master/Documentation/Schedulers.md)
/*:
----
## `replay`
Converts the source `Observable` sequence into a connectable sequence, and will replay `bufferSize` number of previous emissions to each new subscriber. [More info](http://reactivex.io/documentation/operators/replay.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/replay.png)
*/
func sampleWithReplayBuffer() {
printExampleHeader(#function)
let intSequence = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
.replay(5)
_ = intSequence
.subscribeNext { print("Subscription 1:, Event: \($0)") }
delay(2) { intSequence.connect() }
delay(4) {
_ = intSequence
.subscribeNext { print("Subscription 2:, Event: \($0)") }
}
delay(8) {
_ = intSequence
.subscribeNext { print("Subscription 3:, Event: \($0)") }
}
}
// sampleWithReplayBuffer() // Uncomment to run this example; comment to stop running
/*:
----
## `multicast`
Converts the source `Observable` sequence into a connectable sequence, and broadcasts its emissions via the specified `subject`.
*/
func sampleWithMulticast() {
printExampleHeader(#function)
let subject = PublishSubject<Int>()
_ = subject
.subscribeNext { print("Subject: \($0)") }
let intSequence = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
.multicast(subject)
_ = intSequence
.subscribeNext { print("\tSubscription 1:, Event: \($0)") }
delay(2) { intSequence.connect() }
delay(4) {
_ = intSequence
.subscribeNext { print("\tSubscription 2:, Event: \($0)") }
}
delay(6) {
_ = intSequence
.subscribeNext { print("\tSubscription 3:, Event: \($0)") }
}
}
//sampleWithMulticast() // Uncomment to run this example; comment to stop running
//: [Next](@next) - [Table of Contents](Table_of_Contents)

View File

@ -0,0 +1,212 @@
/*:
> # IMPORTANT: To use **Rx.playground**:
1. Open **Rx.xcworkspace**.
1. Build the **RxSwift-OSX** scheme (**Product** **Build**).
1. Open **Rx** playground in the **Project navigator**.
1. Show the Debug Area (**View** **Debug Area** **Show Debug Area**).
----
[Previous](@previous) - [Table of Contents](Table_of_Contents)
*/
import RxSwift
/*:
# Creating and Subscribing to `Observable`s
There are several ways to create and subscribe to `Observable` sequences.
## never
Creates a sequence that never terminates and never emits any events. [More info](http://reactivex.io/documentation/operators/empty-never-throw.html)
*/
example("never") {
let disposeBag = DisposeBag()
let neverSequence = Observable<String>.never()
let neverSequenceSubscription = neverSequence
.subscribe { _ in
print("This will never be printed")
}
neverSequenceSubscription.addDisposableTo(disposeBag)
}
/*:
----
## empty
Creates an empty `Observable` sequence that only emits a Completed event. [More info](http://reactivex.io/documentation/operators/empty-never-throw.html)
*/
example("empty") {
let disposeBag = DisposeBag()
Observable<Int>.empty()
.subscribe { event in
print(event)
}
.addDisposableTo(disposeBag)
}
/*:
> This example also introduces chaining together creating and subscribing to an `Observable` sequence.
----
## just
Creates an `Observable` sequence with a single element. [More info](http://reactivex.io/documentation/operators/just.html)
*/
example("just") {
let disposeBag = DisposeBag()
Observable.just("🔴")
.subscribe { event in
print(event)
}
.addDisposableTo(disposeBag)
}
/*:
----
## of
Creates an `Observable` sequence with a fixed number of elements.
*/
example("of") {
let disposeBag = DisposeBag()
Observable.of("🐶", "🐱", "🐭", "🐹")
.subscribeNext { element in
print(element)
}
.addDisposableTo(disposeBag)
}
/*:
> This example also introduces using the `subscribeNext(_:)` convenience method. Unlike `subscribe(_:)`, which subscribes an _event_ handler for all event types (Next, Error, and Completed), `subscribeNext(_:)` subscribes an _element_ handler that will ignore Error and Completed events and only produce Next event elements. There are also `subscribeError(_:)` and `subscribeCompleted(_:)` convenience methods, should you only want to subscribe to those event types. And there is a `subscribe(onNext:onError:onCompleted:onDisposed:)` method, which allows you to react to one or more event types and when the subscription is terminated for any reason, or disposed, in a single call:
```
someObservable.subscribe(
onNext: { print("Element:", $0) },
onError: { print("Error:", $0) },
onCompleted: { print("Completed") },
onDisposed: { print("Disposed") }
)
```
----
## toObservable
Creates an `Observable` sequence from a `SequenceType`, such as an `Array`, `Dictionary`, or `Set`.
*/
example("toObservable") {
let disposeBag = DisposeBag()
["🐶", "🐱", "🐭", "🐹"].toObservable()
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
/*:
> This example also demonstrates using the default argument name `$0` instead of explicitly naming the argument.
----
## create
Creates a custom `Observable` sequence. [More info](http://reactivex.io/documentation/operators/create.html)
*/
example("create") {
let disposeBag = DisposeBag()
let myJust = { (element: String) -> Observable<String> in
return Observable.create { observer in
observer.on(.Next(element))
observer.on(.Completed)
return NopDisposable.instance
}
}
myJust("🔴")
.subscribe { print($0) }
.addDisposableTo(disposeBag)
}
/*:
----
## range
Creates an `Observable` sequence that emits a range of sequential integers and then terminates. [More info](http://reactivex.io/documentation/operators/range.html)
*/
example("range") {
let disposeBag = DisposeBag()
Observable.range(start: 1, count: 10)
.subscribe { print($0) }
.addDisposableTo(disposeBag)
}
/*:
----
## repeatElement
Creates an `Observable` sequence that emits the given element indefinitely. [More info](http://reactivex.io/documentation/operators/repeat.html)
*/
example("repeatElement") {
let disposeBag = DisposeBag()
Observable.repeatElement("🔴")
.take(3)
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
/*:
> This example also introduces using the `take` operator to return a specified number of elements from the start of a sequence.
----
## generate
Creates an `Observable` sequence that generates values for as long as the provided condition evaluates to `true`.
*/
example("generate") {
let disposeBag = DisposeBag()
Observable.generate(
initialState: 0,
condition: { $0 < 3 },
iterate: { $0 + 1 }
)
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
/*:
----
## deferred
Creates a new `Observable` sequence for each subscriber. [More info](http://reactivex.io/documentation/operators/defer.html)
*/
example("deferred") {
let disposeBag = DisposeBag()
var count = 1
let deferredSequence = Observable<String>.deferred {
print("Creating \(count)")
count += 1
return Observable.create { observer in
print("Emitting...")
observer.onNext("🐶")
observer.onNext("🐱")
observer.onNext("🐵")
return NopDisposable.instance
}
}
deferredSequence
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
deferredSequence
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
/*:
----
## error
Creates an `Observable` sequence that emits no items and immediately terminates with an error.
*/
example("error") {
let disposeBag = DisposeBag()
Observable<Int>.error(Error.Test)
.subscribe { print($0) }
.addDisposableTo(disposeBag)
}
/*:
----
## doOn
Invokes a side-effect action for each emitted event and returns (passes through) the original event. [More info](http://reactivex.io/documentation/operators/do.html)
*/
example("doOn") {
let disposeBag = DisposeBag()
Observable.of("🍎", "🍐", "🍊", "🍋")
.doOn { print("Intercepted:", $0) }
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
//: > There are also `doOnNext(_:)`, `doOnError(_:)`, and `doOnCompleted(_:)` convenience methods to intercept those specific events, and `doOn(onNext:onError:onCompleted:)` to intercept one or more events in a single call.
//: [Next](@next) - [Table of Contents](Table_of_Contents)

View File

@ -0,0 +1,83 @@
/*:
> # IMPORTANT: To use **Rx.playground**:
1. Open **Rx.xcworkspace**.
1. Build the **RxSwift-OSX** scheme (**Product** **Build**).
1. Open **Rx** playground in the **Project navigator**.
1. Show the Debug Area (**View** **Debug Area** **Show Debug Area**).
----
[Previous](@previous) - [Table of Contents](Table_of_Contents)
*/
import RxSwift
/*:
# Debugging Operators
Operators to help debug Rx code.
## `debug`
Prints out all subscriptions, events, and disposals.
*/
example("debug") {
let disposeBag = DisposeBag()
var count = 1
let sequenceThatErrors = Observable<String>.create { observer in
observer.onNext("🍎")
observer.onNext("🍐")
observer.onNext("🍊")
if count < 5 {
observer.onError(Error.Test)
print("Error encountered")
count += 1
}
observer.onNext("🐶")
observer.onNext("🐱")
observer.onNext("🐭")
observer.onCompleted()
return NopDisposable.instance
}
sequenceThatErrors
.retry(3)
.debug()
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
/*:
----
## `RxSwift.resourceCount`
Provides a count of all Rx resource allocations, which is useful for detecting leaks during development.
*/
#if NOT_IN_PLAYGROUND
#else
example("RxSwift.resourceCount") {
print(RxSwift.resourceCount)
let disposeBag = DisposeBag()
print(RxSwift.resourceCount)
let variable = Variable("🍎")
let subscription1 = variable.asObservable().subscribeNext { print($0) }
print(RxSwift.resourceCount)
let subscription2 = variable.asObservable().subscribeNext { print($0) }
print(RxSwift.resourceCount)
subscription1.dispose()
print(RxSwift.resourceCount)
subscription2.dispose()
print(RxSwift.resourceCount)
}
print(RxSwift.resourceCount)
#endif
//: > `RxSwift.resourceCount` is not enabled by default, and should generally not be enabled in Release builds. [Click here](Enable_RxSwift.resourceCount) for instructions on how to enable it.
//: [Table of Contents](Table_of_Contents)

View File

@ -0,0 +1,30 @@
//: [Back](@previous)
/*:
Follow these instructions to enable `RxSwift.resourceCount` in your project:
#
**CocoaPods**
1. Add a `post_install` hook to your Podfile, e.g.:
```
target 'AppTarget' do
pod 'RxSwift'
end
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name == 'RxSwift'
target.build_configurations.each do |config|
if config.name == 'Debug'
config.build_settings['OTHER_SWIFT_FLAGS'] ||= ['-D', 'TRACE_RESOURCES']
end
end
end
end
end
```
2. Run `pod update`.
3. Build project (**Product** **Build**).
#
**Carthage**
1. Run `carthage build --configuration Debug`.
2. Build project (**Product** **Build**).
*/

View File

@ -1,103 +1,131 @@
/*:
> # IMPORTANT: To use `Rx.playground`, please:
1. Open `Rx.xcworkspace`
2. Build `RxSwift-OSX` scheme
3. And then open `Rx` playground in `Rx.xcworkspace` tree view.
4. Choose `View > Show Debug Area`
*/
//: [<< Previous](@previous) - [Index](Index)
> # IMPORTANT: To use **Rx.playground**:
1. Open **Rx.xcworkspace**.
1. Build the **RxSwift-OSX** scheme (**Product** **Build**).
1. Open **Rx** playground in the **Project navigator**.
1. Show the Debug Area (**View** **Debug Area** **Show Debug Area**).
----
[Previous](@previous) - [Table of Contents](Table_of_Contents)
*/
import RxSwift
import Foundation
/*:
## Error Handling Operators
# Error Handling Operators
Operators that help to recover from error notifications from an Observable.
*/
/*:
### `catchError`
Recover from an `Error` notification by continuing the sequence without error
## `catchErrorJustReturn`
Recovers from an Error event by returning an `Observable` sequence that emits a single element and then terminates. [More info](http://reactivex.io/documentation/operators/catch.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/catch.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/catch.html )
*/
example("catchError 1") {
let sequenceThatFails = PublishSubject<Int>()
let recoverySequence = Observable.of(100, 200, 300, 400)
_ = sequenceThatFails
.catchError { error in
example("catchErrorJustReturn") {
let disposeBag = DisposeBag()
let sequenceThatFails = PublishSubject<String>()
sequenceThatFails
.catchErrorJustReturn("😊")
.subscribe { print($0) }
.addDisposableTo(disposeBag)
sequenceThatFails.onNext("😬")
sequenceThatFails.onNext("😨")
sequenceThatFails.onNext("😡")
sequenceThatFails.onNext("🔴")
sequenceThatFails.onError(Error.Test)
}
/*:
----
## `catchError`
Recovers from an Error event by switching to the provided recovery `Observable` sequence. [More info](http://reactivex.io/documentation/operators/catch.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/catch.png)
*/
example("catchError") {
let disposeBag = DisposeBag()
let sequenceThatErrors = PublishSubject<String>()
let recoverySequence = PublishSubject<String>()
sequenceThatErrors
.catchError {
print("Error:", $0)
return recoverySequence
}
.subscribe {
print($0)
}
sequenceThatFails.on(.Next(1))
sequenceThatFails.on(.Next(2))
sequenceThatFails.on(.Next(3))
sequenceThatFails.on(.Next(4))
sequenceThatFails.on(.Error(NSError(domain: "Test", code: 0, userInfo: nil)))
.subscribe { print($0) }
.addDisposableTo(disposeBag)
sequenceThatErrors.onNext("😬")
sequenceThatErrors.onNext("😨")
sequenceThatErrors.onNext("😡")
sequenceThatErrors.onNext("🔴")
sequenceThatErrors.onError(Error.Test)
recoverySequence.onNext("😊")
}
example("catchError 2") {
let sequenceThatFails = PublishSubject<Int>()
_ = sequenceThatFails
.catchErrorJustReturn(100)
.subscribe {
print($0)
}
sequenceThatFails.on(.Next(1))
sequenceThatFails.on(.Next(2))
sequenceThatFails.on(.Next(3))
sequenceThatFails.on(.Next(4))
sequenceThatFails.on(.Error(NSError(domain: "Test", code: 0, userInfo: nil)))
}
/*:
### `retry`
If a source Observable emits an error, resubscribe to it in the hopes that it will complete without error
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/retry.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/retry.html )
*/
----
## `retry`
Recovers repeatedly Error events by rescribing to the `Observable` sequence, indefinitely. [More info](http://reactivex.io/documentation/operators/retry.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/retry.png)
*/
example("retry") {
var count = 1 // bad practice, only for example purposes
let funnyLookingSequence = Observable<Int>.create { observer in
let error = NSError(domain: "Test", code: 0, userInfo: nil)
observer.on(.Next(0))
observer.on(.Next(1))
observer.on(.Next(2))
if count < 2 {
observer.on(.Error(error))
let disposeBag = DisposeBag()
var count = 1
let sequenceThatErrors = Observable<String>.create { observer in
observer.onNext("🍎")
observer.onNext("🍐")
observer.onNext("🍊")
if count == 1 {
observer.onError(Error.Test)
print("Error encountered")
count += 1
}
observer.on(.Next(3))
observer.on(.Next(4))
observer.on(.Next(5))
observer.on(.Completed)
observer.onNext("🐶")
observer.onNext("🐱")
observer.onNext("🐭")
observer.onCompleted()
return NopDisposable.instance
}
_ = funnyLookingSequence
sequenceThatErrors
.retry()
.subscribe {
print($0)
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
/*:
----
## `retry(_:)`
Recovers repeatedly from Error events by resubscribing to the `Observable` sequence, up to `maxAttemptCount` number of retries. [More info](http://reactivex.io/documentation/operators/retry.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/retry.png)
*/
example("retry maxAttemptCount") {
let disposeBag = DisposeBag()
var count = 1
let sequenceThatErrors = Observable<String>.create { observer in
observer.onNext("🍎")
observer.onNext("🍐")
observer.onNext("🍊")
if count < 5 {
observer.onError(Error.Test)
print("Error encountered")
count += 1
}
observer.onNext("🐶")
observer.onNext("🐱")
observer.onNext("🐭")
observer.onCompleted()
return NopDisposable.instance
}
sequenceThatErrors
.retry(3)
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
//: [Index](Index) - [Next >>](@next)
//: [Next](@next) - [Table of Contents](Table_of_Contents)

View File

@ -1,78 +0,0 @@
/*:
> # IMPORTANT: To use `Rx.playground`, please:
1. Open `Rx.xcworkspace`
2. Build `RxSwift-OSX` scheme
3. And then open `Rx` playground in `Rx.xcworkspace` tree view.
4. Choose `View > Show Debug Area`
*/
//: [<< Previous](@previous) - [Index](Index)
import RxSwift
/*:
## Filtering Observables
Operators that selectively emit items from a source Observable.
*/
/*:
### `filter`
Emit only those items from an Observable that pass a predicate test
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/filter.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/filter.html )
*/
example("filter") {
let subscription = Observable.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
.filter {
$0 % 2 == 0
}
.subscribe {
print($0)
}
}
/*:
### `distinctUntilChanged`
Suppress duplicate items emitted by an Observable
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/distinct.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/distinct.html )
*/
example("distinctUntilChanged") {
let subscription = Observable.of(1, 2, 3, 1, 1, 4)
.distinctUntilChanged()
.subscribe {
print($0)
}
}
/*:
### `take`
Emit only the first n items emitted by an Observable
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/take.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/take.html )
*/
example("take") {
let subscription = Observable.of(1, 2, 3, 4, 5, 6)
.take(3)
.subscribe {
print($0)
}
}
//: [Index](Index) - [Next >>](@next)

View File

@ -0,0 +1,221 @@
/*:
> # IMPORTANT: To use **Rx.playground**:
1. Open **Rx.xcworkspace**.
1. Build the **RxSwift-OSX** scheme (**Product** **Build**).
1. Open **Rx** playground in the **Project navigator**.
1. Show the Debug Area (**View** **Debug Area** **Show Debug Area**).
----
[Previous](@previous) - [Table of Contents](Table_of_Contents)
*/
import RxSwift
/*:
# Filtering and Conditional Operators
Operators that selectively emit elements from a source `Observable` sequence.
## `filter`
Emits only those elements from an `Observable` sequence that meet the specified condition. [More info](http://reactivex.io/documentation/operators/filter.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/filter.png)
*/
example("filter") {
let disposeBag = DisposeBag()
Observable.of(
"🐱", "🐰", "🐶",
"🐸", "🐱", "🐰",
"🐹", "🐸", "🐱")
.filter {
$0 == "🐱"
}
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
/*:
----
## `distinctUntilChanged`
Suppresses sequential duplicate elements emitted by an `Observable` sequence. [More info](http://reactivex.io/documentation/operators/distinct.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/distinct.png)
*/
example("distinctUntilChanged") {
let disposeBag = DisposeBag()
Observable.of("🐱", "🐷", "🐱", "🐱", "🐱", "🐵", "🐱")
.distinctUntilChanged()
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
/*:
----
## `elementAt`
Emits only the element at the specified index of all elements emitted by an `Observable` sequence. [More info](http://reactivex.io/documentation/operators/elementat.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/elementat.png)
*/
example("elementAt") {
let disposeBag = DisposeBag()
Observable.of("🐱", "🐰", "🐶", "🐸", "🐷", "🐵")
.elementAt(3)
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
/*:
----
## `single`
Emits only the first element (or the first element that meets a condition) emitted by an `Observable` sequence. Will throw an error if the `Observable` sequence does not emit exactly one element.
*/
example("single") {
let disposeBag = DisposeBag()
Observable.of("🐱", "🐰", "🐶", "🐸", "🐷", "🐵")
.single()
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
example("single with conditions") {
let disposeBag = DisposeBag()
Observable.of("🐱", "🐰", "🐶", "🐸", "🐷", "🐵")
.single { $0 == "🔵" }
.subscribe { print($0) }
.addDisposableTo(disposeBag)
}
/*:
----
## `take`
Emits only the specified number of elements from the beginning of an `Observable` sequence. [More info](http://reactivex.io/documentation/operators/take.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/take.png)
*/
example("take") {
let disposeBag = DisposeBag()
Observable.of("🐱", "🐰", "🐶", "🐸", "🐷", "🐵")
.take(3)
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
/*:
----
## `takeLast`
Emits only the specified number of elements from the end of an `Observable` sequence. [More info](http://reactivex.io/documentation/operators/takelast.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/takelast.png)
*/
example("takeLast") {
let disposeBag = DisposeBag()
Observable.of("🐱", "🐰", "🐶", "🐸", "🐷", "🐵")
.takeLast(3)
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
/*:
----
## `takeWhile`
Emits elements from the beginning of an `Observable` sequence as long as the specified condition evaluates to `true`. [More info](http://reactivex.io/documentation/operators/takewhile.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/takewhile.png)
*/
example("takeWhile") {
let disposeBag = DisposeBag()
Observable.of(1, 2, 3, 4, 5, 6)
.takeWhile { $0 < 4 }
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
/*:
----
## `takeUntil`
Emits elements from a source `Observable` sequence until a reference `Observable` sequence emits an element. [More info](http://reactivex.io/documentation/operators/takeuntil.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/takeuntil.png)
*/
example("takeUntil") {
let disposeBag = DisposeBag()
let sourceSequence = PublishSubject<String>()
let referenceSequence = PublishSubject<String>()
sourceSequence
.takeUntil(referenceSequence)
.subscribe { print($0) }
.addDisposableTo(disposeBag)
sourceSequence.onNext("🐱")
sourceSequence.onNext("🐰")
sourceSequence.onNext("🐶")
referenceSequence.onNext("🔴")
sourceSequence.onNext("🐸")
sourceSequence.onNext("🐷")
sourceSequence.onNext("🐵")
}
/*:
----
## `skip`
Suppresses emitting the specified number of elements from the beginning of an `Observable` sequence. [More info](http://reactivex.io/documentation/operators/skip.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/skip.png)
*/
example("skip") {
let disposeBag = DisposeBag()
Observable.of("🐱", "🐰", "🐶", "🐸", "🐷", "🐵")
.skip(2)
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
/*:
----
## `skipWhile`
Suppresses emitting the elements from the beginning of an `Observable` sequence that meet the specified condition. [More info](http://reactivex.io/documentation/operators/skipwhile.html)
![](http://reactivex.io/documentation/operators/images/skipWhile.c.png)
*/
example("skipWhile") {
let disposeBag = DisposeBag()
Observable.of(1, 2, 3, 4, 5, 6)
.skipWhile { $0 < 4 }
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
/*:
----
## `skipWhileWithIndex`
Suppresses emitting the elements from the beginning of an `Observable` sequence that meet the specified condition, and emits the remaining elements. The closure is also passed each element's index.
*/
example("skipWhileWithIndex") {
let disposeBag = DisposeBag()
Observable.of("🐱", "🐰", "🐶", "🐸", "🐷", "🐵")
.skipWhileWithIndex { element, index in
index < 3
}
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
/*:
----
## `skipUntil`
Suppresses emitting the elements from a source `Observable` sequence until a reference `Observable` sequence emits an element. [More info](http://reactivex.io/documentation/operators/skipuntil.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/skipuntil.png)
*/
example("skipUntil") {
let disposeBag = DisposeBag()
let sourceSequence = PublishSubject<String>()
let referenceSequence = PublishSubject<String>()
sourceSequence
.skipUntil(referenceSequence)
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
sourceSequence.onNext("🐱")
sourceSequence.onNext("🐰")
sourceSequence.onNext("🐶")
referenceSequence.onNext("🔴")
sourceSequence.onNext("🐸")
sourceSequence.onNext("🐷")
sourceSequence.onNext("🐵")
}
//: [Next](@next) - [Table of Contents](Table_of_Contents)

View File

@ -1,27 +0,0 @@
/*:
> # IMPORTANT: To use `Rx.playground`, please:
1. Open `Rx.xcworkspace`
2. Build `RxSwift-OSX` scheme
3. And then open `Rx` playground in `Rx.xcworkspace` tree view.
4. Choose `View > Show Debug Area`
*/
/*:
## Index:
1. [Introduction](Introduction)
1. [Subjects](Subjects)
1. [Transforming Observables](Transforming_Observables)
1. [Filtering Observables](Filtering_Observables)
1. [Combining Observables](Combining_Observables)
1. [Error Handling Operators](Error_Handling_Operators)
1. [Observable Utility Operators](Observable_Utility_Operators)
1. [Conditional and Boolean Operators](Conditional_and_Boolean_Operators)
1. [Mathematical and Aggregate Operators](Mathematical_and_Aggregate_Operators)
1. [Connectable Observable Operators](Connectable_Observable_Operators)
*/
//: [Index](Index) - [Next >>](@next)

View File

@ -1,203 +1,83 @@
/*:
> # IMPORTANT: To use `Rx.playground`, please:
1. Open `Rx.xcworkspace`
2. Build `RxSwift-OSX` scheme
3. And then open `Rx` playground in `Rx.xcworkspace` tree view.
4. Choose `View > Show Debug Area`
*/
//: [<< Index](@previous)
> # IMPORTANT: To use **Rx.playground**:
1. Open **Rx.xcworkspace**.
1. Build the **RxSwift-OSX** scheme (**Product** **Build**).
1. Open **Rx** playground in the **Project navigator**.
1. Show the Debug Area (**View** **Debug Area** **Show Debug Area**).
----
[Previous](@previous)
*/
import RxSwift
import Foundation
/*:
# Introduction
## Why use RxSwift?
A vast majority of the code we write revolves around responding to external actions. When a user manipulates a control, we need to write an @IBAction to respond to that. We need to observe Notifications to detect when the keyboard changes position. We must provide blocks to execute when URL Sessions respond with data. And we use KVO to detect changes in variables.
A vast majority of the code we write involves responding to external events. When a user manipulates a control, we need to write an `@IBAction` handler to respond. We need to observe notifications to detect when the keyboard changes position. We must provide closures to execute when URL sessions respond with data. And we use KVO to detect changes to variables.
All of these various systems makes our code needlessly complex. Wouldn't it be better if there was one consistent system that handled all of our call/response code? Rx is such a system.
## Observables
The key to understanding RxSwift is in understanding the notion of Observables. Creating them, manipulating them, and subscribing to them in order to react to changes.
## Creating and Subscribing to Observables
The first step in understanding this library is in understanding how to create Observables. There are a number of functions available to make Observables.
Creating an Observable is one thing, but if nothing subscribes to the observable, then nothing will come of it so both are explained simultaneously.
RxSwift is the official implementation of [Reactive Extensions](http://reactivex.io) (aka Rx), which exist for [most major languages and platforms](http://reactivex.io/languages.html).
*/
/*:
### empty
`empty` creates an empty sequence. The only message it sends is the `.Completed` message.
## Concepts
**Every `Observable` instance is just a sequence.**
The key advantage for an `Observable` sequence vs. Swift's `SequenceType` is that it can also receive elements asynchronously. _This is the essence of RxSwift._ Everything else expands upon this concept.
* An `Observable` (`ObservableType`) is equivalent to a `SequenceType`.
* The `ObservableType.subscribe(_:)` method is equivalent to `SequenceType.generate()`.
* `ObservableType.subscribe(_:)` takes an observer (`ObserverType`) parameter, which will be subscribed to automatically receive sequence events and elements emitted by the `Observable`, instead of manually calling `next()` on the returned generator.
*/
/*:
If an `Observable` emits a Next event (`Event.Next(Element)`), it can continue to emit more events. However, if the `Observable` emits either an Error event (`Event.Error(ErrorType)`) or a Completed event (`Event.Completed`), the `Observable` sequence cannot emit additional events to the subscriber.
Sequence grammar explains this more concisely:
`Next* (Error | Completed)?`
And this can also be explained more visually using diagrams:
`--1--2--3--4--5--6--|----> // "|" = Terminates normally`
`--a--b--c--d--e--f--X----> // "X" = Terminates with an error`
`--tap--tap----------tap--> // "|" = Continues indefinitely, such as a sequence of button taps`
> These diagrams are call marble diagrams. You can learn more about them at [RxMarbles.com](http://rxmarbles.com).
*/
example("empty") {
let emptySequence = Observable<Int>.empty()
let subscription = emptySequence
.subscribe { event in
print(event)
}
/*:
### Observables and observers (aka subscribers)
`Observable`s will not execute their subscription closure unless there is a subscriber. In the following example, the closure of the `Observable` will never be executed, because there are no subscribers:
*/
example("Observable with no subscribers") {
_ = Observable<String>.create { observerOfString -> Disposable in
print("This will never be printed")
observerOfString.on(.Next("😬"))
observerOfString.on(.Completed)
return NopDisposable.instance
}
}
/*:
### never
`never` creates a sequence that never sends any element or completes.
*/
example("never") {
let neverSequence = Observable<Int>.never()
let subscription = neverSequence
.subscribe { _ in
print("This block is never called.")
}
}
/*:
### just
`just` represents sequence that contains one element. It sends two messages to subscribers. The first message is the value of single element and the second message is `.Completed`.
*/
example("just") {
let singleElementSequence = Observable.just(32)
let subscription = singleElementSequence
.subscribe { event in
print(event)
}
}
/*:
### sequenceOf
`sequenceOf` creates a sequence of a fixed number of elements.
*/
example("sequenceOf") {
let sequenceOfElements/* : Observable<Int> */ = Observable.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
let subscription = sequenceOfElements
.subscribe { event in
print(event)
}
}
/*:
<<<<<<< 48b782d76115d7551f420c325b85b893d5e5d724
### toObservable
`toObservable` creates a sequence out of an array.
=======
### from
`from` creates a sequence from `Sequence`
>>>>>>> Changes for Swift 3.0.
*/
example("toObservable") {
let sequenceFromArray = [1, 2, 3, 4, 5].toObservable()
let subscription = sequenceFromArray
.subscribe { event in
print(event)
}
}
/*:
### create
`create` creates sequence using Swift closure. This examples creates custom version of `just` operator.
*/
example("create") {
let myJust = { (singleElement: Int) -> Observable<Int> in
return Observable.create { observer in
observer.on(.Next(singleElement))
observer.on(.Completed)
----
In the following example, the closure will be executed when `subscribe(_:)` is called:
*/
example("Observable with subscriber") {
_ = Observable<String>.create { observerOfString in
print("Observable created")
observerOfString.on(.Next("😉"))
observerOfString.on(.Completed)
return NopDisposable.instance
}
}
let subscription = myJust(5)
.subscribe { event in
print(event)
}
}
/*:
### generate
`generate` creates sequence that generates its values and determines when to terminate based on its previous values.
*/
example("generate") {
let generated = Observable.generate(
initialState: 0,
condition: { $0 < 3 },
iterate: { $0 + 1 }
)
let subscription = generated
.subscribe { event in
print(event)
}
}
/*:
### error
create an Observable that emits no items and terminates with an error
*/
example("error") {
let error = NSError(domain: "Test", code: -1, userInfo: nil)
let erroredSequence = Observable<Int>.error(error)
let subscription = erroredSequence
.subscribe { event in
print(event)
}
}
/*:
### `deferred`
do not create the Observable until the observer subscribes, and create a fresh Observable for each observer
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/defer.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/defer.html )
*/
example("deferred") {
let deferredSequence: Observable<Int> = Observable.deferred {
print("creating")
return Observable.create { observer in
print("emmiting")
observer.on(.Next(0))
observer.on(.Next(1))
observer.on(.Next(2))
return NopDisposable.instance
}
}
_ = deferredSequence
.subscribe { event in
print(event)
}
_ = deferredSequence
.subscribe { event in
print(event)
}
}
/*:
There is a lot more useful methods in the RxCocoa library, so check them out:
* `rx_observe` exist on every NSObject and wraps KVO.
* `rx_tap` exists on buttons and wraps @IBActions
* `rx_notification` wraps NotificationCenter events
* ... and many others
*/
> Don't concern yourself with the details of how these `Observable`s were created in these examples. We'll get into that [next](@next).
#
> `subscribe(_:)` returns a `Disposable` instance that represents a disposable resource such as a subscription. It was ignored in the previous simple example, but it should normally be properly handled. This usually means adding it to a `DisposeBag` instance. All examples going forward will include proper handling, because, well, practice makes _permanent_ 🙂. You can learn more about this in the [Disposing section](https://github.com/ReactiveX/RxSwift/blob/master/Documentation/GettingStarted.md#disposing) of the [Getting Started guide](https://github.com/ReactiveX/RxSwift/blob/master/Documentation/GettingStarted.md).
*/
//: [Index](Index) - [Next >>](@next)
//: [Next](@next) - [Table of Contents](Table_of_Contents)

View File

@ -1,86 +1,72 @@
/*:
> # IMPORTANT: To use `Rx.playground`, please:
1. Open `Rx.xcworkspace`
2. Build `RxSwift-OSX` scheme
3. And then open `Rx` playground in `Rx.xcworkspace` tree view.
4. Choose `View > Show Debug Area`
*/
//: [<< Previous](@previous) - [Index](Index)
> # IMPORTANT: To use **Rx.playground**:
1. Open **Rx.xcworkspace**.
1. Build the **RxSwift-OSX** scheme (**Product** **Build**).
1. Open **Rx** playground in the **Project navigator**.
1. Show the Debug Area (**View** **Debug Area** **Show Debug Area**).
----
[Previous](@previous) - [Table of Contents](Table_of_Contents)
*/
import RxSwift
/*:
## Mathematical and Aggregate Operators
Operators that operate on the entire sequence of items emitted by an Observable
*/
/*:
### `concat`
Emit the emissions from two or more Observables without interleaving them.
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/concat.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/concat.html )
*/
example("concat") {
let var1 = BehaviorSubject(value: 0)
let var2 = BehaviorSubject(value: 200)
# Mathematical and Aggregate Operators
Operators that operate on the entire sequence of items emitted by an `Observable`.
## `toArray`
Converts an `Observable` sequence into an array, emits that array as a new single-element `Observable` sequence, and then terminates. [More info](http://reactivex.io/documentation/operators/to.html)
![](http://reactivex.io/documentation/operators/images/to.c.png)
*/
example("toArray") {
let disposeBag = DisposeBag()
// var3 is like an Observable<Observable<Int>>
let var3 = BehaviorSubject(value: var1)
let d = var3
.concat()
.subscribe {
print($0)
}
var1.on(.Next(1))
var1.on(.Next(2))
var1.on(.Next(3))
var1.on(.Next(4))
var3.on(.Next(var2))
var2.on(.Next(201))
var1.on(.Next(5))
var1.on(.Next(6))
var1.on(.Next(7))
var1.on(.Completed)
var2.on(.Next(202))
var2.on(.Next(203))
var2.on(.Next(204))
Observable.range(start: 1, count: 10)
.toArray()
.subscribe { print($0) }
.addDisposableTo(disposeBag)
}
/*:
### `reduce`
Apply a function to each item emitted by an Observable, sequentially, and emit the final value.
This function will perform a function on each element in the sequence until it is completed, then send a message with the aggregate value. It works much like the Swift `reduce` function works on sequences.
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/reduce.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/reduce.html )
*/
----
## `reduce`
Begins with an initial seed value, and then applies an accumulator closure to all elements emitted by an `Observable` sequence, and returns the aggregate result as a single-element `Observable` sequence. [More info](http://reactivex.io/documentation/operators/reduce.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/reduce.png)
*/
example("reduce") {
_ = Observable.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
.reduce(0, accumulator: +)
.subscribe {
print($0)
}
let disposeBag = DisposeBag()
Observable.of(10, 100, 1000)
.reduce(1, accumulator: +)
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
/*:
----
## `concat`
Joins elements from inner `Observable` sequences of an `Observable` sequence in a sequential manner, waiting for each sequence to terminate successfully before emitting elements from the next sequence. [More info](http://reactivex.io/documentation/operators/concat.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/concat.png)
*/
example("concat") {
let disposeBag = DisposeBag()
let subject1 = BehaviorSubject(value: "🍎")
let subject2 = BehaviorSubject(value: "🐶")
let variable = Variable(subject1)
variable.asObservable()
.concat()
.subscribe { print($0) }
.addDisposableTo(disposeBag)
subject1.onNext("🍐")
subject1.onNext("🍊")
variable.value = subject2
subject2.onNext("I would be ignored")
subject2.onNext("🐱")
subject1.onCompleted()
subject2.onNext("🐭")
}
//: [Index](Index) - [Next >>](@next)
//: [Next](@next) - [Table of Contents](Table_of_Contents)

View File

@ -1,122 +0,0 @@
/*:
> # IMPORTANT: To use `Rx.playground`, please:
1. Open `Rx.xcworkspace`
2. Build `RxSwift-OSX` scheme
3. And then open `Rx` playground in `Rx.xcworkspace` tree view.
4. Choose `View > Show Debug Area`
*/
//: [<< Previous](@previous) - [Index](Index)
import RxSwift
import Foundation
/*:
## Observable Utility Operators
A toolbox of useful Operators for working with Observables.
*/
/*:
### `subscribe`
[More info in reactive.io website]( http://reactivex.io/documentation/operators/subscribe.html )
*/
example("subscribe") {
let sequenceOfInts = PublishSubject<Int>()
_ = sequenceOfInts
.subscribe {
print($0)
}
sequenceOfInts.on(.Next(1))
sequenceOfInts.on(.Completed)
}
/*:
There are several variants of the `subscribe` operator.
*/
/*:
### `subscribeNext`
*/
example("subscribeNext") {
let sequenceOfInts = PublishSubject<Int>()
_ = sequenceOfInts
.subscribeNext {
print($0)
}
sequenceOfInts.on(.Next(1))
sequenceOfInts.on(.Completed)
}
/*:
### `subscribeCompleted`
*/
example("subscribeCompleted") {
let sequenceOfInts = PublishSubject<Int>()
_ = sequenceOfInts
.subscribeCompleted {
print("It's completed")
}
sequenceOfInts.on(.Next(1))
sequenceOfInts.on(.Completed)
}
/*:
### `subscribeError`
*/
example("subscribeError") {
let sequenceOfInts = PublishSubject<Int>()
_ = sequenceOfInts
.subscribeError { error in
print(error)
}
sequenceOfInts.on(.Next(1))
sequenceOfInts.on(.Error(NSError(domain: "Examples", code: -1, userInfo: nil)))
}
/*:
### `doOn`
register an action to take upon a variety of Observable lifecycle events
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/do.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/do.html )
*/
example("doOn") {
let sequenceOfInts = PublishSubject<Int>()
_ = sequenceOfInts
.doOn {
print("Intercepted event \($0)")
}
.subscribe {
print($0)
}
sequenceOfInts.on(.Next(1))
sequenceOfInts.on(.Completed)
}
//: [Index](Index) - [Next >>](@next)

View File

@ -1,113 +0,0 @@
/*:
> # IMPORTANT: To use `Rx.playground`, please:
1. Open `Rx.xcworkspace`
2. Build `RxSwift-OSX` scheme
3. And then open `Rx` playground in `Rx.xcworkspace` tree view.
4. Choose `View > Show Debug Area`
*/
//: [<< Previous](@previous) - [Index](Index)
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<O: ObservableType>(name: String, sequence: O) -> Disposable {
return sequence
.subscribe { e in
print("Subscription: \(name), event: \(e)")
}
}
/*:
## PublishSubject
`PublishSubject` emits to an observer only those items that are emitted by the source Observable(s) subsequent to the time of the subscription.
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/publishsubject.png)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/publishsubject_error.png)
*/
example("PublishSubject") {
let disposeBag = DisposeBag()
let subject = PublishSubject<String>()
writeSequenceToConsole("1", sequence: subject).addDisposableTo(disposeBag)
subject.on(.Next("a"))
subject.on(.Next("b"))
writeSequenceToConsole("2", sequence: subject).addDisposableTo(disposeBag)
subject.on(.Next("c"))
subject.on(.Next("d"))
}
/*:
## ReplaySubject
`ReplaySubject` emits to any observer all of the items that were emitted by the source Observable(s), regardless of when the observer subscribes.
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/replaysubject.png)
*/
example("ReplaySubject") {
let disposeBag = DisposeBag()
let subject = ReplaySubject<String>.create(bufferSize: 1)
writeSequenceToConsole("1", sequence: subject).addDisposableTo(disposeBag)
subject.on(.Next("a"))
subject.on(.Next("b"))
writeSequenceToConsole("2", sequence: subject).addDisposableTo(disposeBag)
subject.on(.Next("c"))
subject.on(.Next("d"))
}
/*:
## BehaviorSubject
When an observer subscribes to a `BehaviorSubject`, it begins by emitting the item most recently emitted by the source Observable (or a seed/default value if none has yet been emitted) and then continues to emit any other items emitted later by the source Observable(s).
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/behaviorsubject.png)
![](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).addDisposableTo(disposeBag)
subject.on(.Next("a"))
subject.on(.Next("b"))
writeSequenceToConsole("2", sequence: subject).addDisposableTo(disposeBag)
subject.on(.Next("c"))
subject.on(.Next("d"))
subject.on(.Completed)
}
/*:
## Variable
`Variable` wraps `BehaviorSubject`. Advantage of using variable over `BehaviorSubject` is that variable can never explicitly complete or error out, and `BehaviorSubject` can in case `Error` or `Completed` message is send to it. `Variable` will also automatically complete in case it's being deallocated.
*/
example("Variable") {
let disposeBag = DisposeBag()
let variable = Variable("z")
writeSequenceToConsole("1", sequence: variable.asObservable()).addDisposableTo(disposeBag)
variable.value = "a"
variable.value = "b"
writeSequenceToConsole("2", sequence: variable.asObservable()).addDisposableTo(disposeBag)
variable.value = "c"
variable.value = "d"
}
//: [Index](Index) - [Next >>](@next)

View File

@ -0,0 +1,21 @@
/*:
> # IMPORTANT: To use **Rx.playground**:
1. Open **Rx.xcworkspace**.
1. Build the **RxSwift-OSX** scheme (**Product** **Build**).
1. Open **Rx** playground in the **Project navigator**.
1. Show the Debug Area (**View** **Debug Area** **Show Debug Area**).
----
## Table of Contents:
1. [Introduction](Introduction)
1. [Creating and Subscribing to Observables](Creating_and_Subscribing_to_Observables)
1. [Working with Subjects](Working_with_Subjects)
1. [Combining Operators](Combining_Operators)
1. [Transforming Operators](Transforming_Operators)
1. [Filtering and Conditional Operators](Filtering_and_Conditional_Operators)
1. [Mathematical and Aggregate Operators](Mathematical_and_Aggregate_Operators)
1. [Connectable Operators](Connectable_Operators)
1. [Error Handling Operators](Error_Handling_Operators)
1. [Debugging Operators](Debugging_Operators)
*/
//: [Next](@next)

View File

@ -1,88 +0,0 @@
/*:
> # IMPORTANT: To use `Rx.playground`, please:
1. Open `Rx.xcworkspace`
2. Build `RxSwift-OSX` scheme
3. And then open `Rx` playground in `Rx.xcworkspace` tree view.
4. Choose `View > Show Debug Area`
*/
//: [<< Previous](@previous) - [Index](Index)
import RxSwift
/*:
## Transforming Observables
Operators that transform items that are emitted by an Observable.
*/
/*:
### `map` / `select`
Transform the items emitted by an Observable by applying a function to each item
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/map.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/map.html )
*/
example("map") {
let originalSequence = Observable.of(1, 2, 3)
_ = originalSequence
.map { number in
number * 2
}
.subscribe { print($0) }
}
/*:
### `flatMap`
Transform the items emitted by an Observable into Observables, then flatten the emissions from those into a single Observable
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/flatmap.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/flatmap.html )
*/
example("flatMap") {
let sequenceInt = Observable.of(1, 2, 3)
let sequenceString = Observable.of("A", "B", "C", "D", "E", "F", "--")
_ = sequenceInt
.flatMap { (x:Int) -> Observable<String> in
print("from sequenceInt \(x)")
return sequenceString
}
.subscribe {
print($0)
}
}
/*:
### `scan`
Apply a function to each item emitted by an Observable, sequentially, and emit each successive value
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/scan.png)
[More info in reactive.io website]( http://reactivex.io/documentation/operators/scan.html )
*/
example("scan") {
let sequenceToSum = Observable.of(0, 1, 2, 3, 4, 5)
_ = sequenceToSum
.scan(0) { acum, elem in
acum + elem
}
.subscribe {
print($0)
}
}
//: [Index](Index) - [Next >>](@next)

View File

@ -0,0 +1,78 @@
/*:
> # IMPORTANT: To use **Rx.playground**:
1. Open **Rx.xcworkspace**.
1. Build the **RxSwift-OSX** scheme (**Product** **Build**).
1. Open **Rx** playground in the **Project navigator**.
1. Show the Debug Area (**View** **Debug Area** **Show Debug Area**).
----
[Previous](@previous) - [Table of Contents](Table_of_Contents)
*/
import RxSwift
/*:
# Transforming Operators
Operators that transform Next event elements emitted by an `Observable` sequence.
## `map`
Applies a transforming closure to elements emitted by an `Observable` sequence, and returns a new `Observable` sequence of the transformed elements. [More info](http://reactivex.io/documentation/operators/map.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/map.png)
*/
example("map") {
let disposeBag = DisposeBag()
Observable.of(1, 2, 3)
.map { $0 * $0 }
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
/*:
----
## `flatMap` and `flatMapLatest`
Transforms the elements emitted by an `Observable` sequence into `Observable` sequences, and merges the emissions from both `Observable` sequences into a single `Observable` sequence. This is also useful when, for example, when you have an `Observable` sequence that itself emits `Observable` sequences, and you want to be able to react to new emissions from either `Observable` sequence. The difference between `flatMap` and `flatMapLatest` is, `flatMapLatest` will only emit elements from the most recent inner `Observable` sequence. [More info](http://reactivex.io/documentation/operators/flatmap.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/flatmap.png)
*/
example("flatMap and flatMapLatest") {
let disposeBag = DisposeBag()
struct Player {
var score: Variable<Int>
}
let 👦🏻 = Player(score: Variable(80))
let 👧🏼 = Player(score: Variable(90))
let player = Variable(👦🏻)
player.asObservable()
.flatMap { $0.score.asObservable() } // Change flatMap to flatMapLatest and observe change in printed output
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
👦🏻.score.value = 85
player.value = 👧🏼
👦🏻.score.value = 95 // Will be printed when using flatMap, but will not be printed when using flatMapLatest
👧🏼.score.value = 100
}
/*:
> In this example, using `flatMap` may have unintended consequences. After assigning 👧🏼 to `player.value`, `👧🏼.score` will begin to emit elements, but the previous inner `Observable` sequence (`👦🏻.score`) will also still emit elements. By changing `flatMap` to `flatMapLatest`, only the most recent inner `Observable` sequence (`👧🏼.score`) will emit elements, i.e., setting `👦🏻.score.value` to `95` has no effect.
#
> `flatMapLatest` is actually a combination of the `map` and `switchLatest` operators.
*/
/*:
----
## `scan`
Begins with an initial seed value, and then applies an accumulator closure to each element emitted by an `Observable` sequence, and returns each intermediate result as a single-element `Observable` sequence. [More info](http://reactivex.io/documentation/operators/scan.html)
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/scan.png)
*/
example("scan") {
let disposeBag = DisposeBag()
Observable.of(10, 100, 1000)
.scan(1) { aggregateValue, newValue in
aggregateValue + newValue
}
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
}
//: [Next](@next) - [Table of Contents](Table_of_Contents)

View File

@ -0,0 +1,110 @@
/*:
> # IMPORTANT: To use **Rx.playground**:
1. Open **Rx.xcworkspace**.
1. Build the **RxSwift-OSX** scheme (**Product** **Build**).
1. Open **Rx** playground in the **Project navigator**.
1. Show the Debug Area (**View** **Debug Area** **Show Debug Area**).
----
[Previous](@previous) - [Table of Contents](Table_of_Contents)
*/
import RxSwift
/*:
# Working with Subjects
A Subject is a sort of bridge or proxy that is available in some implementations of Rx that acts as both an observer and `Observable`. Because it is an observer, it can subscribe to one or more `Observable`s, and because it is an `Observable`, it can pass through the items it observes by reemitting them, and it can also emit new items. [More info](http://reactivex.io/documentation/subject.html)
*/
extension ObservableType {
/**
Add observer with `id` and print each emitted event.
- parameter id: an identifier for the subscription.
*/
func addObserver(id: String) -> Disposable {
return subscribe { print("Subscription:", id, "Event:", $0) }
}
}
func writeSequenceToConsole<O: ObservableType>(name: String, sequence: O) -> Disposable {
return sequence.subscribe { event in
print("Subscription: \(name), event: \(event)")
}
}
/*:
## PublishSubject
Broadcasts new events to all observers as of their time of the subscription.
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/publishsubject.png "PublishSubject")
*/
example("PublishSubject") {
let disposeBag = DisposeBag()
let subject = PublishSubject<String>()
subject.addObserver("1").addDisposableTo(disposeBag)
subject.onNext("🐶")
subject.onNext("🐱")
subject.addObserver("2").addDisposableTo(disposeBag)
subject.onNext("🅰️")
subject.onNext("🅱️")
}
/*:
> This example also introduces using the `onNext(_:)` convenience method, equivalent to `on(.Next(_:)`, which causes a new Next event to be emitted to subscribers with the provided `element`. There are also `onError(_:)` and `onCompleted()` convenience methods, equivalent to `on(.Error(_:))` and `on(.Completed)`, respectively.
----
## ReplaySubject
Broadcasts new events to all subscribers, and the specified `bufferSize` number of previous events to new subscribers.
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/replaysubject.png)
*/
example("ReplaySubject") {
let disposeBag = DisposeBag()
let subject = ReplaySubject<String>.create(bufferSize: 1)
subject.addObserver("1").addDisposableTo(disposeBag)
subject.onNext("🐶")
subject.onNext("🐱")
subject.addObserver("2").addDisposableTo(disposeBag)
subject.onNext("🅰️")
subject.onNext("🅱️")
}
/*:
----
## BehaviorSubject
Broadcasts new events to all subscribers, and the most recent (or initial) value to new subscribers.
![](https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/MarbleDiagrams/png/behaviorsubject.png)
*/
example("BehaviorSubject") {
let disposeBag = DisposeBag()
let subject = BehaviorSubject(value: "🔴")
subject.addObserver("1").addDisposableTo(disposeBag)
subject.onNext("🐶")
subject.onNext("🐱")
subject.addObserver("2").addDisposableTo(disposeBag)
subject.onNext("🅰️")
subject.onNext("🅱️")
subject.addObserver("3").addDisposableTo(disposeBag)
subject.onNext("🍐")
subject.onNext("🍊")
}
/*:
> Notice what's missing in these previous examples? A Completed event. `PublishSubject`, `ReplaySubject`, and `BehaviorSubject` do not automatically emit Completed events when they are about to be disposed of.
----
## Variable
Wraps a `BehaviorSubject`, so it will emit the most recent (or initial) value to new subscribers. And `Variable` also maintains current value state. `Variable` will never emit an Error event. However, it will automatically emit a Completed event and terminate on `deinit`.
*/
example("Variable") {
let disposeBag = DisposeBag()
let variable = Variable("🔴")
variable.asObservable().addObserver("1").addDisposableTo(disposeBag)
variable.value = "🐶"
variable.value = "🐱"
variable.asObservable().addObserver("2").addDisposableTo(disposeBag)
variable.value = "🅰️"
variable.value = "🅱️"
}
//: > Call `asObservable()` on a `Variable` instance in order to access its underlying `BehaviorSubject` sequence. `Variable`s do not implement the `on` operator (or, e.g., `onNext(_:)`), but instead expose a `value` property that can be used to get the current value, and also set a new value. Setting a new value will also add that value onto its underlying `BehaviorSubject` sequence.
//: [Next](@next) - [Table of Contents](Table_of_Contents)

View File

@ -1,31 +1,48 @@
import Foundation
public func example(description: String, action: () -> ()) {
print("\n--- \(description) example ---")
/**
Encloses each code example in its own scope. Prints a `description` header and then executes the `action` closure.
- parameter description: example description
- parameter action: `Void` closure
*/
public func example(description: String, @noescape action: Void -> Void) {
printExampleHeader(description)
action()
}
public func delay(delay:Double, closure:()->()) {
public func printExampleHeader(description: String) {
print("\n--- \(description) example ---")
}
public enum Error: ErrorType {
case Test
}
/**
Executes `closure` on main thread after `delay` seconds.
- parameter delay: time in seconds to wait before executing `closure`
- parameter closure: `Void` closure
*/
public func delay(delay: Double, closure: Void -> Void) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
dispatch_get_main_queue(),
closure)
}
#if NOT_IN_PLAYGROUND
public func playgroundShouldContinueIndefinitely() {
}
public func playgroundShouldContinueIndefinitely() { }
#else
import XCPlayground
public func playgroundShouldContinueIndefinitely() {
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
}
import XCPlayground
public func playgroundShouldContinueIndefinitely() {
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
}
#endif

View File

@ -1,16 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='6.0' target-platform='osx' display-mode='rendered'>
<pages>
<page name='Index'/>
<page name='Table_of_Contents'/>
<page name='Introduction'/>
<page name='Subjects'/>
<page name='Transforming_Observables'/>
<page name='Filtering_Observables'/>
<page name='Combining_Observables'/>
<page name='Error_Handling_Operators'/>
<page name='Observable_Utility_Operators'/>
<page name='Conditional_and_Boolean_Operators'/>
<page name='Creating_and_Subscribing_to_Observables'/>
<page name='Working_with_Subjects'/>
<page name='Combining_Operators'/>
<page name='Transforming_Operators'/>
<page name='Filtering_and_Conditional_Operators'/>
<page name='Mathematical_and_Aggregate_Operators'/>
<page name='Connectable_Observable_Operators'/>
<page name='Connectable_Operators'/>
<page name='Error_Handling_Operators'/>
<page name='Debugging_Operators'/>
<page name='Enable_RxSwift.resourceCount'/>
</pages>
</playground>

View File

@ -7,6 +7,21 @@
objects = {
/* Begin PBXBuildFile section */
1AF67DA21CED420A00C310FA /* PublishSubjectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF67DA11CED420A00C310FA /* PublishSubjectTest.swift */; };
1AF67DA31CED427D00C310FA /* PublishSubjectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF67DA11CED420A00C310FA /* PublishSubjectTest.swift */; };
1AF67DA41CED427D00C310FA /* PublishSubjectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF67DA11CED420A00C310FA /* PublishSubjectTest.swift */; };
1AF67DA61CED430100C310FA /* ReplaySubjectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF67DA51CED430100C310FA /* ReplaySubjectTest.swift */; };
1AF67DA71CED430100C310FA /* ReplaySubjectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF67DA51CED430100C310FA /* ReplaySubjectTest.swift */; };
1AF67DA81CED430100C310FA /* ReplaySubjectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF67DA51CED430100C310FA /* ReplaySubjectTest.swift */; };
271A97411CFC996B00D64125 /* UIViewController+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 271A97401CFC996B00D64125 /* UIViewController+Rx.swift */; };
271A97441CFC9F7B00D64125 /* UIViewControler+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 271A97421CFC99FE00D64125 /* UIViewControler+RxTests.swift */; };
46307D4E1CDE77D800E47A1C /* UIAlertAction+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46307D4D1CDE77D800E47A1C /* UIAlertAction+Rx.swift */; };
46307D4F1CDE77D800E47A1C /* UIAlertAction+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46307D4D1CDE77D800E47A1C /* UIAlertAction+Rx.swift */; };
54700CA01CE37E1800EF3A8F /* UINavigationItem+RxTests.swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54700C9E1CE37D1000EF3A8F /* UINavigationItem+RxTests.swift.swift */; };
54700CA11CE37E1900EF3A8F /* UINavigationItem+RxTests.swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54700C9E1CE37D1000EF3A8F /* UINavigationItem+RxTests.swift.swift */; };
54D2138E1CE0824E0028D5B4 /* UINavigationItem+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D2138C1CE081890028D5B4 /* UINavigationItem+Rx.swift */; };
54D213921CE08D0C0028D5B4 /* UINavigationItem+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D2138C1CE081890028D5B4 /* UINavigationItem+Rx.swift */; };
54D213931CE08DDB0028D5B4 /* UINavigationItem+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D2138C1CE081890028D5B4 /* UINavigationItem+Rx.swift */; };
79E9DE891C3417FD009970AF /* DispatchQueueSchedulerQOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79E9DE881C3417FD009970AF /* DispatchQueueSchedulerQOS.swift */; };
79E9DE8A1C3417FD009970AF /* DispatchQueueSchedulerQOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79E9DE881C3417FD009970AF /* DispatchQueueSchedulerQOS.swift */; };
79E9DE8B1C3417FD009970AF /* DispatchQueueSchedulerQOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79E9DE881C3417FD009970AF /* DispatchQueueSchedulerQOS.swift */; };
@ -24,6 +39,13 @@
842A5A2C1C357F92003568D5 /* NSTextStorage+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842A5A281C357F7D003568D5 /* NSTextStorage+Rx.swift */; };
842A5A2D1C357F93003568D5 /* NSTextStorage+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842A5A281C357F7D003568D5 /* NSTextStorage+Rx.swift */; };
842A5A2E1C357F94003568D5 /* NSTextStorage+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842A5A281C357F7D003568D5 /* NSTextStorage+Rx.swift */; };
844BC8AC1CE4FA6300F5C7CB /* RxPickerViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844BC8AA1CE4FA5600F5C7CB /* RxPickerViewDelegateProxy.swift */; };
844BC8AD1CE4FA6400F5C7CB /* RxPickerViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844BC8AA1CE4FA5600F5C7CB /* RxPickerViewDelegateProxy.swift */; };
844BC8AE1CE4FA6600F5C7CB /* RxPickerViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844BC8AA1CE4FA5600F5C7CB /* RxPickerViewDelegateProxy.swift */; };
844BC8B41CE4FD7500F5C7CB /* UIPickerView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844BC8B31CE4FD7500F5C7CB /* UIPickerView+Rx.swift */; };
844BC8B51CE4FD7500F5C7CB /* UIPickerView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844BC8B31CE4FD7500F5C7CB /* UIPickerView+Rx.swift */; };
844BC8B61CE4FD7500F5C7CB /* UIPickerView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844BC8B31CE4FD7500F5C7CB /* UIPickerView+Rx.swift */; };
844BC8BB1CE5024500F5C7CB /* UIPickerView+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844BC8B71CE5023200F5C7CB /* UIPickerView+RxTests.swift */; };
846436E31C9AF65B0035B40D /* RxSearchControllerDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846436E11C9AF64C0035B40D /* RxSearchControllerDelegateProxy.swift */; };
846436E51C9AF65E0035B40D /* RxSearchControllerDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846436E11C9AF64C0035B40D /* RxSearchControllerDelegateProxy.swift */; };
846436E61C9AF6670035B40D /* RxSearchControllerDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846436E11C9AF64C0035B40D /* RxSearchControllerDelegateProxy.swift */; };
@ -43,6 +65,11 @@
84E4D3931C9AFD3500ADFDC9 /* UISearchController+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E4D3901C9AFCD500ADFDC9 /* UISearchController+Rx.swift */; };
84E4D3941C9AFD3600ADFDC9 /* UISearchController+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E4D3901C9AFCD500ADFDC9 /* UISearchController+Rx.swift */; };
84E4D3961C9B011000ADFDC9 /* UISearchController+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E4D3951C9B011000ADFDC9 /* UISearchController+RxTests.swift */; };
914FCD671CCDB82E0058B304 /* UIPageControl+RxTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 914FCD661CCDB82E0058B304 /* UIPageControl+RxTest.swift */; };
914FCD681CCDB82E0058B304 /* UIPageControl+RxTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 914FCD661CCDB82E0058B304 /* UIPageControl+RxTest.swift */; };
91BE429C1CBF7EC000F6B062 /* UIPageControl+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91BE429B1CBF7EC000F6B062 /* UIPageControl+Rx.swift */; };
91BE429D1CBF7EC000F6B062 /* UIPageControl+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91BE429B1CBF7EC000F6B062 /* UIPageControl+Rx.swift */; };
91BE429F1CBF7F3D00F6B062 /* UIPageControl+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91BE429B1CBF7EC000F6B062 /* UIPageControl+Rx.swift */; };
9BA1CBD31C0F7D550044B50A /* UIActivityIndicatorView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BA1CBD11C0F7C0A0044B50A /* UIActivityIndicatorView+Rx.swift */; };
9BA1CBFD1C0F84A10044B50A /* UIActivityIndicatorView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BA1CBD11C0F7C0A0044B50A /* UIActivityIndicatorView+Rx.swift */; };
9BA1CBFE1C0F84C40044B50A /* UIActivityIndicatorView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BA1CBD11C0F7C0A0044B50A /* UIActivityIndicatorView+Rx.swift */; };
@ -627,6 +654,10 @@
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 */; };
C88F76811CE5341700D5A014 /* RxTextInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88F76801CE5341700D5A014 /* RxTextInput.swift */; };
C88F76821CE5341700D5A014 /* RxTextInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88F76801CE5341700D5A014 /* RxTextInput.swift */; };
C88F76831CE5341700D5A014 /* RxTextInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88F76801CE5341700D5A014 /* RxTextInput.swift */; };
C88F76841CE5341700D5A014 /* RxTextInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88F76801CE5341700D5A014 /* RxTextInput.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 */; };
@ -916,6 +947,10 @@
C8F0C0431BBBFBB9001B112F /* _RX.h in Headers */ = {isa = PBXBuildFile; fileRef = C8093E821B8A732E0088E94D /* _RX.h */; settings = {ATTRIBUTES = (Public, ); }; };
C8F0C0451BBBFBB9001B112F /* _RXKVOObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = C8093E861B8A732E0088E94D /* _RXKVOObserver.h */; settings = {ATTRIBUTES = (Public, ); }; };
C8F0C04F1BBBFBCE001B112F /* ObservableConvertibleType+Blocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093F581B8A73A20088E94D /* ObservableConvertibleType+Blocking.swift */; };
C8F27DC01CE68DA600D5FB4F /* UITextView+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F27DB11CE6711600D5FB4F /* UITextView+RxTests.swift */; };
C8F27DC11CE68DA700D5FB4F /* UITextView+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F27DB11CE6711600D5FB4F /* UITextView+RxTests.swift */; };
C8F27DC21CE68DAB00D5FB4F /* UITextField+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F27DAC1CE6710900D5FB4F /* UITextField+RxTests.swift */; };
C8F27DC31CE68DAC00D5FB4F /* UITextField+RxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F27DAC1CE6710900D5FB4F /* UITextField+RxTests.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 */; };
@ -1129,6 +1164,9 @@
D2EBEB431BB9B6DE003A27DC /* SubjectType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CC11B8A72BE0088E94D /* SubjectType.swift */; };
D2EBEB441BB9B6DE003A27DC /* Variable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093CC21B8A72BE0088E94D /* Variable.swift */; };
D2EBEB8A1BB9B9EE003A27DC /* ObservableConvertibleType+Blocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8093F581B8A73A20088E94D /* ObservableConvertibleType+Blocking.swift */; };
D2F461021CD7AC1F00527B4D /* Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2F461001CD7ABE400527B4D /* Reactive.swift */; };
D2F461031CD7AC2000527B4D /* Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2F461001CD7ABE400527B4D /* Reactive.swift */; };
D2F461041CD7AC2100527B4D /* Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2F461001CD7ABE400527B4D /* Reactive.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 */; };
@ -1335,12 +1373,22 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
1AF67DA11CED420A00C310FA /* PublishSubjectTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PublishSubjectTest.swift; sourceTree = "<group>"; };
1AF67DA51CED430100C310FA /* ReplaySubjectTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReplaySubjectTest.swift; sourceTree = "<group>"; };
271A97401CFC996B00D64125 /* UIViewController+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Rx.swift"; sourceTree = "<group>"; };
271A97421CFC99FE00D64125 /* UIViewControler+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewControler+RxTests.swift"; sourceTree = "<group>"; };
46307D4D1CDE77D800E47A1C /* UIAlertAction+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIAlertAction+Rx.swift"; sourceTree = "<group>"; };
54700C9E1CE37D1000EF3A8F /* UINavigationItem+RxTests.swift.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UINavigationItem+RxTests.swift.swift"; sourceTree = "<group>"; };
54D2138C1CE081890028D5B4 /* UINavigationItem+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UINavigationItem+Rx.swift"; sourceTree = "<group>"; };
79E9DE881C3417FD009970AF /* DispatchQueueSchedulerQOS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DispatchQueueSchedulerQOS.swift; sourceTree = "<group>"; };
7EDBAEAB1C89B1A5006CBE67 /* UITabBarItem+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITabBarItem+RxTests.swift"; sourceTree = "<group>"; };
7EDBAEB71C89B9B7006CBE67 /* UITabBarItem+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITabBarItem+Rx.swift"; sourceTree = "<group>"; };
7F600F3D1C5D0C0100535B1D /* UIRefreshControl+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIRefreshControl+Rx.swift"; sourceTree = "<group>"; };
7F600F421C5D0D2D00535B1D /* UIRefreshControl+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIRefreshControl+RxTests.swift"; sourceTree = "<group>"; };
842A5A281C357F7D003568D5 /* NSTextStorage+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSTextStorage+Rx.swift"; sourceTree = "<group>"; };
844BC8AA1CE4FA5600F5C7CB /* RxPickerViewDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxPickerViewDelegateProxy.swift; sourceTree = "<group>"; };
844BC8B31CE4FD7500F5C7CB /* UIPickerView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIPickerView+Rx.swift"; sourceTree = "<group>"; };
844BC8B71CE5023200F5C7CB /* UIPickerView+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIPickerView+RxTests.swift"; sourceTree = "<group>"; };
846436E11C9AF64C0035B40D /* RxSearchControllerDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxSearchControllerDelegateProxy.swift; sourceTree = "<group>"; };
8476A01F1C3D5D580040BA22 /* UIImagePickerController+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImagePickerController+RxTests.swift"; sourceTree = "<group>"; };
8479BC441C3ACDA400FB8B54 /* RxImagePickerDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxImagePickerDelegateProxy.swift; sourceTree = "<group>"; };
@ -1348,6 +1396,8 @@
84C225A21C33F00B008724EC /* RxTextStorageDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTextStorageDelegateProxy.swift; sourceTree = "<group>"; };
84E4D3901C9AFCD500ADFDC9 /* UISearchController+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UISearchController+Rx.swift"; sourceTree = "<group>"; };
84E4D3951C9B011000ADFDC9 /* UISearchController+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UISearchController+RxTests.swift"; sourceTree = "<group>"; };
914FCD661CCDB82E0058B304 /* UIPageControl+RxTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIPageControl+RxTest.swift"; sourceTree = "<group>"; };
91BE429B1CBF7EC000F6B062 /* UIPageControl+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIPageControl+Rx.swift"; sourceTree = "<group>"; };
9BA1CBD11C0F7C0A0044B50A /* UIActivityIndicatorView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIActivityIndicatorView+Rx.swift"; sourceTree = "<group>"; };
A111CE961B91C97C00D0DCEE /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
AAE623751C82475700FC7801 /* UIProgressView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIProgressView+Rx.swift"; sourceTree = "<group>"; };
@ -1601,7 +1651,7 @@
C88254001B8A752B00B02D69 /* RxTableViewDataSourceProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewDataSourceProxy.swift; sourceTree = "<group>"; };
C88254011B8A752B00B02D69 /* RxTableViewDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewDelegateProxy.swift; sourceTree = "<group>"; };
C88254021B8A752B00B02D69 /* RxTextViewDelegateProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTextViewDelegateProxy.swift; sourceTree = "<group>"; };
C88254051B8A752B00B02D69 /* UIBarButtonItem+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "UIBarButtonItem+Rx.swift"; sourceTree = "<group>"; };
C88254051B8A752B00B02D69 /* UIBarButtonItem+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "UIBarButtonItem+Rx.swift"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
C88254061B8A752B00B02D69 /* UIButton+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIButton+Rx.swift"; sourceTree = "<group>"; };
C88254071B8A752B00B02D69 /* UICollectionView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UICollectionView+Rx.swift"; sourceTree = "<group>"; };
C88254081B8A752B00B02D69 /* UIControl+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "UIControl+Rx.swift"; sourceTree = "<group>"; };
@ -1619,6 +1669,7 @@
C88254141B8A752B00B02D69 /* UITextView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextView+Rx.swift"; sourceTree = "<group>"; };
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 = "<group>"; };
C88F76801CE5341700D5A014 /* RxTextInput.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTextInput.swift; sourceTree = "<group>"; };
C88FA50C1C25C44800CCFEA4 /* RxTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
C88FA51D1C25C4B500CCFEA4 /* RxTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
C88FA52E1C25C4C000CCFEA4 /* RxTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -1670,6 +1721,8 @@
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; };
C8F27DAC1CE6710900D5FB4F /* UITextField+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextField+RxTests.swift"; sourceTree = "<group>"; };
C8F27DB11CE6711600D5FB4F /* UITextView+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextView+RxTests.swift"; sourceTree = "<group>"; };
C8F6A1441BF0B9B1007DF367 /* NSObject+Rx+RawRepresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Rx+RawRepresentable.swift"; sourceTree = "<group>"; };
C8FA89121C30405400CD3A17 /* VirtualTimeConverterType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VirtualTimeConverterType.swift; sourceTree = "<group>"; };
C8FA89131C30405400CD3A17 /* VirtualTimeScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VirtualTimeScheduler.swift; sourceTree = "<group>"; };
@ -1690,6 +1743,7 @@
D285BAC31BC0231000B3F602 /* SkipUntil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipUntil.swift; sourceTree = "<group>"; };
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; };
D2F461001CD7ABE400527B4D /* Reactive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reactive.swift; sourceTree = "<group>"; };
F31F35AF1BB4FED800961002 /* UIStepper+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStepper+Rx.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -2087,6 +2141,8 @@
C8DB968C1BF7595D0084BD53 /* KVORepresentable+Swift.swift */,
C8BCD3F31C14B6D1005F1280 /* NSLayoutConstraint+Rx.swift */,
C8D132431C42D15E00B59FFF /* SectionedViewDataSourceType.swift */,
D2F461001CD7ABE400527B4D /* Reactive.swift */,
C88F76801CE5341700D5A014 /* RxTextInput.swift */,
);
path = Common;
sourceTree = "<group>";
@ -2230,12 +2286,18 @@
C83508EF1C38706D0027C24C /* RxTests-tvOS-Bridging-Header.h */,
C83508F01C38706D0027C24C /* SentMessageTest.swift */,
C83508F11C38706D0027C24C /* UIView+RxTests.swift */,
271A97421CFC99FE00D64125 /* UIViewControler+RxTests.swift */,
7F600F421C5D0D2D00535B1D /* UIRefreshControl+RxTests.swift */,
7EDBAEAB1C89B1A5006CBE67 /* UITabBarItem+RxTests.swift */,
C8B2908C1C94D6C500E923D0 /* UISearchBar+RxTests.swift */,
84E4D3951C9B011000ADFDC9 /* UISearchController+RxTests.swift */,
C8C217D41CB7100E0038A2E6 /* UITableView+RxTests.swift */,
C8C217D61CB710200038A2E6 /* UICollectionView+RxTests.swift */,
54700C9E1CE37D1000EF3A8F /* UINavigationItem+RxTests.swift.swift */,
C8F27DAC1CE6710900D5FB4F /* UITextField+RxTests.swift */,
C8F27DB11CE6711600D5FB4F /* UITextView+RxTests.swift */,
844BC8B71CE5023200F5C7CB /* UIPickerView+RxTests.swift */,
914FCD661CCDB82E0058B304 /* UIPageControl+RxTest.swift */,
);
path = RxCocoaTests;
sourceTree = "<group>";
@ -2292,17 +2354,19 @@
C835090C1C38706D0027C24C /* Observable+BlockingTest.swift */,
C835090D1C38706D0027C24C /* Observable+ConcurrencyTest.swift */,
C835090E1C38706D0027C24C /* Observable+CreationTest.swift */,
C83509131C38706D0027C24C /* Observable+MultipleTest.swift */,
C835090F1C38706D0027C24C /* Observable+MultipleTest+CombineLatest.swift */,
C83509101C38706D0027C24C /* Observable+MultipleTest+CombineLatest.tt */,
C83509111C38706D0027C24C /* Observable+MultipleTest+Zip.swift */,
C83509121C38706D0027C24C /* Observable+MultipleTest+Zip.tt */,
C83509131C38706D0027C24C /* Observable+MultipleTest.swift */,
C83509141C38706D0027C24C /* Observable+SingleTest.swift */,
C83509151C38706D0027C24C /* Observable+StandardSequenceOperatorsTest.swift */,
C83509161C38706D0027C24C /* Observable+SubscriptionTest.swift */,
C83509171C38706D0027C24C /* Observable+TimeTest.swift */,
C83509181C38706D0027C24C /* ObserverTests.swift */,
1AF67DA11CED420A00C310FA /* PublishSubjectTest.swift */,
C83509191C38706D0027C24C /* QueueTests.swift */,
1AF67DA51CED430100C310FA /* ReplaySubjectTest.swift */,
C835091A1C38706D0027C24C /* SubjectConcurrencyTest.swift */,
C835091B1C38706D0027C24C /* VariableTest.swift */,
C835091C1C38706D0027C24C /* VirtualSchedulerTest.swift */,
@ -2351,6 +2415,7 @@
C88253F91B8A752B00B02D69 /* Proxies */,
C88254051B8A752B00B02D69 /* UIBarButtonItem+Rx.swift */,
C839365E1C70E02200A9A09E /* UIApplication+Rx.swift */,
271A97401CFC996B00D64125 /* UIViewController+Rx.swift */,
C88254061B8A752B00B02D69 /* UIButton+Rx.swift */,
C88254071B8A752B00B02D69 /* UICollectionView+Rx.swift */,
C88254081B8A752B00B02D69 /* UIControl+Rx.swift */,
@ -2358,6 +2423,7 @@
C882540A1B8A752B00B02D69 /* UIGestureRecognizer+Rx.swift */,
C882540B1B8A752B00B02D69 /* UIImageView+Rx.swift */,
C882540C1B8A752B00B02D69 /* UILabel+Rx.swift */,
91BE429B1CBF7EC000F6B062 /* UIPageControl+Rx.swift */,
AAE623751C82475700FC7801 /* UIProgressView+Rx.swift */,
7F600F3D1C5D0C0100535B1D /* UIRefreshControl+Rx.swift */,
C882540D1B8A752B00B02D69 /* UIScrollView+Rx.swift */,
@ -2375,6 +2441,9 @@
C8BCD3EC1C14B5FB005F1280 /* UIView+Rx.swift */,
7EDBAEB71C89B9B7006CBE67 /* UITabBarItem+Rx.swift */,
84E4D3901C9AFCD500ADFDC9 /* UISearchController+Rx.swift */,
54D2138C1CE081890028D5B4 /* UINavigationItem+Rx.swift */,
844BC8B31CE4FD7500F5C7CB /* UIPickerView+Rx.swift */,
46307D4D1CDE77D800E47A1C /* UIAlertAction+Rx.swift */,
);
path = iOS;
sourceTree = "<group>";
@ -2418,6 +2487,7 @@
84C225A21C33F00B008724EC /* RxTextStorageDelegateProxy.swift */,
8479BC441C3ACDA400FB8B54 /* RxImagePickerDelegateProxy.swift */,
846436E11C9AF64C0035B40D /* RxSearchControllerDelegateProxy.swift */,
844BC8AA1CE4FA5600F5C7CB /* RxPickerViewDelegateProxy.swift */,
);
path = Proxies;
sourceTree = "<group>";
@ -3253,6 +3323,7 @@
C88254321B8A752B00B02D69 /* UISlider+Rx.swift in Sources */,
C8093ED91B8A732E0088E94D /* _RXKVOObserver.m in Sources */,
C882542F1B8A752B00B02D69 /* UIScrollView+Rx.swift in Sources */,
844BC8B41CE4FD7500F5C7CB /* UIPickerView+Rx.swift in Sources */,
C80DDE9B1BCE69BA006A1832 /* Driver+Operators.swift in Sources */,
C8093EE31B8A732E0088E94D /* DelegateProxyType.swift in Sources */,
C8093EFD1B8A732E0088E94D /* RxTarget.swift in Sources */,
@ -3282,19 +3353,26 @@
C8C4B4A91C17722400828BD5 /* _RXObjCRuntime.m in Sources */,
C8093EEF1B8A732E0088E94D /* KVOObserver.swift in Sources */,
C882541F1B8A752B00B02D69 /* RxCollectionViewDelegateProxy.swift in Sources */,
46307D4E1CDE77D800E47A1C /* UIAlertAction+Rx.swift in Sources */,
C88254201B8A752B00B02D69 /* RxScrollViewDelegateProxy.swift in Sources */,
C88F76811CE5341700D5A014 /* RxTextInput.swift in Sources */,
C882542E1B8A752B00B02D69 /* UILabel+Rx.swift in Sources */,
54D2138E1CE0824E0028D5B4 /* UINavigationItem+Rx.swift in Sources */,
91BE429C1CBF7EC000F6B062 /* UIPageControl+Rx.swift in Sources */,
C88254211B8A752B00B02D69 /* RxSearchBarDelegateProxy.swift in Sources */,
C80DDEA71BCE69BA006A1832 /* ObservableConvertibleType+Driver.swift in Sources */,
7EDBAEBC1C89B9B7006CBE67 /* UITabBarItem+Rx.swift in Sources */,
C839365F1C70E02200A9A09E /* UIApplication+Rx.swift in Sources */,
C80DDE9F1BCE69BA006A1832 /* Driver+Subscription.swift in Sources */,
844BC8AC1CE4FA6300F5C7CB /* RxPickerViewDelegateProxy.swift in Sources */,
C811C89D1C24D80100A2DDD4 /* DeallocObservable.swift in Sources */,
C8BCD3ED1C14B5FB005F1280 /* UIView+Rx.swift in Sources */,
C80D338F1B91EF9E0014629D /* Observable+Bind.swift in Sources */,
C88254311B8A752B00B02D69 /* UISegmentedControl+Rx.swift in Sources */,
C8093EED1B8A732E0088E94D /* KVOObservable.swift in Sources */,
D2F461021CD7AC1F00527B4D /* Reactive.swift in Sources */,
AAE623761C82475700FC7801 /* UIProgressView+Rx.swift in Sources */,
271A97411CFC996B00D64125 /* UIViewController+Rx.swift in Sources */,
C8DB968D1BF7595D0084BD53 /* KVORepresentable+Swift.swift in Sources */,
C80DDEB11BCE8CA3006A1832 /* Driver+Operators+arity.swift in Sources */,
C88254281B8A752B00B02D69 /* UIButton+Rx.swift in Sources */,
@ -3343,6 +3421,7 @@
C8093EE41B8A732E0088E94D /* DelegateProxyType.swift in Sources */,
C8093F481B8A732E0088E94D /* NSControl+Rx.swift in Sources */,
C8093F4E1B8A732E0088E94D /* NSTextField+Rx.swift in Sources */,
C88F76821CE5341700D5A014 /* RxTextInput.swift in Sources */,
C8DB967F1BF7496C0084BD53 /* KVORepresentable.swift in Sources */,
C8093EFE1B8A732E0088E94D /* RxTarget.swift in Sources */,
C8093ED21B8A732E0088E94D /* _RX.m in Sources */,
@ -3360,6 +3439,7 @@
C8DB96841BF754C80084BD53 /* NSObject+Rx+KVORepresentable.swift in Sources */,
C8C4B4C31C17727000828BD5 /* MessageSentObserver.swift in Sources */,
C849EF8C1C3195950048AC4A /* Variable+Driver.swift in Sources */,
D2F461031CD7AC2000527B4D /* Reactive.swift in Sources */,
C8093F461B8A732E0088E94D /* NSButton+Rx.swift in Sources */,
C80DDEA01BCE69BA006A1832 /* Driver+Subscription.swift in Sources */,
C8093ED61B8A732E0088E94D /* _RXDelegateProxy.m in Sources */,
@ -3416,13 +3496,16 @@
C835094B1C38706E0027C24C /* AnonymousObservable+Test.swift in Sources */,
C8C217D51CB7100E0038A2E6 /* UITableView+RxTests.swift in Sources */,
C835092E1C38706E0027C24C /* ControlEventTests.swift in Sources */,
844BC8BB1CE5024500F5C7CB /* UIPickerView+RxTests.swift in Sources */,
C83509531C38706E0027C24C /* Observable+AggregateTest.swift in Sources */,
C8B290891C94D64600E923D0 /* RxTest+Controls.swift in Sources */,
C83509291C38706E0027C24C /* PerformanceTools.swift in Sources */,
C835096A1C38706E0027C24C /* TestErrors.swift in Sources */,
1AF67DA61CED430100C310FA /* ReplaySubjectTest.swift in Sources */,
C83509561C38706E0027C24C /* Observable+ConcurrencyTest.swift in Sources */,
C835095A1C38706E0027C24C /* Observable+MultipleTest+Zip.swift in Sources */,
C83509621C38706E0027C24C /* QueueTests.swift in Sources */,
914FCD671CCDB82E0058B304 /* UIPageControl+RxTest.swift in Sources */,
C83509551C38706E0027C24C /* Observable+BlockingTest.swift in Sources */,
C83509351C38706E0027C24C /* KVOObservableTests.swift in Sources */,
C83509421C38706E0027C24C /* MainThreadPrimitiveHotObservable.swift in Sources */,
@ -3436,6 +3519,7 @@
C83509641C38706E0027C24C /* VariableTest.swift in Sources */,
C83509461C38706E0027C24C /* PrimitiveHotObservable.swift in Sources */,
C835097E1C38726E0027C24C /* RxMutableBox.swift in Sources */,
C8F27DC21CE68DAB00D5FB4F /* UITextField+RxTests.swift in Sources */,
C83509311C38706E0027C24C /* DelegateProxyTest+UIKit.swift in Sources */,
C83509671C38706E0027C24C /* Foundation+Extensions.swift in Sources */,
C83509481C38706E0027C24C /* TestConnectableObservable.swift in Sources */,
@ -3443,6 +3527,7 @@
C835095C1C38706E0027C24C /* Observable+MultipleTest.swift in Sources */,
C83509331C38706E0027C24C /* Driver+Extensions.swift in Sources */,
C835094F1C38706E0027C24C /* CurrentThreadSchedulerTest.swift in Sources */,
1AF67DA21CED420A00C310FA /* PublishSubjectTest.swift in Sources */,
C835093E1C38706E0027C24C /* UIView+RxTests.swift in Sources */,
7EDBAEB41C89B1A6006CBE67 /* UITabBarItem+RxTests.swift in Sources */,
C83509411C38706E0027C24C /* BackgroundThreadPrimitiveHotObservable.swift in Sources */,
@ -3457,6 +3542,7 @@
C83509601C38706E0027C24C /* Observable+TimeTest.swift in Sources */,
C835093F1C38706E0027C24C /* ElementIndexPair.swift in Sources */,
C83509381C38706E0027C24C /* NSObject+RxTests.swift in Sources */,
271A97441CFC9F7B00D64125 /* UIViewControler+RxTests.swift in Sources */,
C83509631C38706E0027C24C /* SubjectConcurrencyTest.swift in Sources */,
84E4D3961C9B011000ADFDC9 /* UISearchController+RxTests.swift in Sources */,
C83509511C38706E0027C24C /* HistoricalSchedulerTest.swift in Sources */,
@ -3464,6 +3550,7 @@
C83509541C38706E0027C24C /* Observable+BindingTest.swift in Sources */,
C83509521C38706E0027C24C /* MainSchedulerTests.swift in Sources */,
C835094D1C38706E0027C24C /* BagTest.swift in Sources */,
54700CA01CE37E1800EF3A8F /* UINavigationItem+RxTests.swift.swift in Sources */,
C835093D1C38706E0027C24C /* SentMessageTest.swift in Sources */,
C83509401C38706E0027C24C /* EquatableArray.swift in Sources */,
C835095D1C38706E0027C24C /* Observable+SingleTest.swift in Sources */,
@ -3478,6 +3565,7 @@
C835092C1C38706E0027C24C /* Control+RxTests+UIKit.swift in Sources */,
C83509571C38706E0027C24C /* Observable+CreationTest.swift in Sources */,
C83509321C38706E0027C24C /* DelegateProxyTest.swift in Sources */,
C8F27DC01CE68DA600D5FB4F /* UITextView+RxTests.swift in Sources */,
C860EC951C42E25E00A664B3 /* SectionedViewDataSourceMock.swift in Sources */,
C83509431C38706E0027C24C /* MockDisposable.swift in Sources */,
);
@ -3498,6 +3586,7 @@
C8350A181C38756A0027C24C /* VariableTest.swift in Sources */,
C83509EF1C3875580027C24C /* PrimitiveHotObservable.swift in Sources */,
C83509FB1C38755D0027C24C /* Observable+BindingTest.swift in Sources */,
54700CA11CE37E1900EF3A8F /* UINavigationItem+RxTests.swift.swift in Sources */,
C8350A191C38756A0027C24C /* VirtualSchedulerTest.swift in Sources */,
C83509BF1C3875220027C24C /* DelegateProxyTest+UIKit.swift in Sources */,
C83509D41C38753C0027C24C /* RxObjCRuntimeState.swift in Sources */,
@ -3509,10 +3598,12 @@
C83509C41C3875220027C24C /* NSLayoutConstraint+RxTests.swift in Sources */,
C83509BC1C38750D0027C24C /* ControlEventTests.swift in Sources */,
C83509EC1C3875580027C24C /* MockDisposable.swift in Sources */,
1AF67DA31CED427D00C310FA /* PublishSubjectTest.swift in Sources */,
C8350A111C38756A0027C24C /* Observable+SingleTest.swift in Sources */,
C8350A2A1C3875B50027C24C /* RxMutableBox.swift in Sources */,
C8350A151C38756A0027C24C /* ObserverTests.swift in Sources */,
C83509B11C3874E20027C24C /* PerformanceTools.swift in Sources */,
1AF67DA71CED430100C310FA /* ReplaySubjectTest.swift in Sources */,
C83509AE1C3874D80027C24C /* Foundation+Extensions.swift in Sources */,
C83509F11C3875580027C24C /* TestConnectableObservable.swift in Sources */,
C83509AA1C3874D20027C24C /* TestErrors.swift in Sources */,
@ -3525,6 +3616,7 @@
8476A0221C3D5DC60040BA22 /* UIImagePickerController+RxTests.swift in Sources */,
C8350A161C38756A0027C24C /* QueueTests.swift in Sources */,
C83509C51C3875220027C24C /* NSNotificationCenterTests.swift in Sources */,
C8F27DC31CE68DAC00D5FB4F /* UITextField+RxTests.swift in Sources */,
C83509FF1C38755D0027C24C /* Observable+MultipleTest+CombineLatest.swift in Sources */,
C83509D81C3875420027C24C /* SentMessageTest.swift in Sources */,
C83509F61C38755D0027C24C /* CurrentThreadSchedulerTest.swift in Sources */,
@ -3533,6 +3625,7 @@
C83509C31C3875220027C24C /* KVOObservableTests.swift in Sources */,
C83509F91C38755D0027C24C /* MainSchedulerTests.swift in Sources */,
7EDBAEC31C89BCB9006CBE67 /* UITabBarItem+RxTests.swift in Sources */,
914FCD681CCDB82E0058B304 /* UIPageControl+RxTest.swift in Sources */,
C83509AB1C3874D20027C24C /* XCTest+AllTests.swift in Sources */,
C83509C11C3875220027C24C /* Driver+Extensions.swift in Sources */,
C83509DD1C38754C0027C24C /* EquatableArray.swift in Sources */,
@ -3552,6 +3645,7 @@
C83509D01C38752E0027C24C /* RuntimeStateSnapshot.swift in Sources */,
C83509AF1C3874DC0027C24C /* RxTest.swift in Sources */,
C83509F41C38755D0027C24C /* BagTest.swift in Sources */,
C8F27DC11CE68DA700D5FB4F /* UITextView+RxTests.swift in Sources */,
C860EC961C42E26100A664B3 /* SectionedViewDataSourceMock.swift in Sources */,
C8350A0F1C3875630027C24C /* Observable+MultipleTest+Zip.swift in Sources */,
);
@ -3562,6 +3656,7 @@
buildActionMask = 2147483647;
files = (
C8350A201C38756B0027C24C /* QueueTests.swift in Sources */,
1AF67DA41CED427D00C310FA /* PublishSubjectTest.swift in Sources */,
C83509E71C3875580027C24C /* PrimitiveHotObservable.swift in Sources */,
C83509CE1C3875230027C24C /* NSObject+RxTests.swift in Sources */,
C8350A011C38755E0027C24C /* AssumptionsTest.swift in Sources */,
@ -3605,6 +3700,7 @@
C83509CA1C3875230027C24C /* Driver+Test.swift in Sources */,
C83509E41C3875580027C24C /* MockDisposable.swift in Sources */,
C83509D51C38753E0027C24C /* RxObjCRuntimeState.swift in Sources */,
1AF67DA81CED430100C310FA /* ReplaySubjectTest.swift in Sources */,
C8350A001C38755E0027C24C /* AnonymousObservable+Test.swift in Sources */,
C83509E91C3875580027C24C /* TestConnectableObservable.swift in Sources */,
C83509DF1C38754F0027C24C /* TestVirtualScheduler.swift in Sources */,
@ -4183,6 +4279,8 @@
C8F0C0161BBBFBB9001B112F /* UITableView+Rx.swift in Sources */,
84E4D3941C9AFD3600ADFDC9 /* UISearchController+Rx.swift in Sources */,
C8F0C0171BBBFBB9001B112F /* RxCollectionViewReactiveArrayDataSource.swift in Sources */,
844BC8B61CE4FD7500F5C7CB /* UIPickerView+Rx.swift in Sources */,
54D213931CE08DDB0028D5B4 /* UINavigationItem+Rx.swift in Sources */,
C8C4B4AC1C17722400828BD5 /* _RXObjCRuntime.m in Sources */,
C8F0C0181BBBFBB9001B112F /* KVOObserver.swift in Sources */,
C8F0C01A1BBBFBB9001B112F /* RxCollectionViewDelegateProxy.swift in Sources */,
@ -4211,6 +4309,7 @@
C8F0C02B1BBBFBB9001B112F /* ControlProperty.swift in Sources */,
C8F0C02C1BBBFBB9001B112F /* UIDatePicker+Rx.swift in Sources */,
C8F0C02D1BBBFBB9001B112F /* RxTableViewDataSourceProxy.swift in Sources */,
844BC8AE1CE4FA6600F5C7CB /* RxPickerViewDelegateProxy.swift in Sources */,
C8BCD3F71C14B6D1005F1280 /* NSLayoutConstraint+Rx.swift in Sources */,
C8F0C0301BBBFBB9001B112F /* UIGestureRecognizer+Rx.swift in Sources */,
C8F0C0311BBBFBB9001B112F /* DelegateProxy.swift in Sources */,
@ -4221,11 +4320,13 @@
C8F0C0341BBBFBB9001B112F /* Logging.swift in Sources */,
C8F0C0351BBBFBB9001B112F /* UICollectionView+Rx.swift in Sources */,
C8F0C0361BBBFBB9001B112F /* RxCollectionViewDataSourceType.swift in Sources */,
91BE429F1CBF7F3D00F6B062 /* UIPageControl+Rx.swift in Sources */,
846436E61C9AF6670035B40D /* RxSearchControllerDelegateProxy.swift in Sources */,
C8F0C0371BBBFBB9001B112F /* NSNotificationCenter+Rx.swift in Sources */,
C8F0C0381BBBFBB9001B112F /* UITextField+Rx.swift in Sources */,
C8F0C0391BBBFBB9001B112F /* NSURLSession+Rx.swift in Sources */,
C8F0C03A1BBBFBB9001B112F /* ControlTarget.swift in Sources */,
C88F76841CE5341700D5A014 /* RxTextInput.swift in Sources */,
C8F0C03B1BBBFBB9001B112F /* UISearchBar+Rx.swift in Sources */,
C8F0C03C1BBBFBB9001B112F /* ItemEvents.swift in Sources */,
7EDBAEBF1C89B9B7006CBE67 /* UITabBarItem+Rx.swift in Sources */,
@ -4260,6 +4361,7 @@
D203C5081BB9C53E00D02D00 /* UIGestureRecognizer+Rx.swift in Sources */,
D2138C931BB9BEDA00339B5C /* NSNotificationCenter+Rx.swift in Sources */,
C80DDE991BCE69BA006A1832 /* ControlProperty+Driver.swift in Sources */,
844BC8B51CE4FD7500F5C7CB /* UIPickerView+Rx.swift in Sources */,
C80DDE951BCE69BA006A1832 /* ControlEvent+Driver.swift in Sources */,
D203C4F61BB9C52E00D02D00 /* RxCollectionViewDataSourceType.swift in Sources */,
84C225A41C33F00B008724EC /* RxTextStorageDelegateProxy.swift in Sources */,
@ -4287,10 +4389,14 @@
C80DDEA91BCE69BA006A1832 /* ObservableConvertibleType+Driver.swift in Sources */,
C80DDEA11BCE69BA006A1832 /* Driver+Subscription.swift in Sources */,
D2138C891BB9BEBE00339B5C /* DelegateProxyType.swift in Sources */,
C88F76831CE5341700D5A014 /* RxTextInput.swift in Sources */,
C811C89F1C24D80100A2DDD4 /* DeallocObservable.swift in Sources */,
54D213921CE08D0C0028D5B4 /* UINavigationItem+Rx.swift in Sources */,
D2F461041CD7AC2100527B4D /* Reactive.swift in Sources */,
C8BCD3EF1C14B5FB005F1280 /* UIView+Rx.swift in Sources */,
D2138C921BB9BED600339B5C /* KVOObserver.swift in Sources */,
D2138C831BB9BEBE00339B5C /* _RXKVOObserver.m in Sources */,
46307D4F1CDE77D800E47A1C /* UIAlertAction+Rx.swift in Sources */,
C80DDEB31BCE8CA3006A1832 /* Driver+Operators+arity.swift in Sources */,
C8DB968F1BF7595D0084BD53 /* KVORepresentable+Swift.swift in Sources */,
D203C5061BB9C53E00D02D00 /* UIControl+Rx.swift in Sources */,
@ -4313,8 +4419,10 @@
D203C4FA1BB9C53700D02D00 /* RxCollectionViewDataSourceProxy.swift in Sources */,
D2138C7F1BB9BEBE00339B5C /* _RX.m in Sources */,
8479BC4D1C3ACF6E00FB8B54 /* UIImagePickerController+Rx.swift in Sources */,
91BE429D1CBF7EC000F6B062 /* UIPageControl+Rx.swift in Sources */,
D203C4FE1BB9C53700D02D00 /* RxTableViewDataSourceProxy.swift in Sources */,
D203C5001BB9C53700D02D00 /* RxTextViewDelegateProxy.swift in Sources */,
844BC8AD1CE4FA6400F5C7CB /* RxPickerViewDelegateProxy.swift in Sources */,
C8DB96851BF754C80084BD53 /* NSObject+Rx+KVORepresentable.swift in Sources */,
D203C5091BB9C53E00D02D00 /* UIImageView+Rx.swift in Sources */,
D2138C871BB9BEBE00339B5C /* CLLocationManager+Rx.swift in Sources */,

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "RxBlocking"
s.version = "2.4"
s.version = "2.5.0"
s.summary = "RxSwift Blocking operatos"
s.description = <<-DESC
Set of blocking operators for RxSwift. These operators are mostly intended for unit/integration tests
@ -24,5 +24,5 @@ Waiting for observable sequence to complete before exiting command line applicat
s.source_files = 'RxBlocking/**/*.swift'
s.dependency 'RxSwift', '~> 2.4'
s.dependency 'RxSwift', '~> 2.5'
end

Binary file not shown.

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "RxCocoa"
s.version = "2.4"
s.version = "2.5.0"
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.4'
s.dependency 'RxSwift', '~> 2.5'
end

View File

@ -20,7 +20,7 @@ extension CLLocationManager {
For more information take a look at `DelegateProxyType` protocol documentation.
*/
public var rx_delegate: DelegateProxy {
return proxyForObject(RxCLLocationManagerDelegateProxy.self, self)
return RxCLLocationManagerDelegateProxy.proxyForObject(self)
}
// MARK: Responding to Location Events

View File

@ -187,7 +187,7 @@ extension Driver where Element: SignedInteger {
/**
This method can be used in unit tests to ensure that driver is using mock schedulers instead of
maind schedulers.
main schedulers.
**This shouldn't be used in normal release builds.**
*/

View File

@ -45,7 +45,43 @@ public class DelegateProxy : _RXDelegateProxy {
/**
Returns observable sequence of invocations of delegate methods.
Only methods that have `void` return value can be observed using this method because
those methods are used as a notification mechanism. It doesn't matter if they are optional
or not. Observing is performed by installing a hidden associated `PublishSubject` that is
used to dispatch messages to observers.
Delegate methods that have non `void` return value can't be observed directly using this method
because:
* those methods are not intended to be used as a notification mechanism, but as a behavior customization mechanism
* there is no sensible automatic way to determine a default return value
In case observing of delegate methods that have return type is required, it can be done by
manually installing a `PublishSubject` or `BehaviorSubject` and implementing delegate method.
e.g.
// delegate proxy part (RxScrollViewDelegateProxy)
let internalSubject = PublishSubject<CGPoint>
public func requiredDelegateMethod(scrollView: UIScrollView, arg1: CGPoint) -> Bool {
internalSubject.on(.Next(arg1))
return self._forwardToDelegate?.requiredDelegateMethod?(scrollView, arg1: arg1) ?? defaultReturnValue
}
....
// reactive property implementation in a real class (`UIScrollView`)
public var rx_property: Observable<CGPoint> {
let proxy = RxScrollViewDelegateProxy.proxyForObject(self)
return proxy.internalSubject.asObservable()
}
**In case calling this method prints "Delegate proxy is already implementing `\(selector)`,
a more performant way of registering might exist.", that means that manual observing method
is required analog to the example above because delegate method has already been implemented.**
- parameter selector: Selector used to filter observed invocations of delegate methods.
- returns: Observable sequence of arguments passed to `selector` method.
*/

View File

@ -68,9 +68,9 @@ every view has a corresponding delegate virtual factory method.
In case of UITableView / UIScrollView, there is
extensions UIScrollView {
extension UIScrollView {
public func rx_createDelegateProxy() -> RxScrollViewDelegateProxy {
return RxScrollViewDelegateProxy(view: self)
return RxScrollViewDelegateProxy(parentObject: self)
}
....
@ -147,91 +147,113 @@ public protocol DelegateProxyType : AnyObject {
func setForwardToDelegate(forwardToDelegate: AnyObject?, retainDelegate: Bool)
}
/**
Returns existing proxy for object or installs new instance of delegate proxy.
- parameter object: Target object on which to install delegate proxy.
- returns: Installed instance of delegate proxy.
extension UISearchBar {
public var rx_delegate: DelegateProxy {
return proxyForObject(RxSearchBarDelegateProxy.self, self)
}
public var rx_text: ControlProperty<String> {
let source: Observable<String> = self.rx_delegate.observe("searchBar:textDidChange:")
...
}
}
*/
public func proxyForObject<P: DelegateProxyType>(_ type: P.Type, _ object: AnyObject) -> P {
MainScheduler.ensureExecutingOnScheduler()
let maybeProxy = P.assignedProxyFor(object: object) as? P
let proxy: P
if maybeProxy == nil {
proxy = P.createProxyForObject(object: object) as! P
P.assignProxy(proxy: proxy, toObject: object)
assert(P.assignedProxyFor(object: object) === proxy)
}
else {
proxy = maybeProxy!
}
let currentDelegate: AnyObject? = P.currentDelegateFor(object: object)
if currentDelegate !== proxy {
proxy.setForwardToDelegate(forwardToDelegate: currentDelegate, retainDelegate: false)
P.setCurrentDelegate(delegate: proxy, toObject: object)
assert(P.currentDelegateFor(object: object) === proxy)
assert(proxy.forwardToDelegate() === currentDelegate)
}
return proxy
@available(*, deprecated=2.5, renamed="DelegateProxyType.proxyForObject", message="You can just use normal static protocol extension. E.g. `RxScrollViewDelegateProxy.proxyForObject`")
public func proxyForObject<P: DelegateProxyType>(type: P.Type, _ object: AnyObject) -> P {
return P.proxyForObject(object)
}
func installDelegate<P: DelegateProxyType>(proxy: P, delegate: AnyObject, retainDelegate: Bool, onProxyForObject object: AnyObject) -> Disposable {
weak var weakDelegate: AnyObject? = delegate
assert(proxy.forwardToDelegate() === nil, "There is already a delegate set -> `\(proxy.forwardToDelegate())` for object -> `\(object)`.\nMaybe delegate was already set in `xib` or `storyboard` and now it's being overwritten in code.")
proxy.setForwardToDelegate(forwardToDelegate: delegate, retainDelegate: retainDelegate)
// refresh properties after delegate is set
// some views like UITableView cache `respondsToSelector`
P.setCurrentDelegate(delegate: nil, toObject: object)
P.setCurrentDelegate(delegate: proxy, toObject: object)
assert(proxy.forwardToDelegate() === delegate, "Setting of delegate failed")
return AnonymousDisposable {
extension DelegateProxyType {
/**
Returns existing proxy for object or installs new instance of delegate proxy.
- parameter object: Target object on which to install delegate proxy.
- returns: Installed instance of delegate proxy.
extension UISearchBar {
public var rx_delegate: DelegateProxy {
return RxSearchBarDelegateProxy.proxyForObject(self)
}
public var rx_text: ControlProperty<String> {
let source: Observable<String> = self.rx_delegate.observe(#selector(UISearchBarDelegate.searchBar(_:textDidChange:)))
...
}
}
*/
public static func proxyForObject(object: AnyObject) -> Self {
MainScheduler.ensureExecutingOnScheduler()
let maybeProxy = Self.assignedProxyFor(object) as? Self
let proxy: Self
if maybeProxy == nil {
proxy = Self.createProxyForObject(object) as! Self
Self.assignProxy(proxy, toObject: object)
assert(Self.assignedProxyFor(object) === proxy)
}
else {
proxy = maybeProxy!
}
let currentDelegate: AnyObject? = Self.currentDelegateFor(object)
if currentDelegate !== proxy {
proxy.setForwardToDelegate(currentDelegate, retainDelegate: false)
Self.setCurrentDelegate(proxy, toObject: object)
assert(Self.currentDelegateFor(object) === proxy)
assert(proxy.forwardToDelegate() === currentDelegate)
}
let delegate: AnyObject? = weakDelegate
return proxy
}
/**
Sets forward delegate for `DelegateProxyType` associated with a specific object and return disposable that can be used to unset the forward to delegate.
Using this method will also make sure that potential original object cached selectors are cleared and will report any accidental forward delegate mutations.
- parameter forwardDelegate: Delegate object to set.
- parameter retainDelegate: Retain `forwardDelegate` while it's being set.
- parameter onProxyForObject: Object that has `delegate` property.
- returns: Disposable object that can be used to clear forward delegate.
*/
public static func installForwardDelegate(forwardDelegate: AnyObject, retainDelegate: Bool, onProxyForObject object: AnyObject) -> Disposable {
weak var weakForwardDelegate: AnyObject? = forwardDelegate
let proxy = Self.proxyForObject(object)
assert(delegate == nil || proxy.forwardToDelegate() === delegate, "Delegate was changed from time it was first set. Current \(proxy.forwardToDelegate()), and it should have been \(proxy)")
assert(proxy.forwardToDelegate() === nil, "This is a feature to warn you that there is already a delegate (or data source) set somewhere previously. The action you are trying to perform will clear that delegate (data source) and that means that some of your features that depend on that delegate (data source) being set will likely stop working.\n" +
"If you are ok with this, try to set delegate (data source) to `nil` in front of this operation.\n" +
" This is the source object value: \(object)\n" +
" This this the original delegate (data source) value: \(proxy.forwardToDelegate()!)\n" +
"Hint: Maybe delegate was already set in xib or storyboard and now it's being overwritten in code.\n")
proxy.setForwardToDelegate(forwardDelegate, retainDelegate: retainDelegate)
proxy.setForwardToDelegate(forwardToDelegate: nil, retainDelegate: retainDelegate)
// refresh properties after delegate is set
// some views like UITableView cache `respondsToSelector`
Self.setCurrentDelegate(nil, toObject: object)
Self.setCurrentDelegate(proxy, toObject: object)
assert(proxy.forwardToDelegate() === forwardDelegate, "Setting of delegate failed")
return AnonymousDisposable {
MainScheduler.ensureExecutingOnScheduler()
let delegate: AnyObject? = weakForwardDelegate
assert(delegate == nil || proxy.forwardToDelegate() === delegate, "Delegate was changed from time it was first set. Current \(proxy.forwardToDelegate()), and it should have been \(proxy)")
proxy.setForwardToDelegate(nil, retainDelegate: retainDelegate)
}
}
}
extension ObservableType {
func subscribeProxyDataSourceForObject<P: DelegateProxyType>(object: AnyObject, dataSource: AnyObject, retainDataSource: Bool, binding: (P, Event<E>) -> Void)
-> Disposable {
let proxy = proxyForObject(P.self, object)
let disposable = installDelegate(proxy: proxy, delegate: dataSource, retainDelegate: retainDataSource, onProxyForObject: object)
let proxy = P.proxyForObject(object)
let disposable = P.installForwardDelegate(dataSource, retainDelegate: retainDataSource, onProxyForObject: object)
let subscription = self.asObservable()
// source can't ever end, otherwise it will release the subscriber
.concat(second: Observable.never())
// source can never end, otherwise it would release the subscriber
.concat(Observable.never())
.subscribe { [weak object] (event: Event<E>) in
MainScheduler.ensureExecutingOnScheduler()
if let object = object {
assert(proxy === P.currentDelegateFor(object: object), "Proxy changed from the time it was first set.\nOriginal: \(proxy)\nExisting: \(P.currentDelegateFor(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)

View File

@ -42,9 +42,7 @@ class KVOObservable<Element>
observer.on(event: .Next(value as? Element))
}
return AnonymousDisposable {
observer.dispose()
}
return AnonymousDisposable(observer.dispose)
}
}

View File

@ -155,9 +155,7 @@ extension NSURLSession {
let t = task
t.resume()
return AnonymousDisposable {
task.cancel()
}
return AnonymousDisposable(task.cancel)
}
}

View File

@ -0,0 +1,33 @@
//
// Reactive.swift
// Rx
//
// Created by Yury Korolev on 5/2/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
/**
We can use `Reactive` protocol as customization point for constrained protocol extensions.
General pattern would be:
// 1. Conform SomeType to Reactive protocol
extension SomeType: Reactive {}
// 2. Extend Reactive protocol with constrain on Self
// Read as: Reactive Extension where Self is a SomeType
extension Reactive where Self: SomeType {
// 3. Put any specific reactive extension for SomeType here
}
With this approach we can have more specialized methods and properties using
`Self` and not just specialized on common base type.
See UIGestureRecognizer+Rx.swift as an example
*/
public protocol Reactive {
}

View File

@ -0,0 +1,39 @@
//
// RxTextInput.swift
// Rx
//
// Created by Krunoslav Zaher on 5/12/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import Foundation
#if os(iOS) || os(tvOS)
import UIKit
/**
Represents text input with reactive extensions.
*/
public protocol RxTextInput : UITextInput {
/**
Reactive wrapper for `text` property.
*/
var rx_text: ControlProperty<String> { get }
}
#endif
#if os(OSX)
import Cocoa
/**
Represents text input with reactive extensions.
*/
public protocol RxTextInput : NSTextInput {
/**
Reactive wrapper for `text` property.
*/
var rx_text: ControlProperty<String> { get }
}
#endif

Binary file not shown.

View File

@ -76,7 +76,7 @@ public class RxTextFieldDelegateProxy
}
extension NSTextField {
extension NSTextField : RxTextInput {
/**
Factory method that enables subclasses to implement their own `rx_delegate`.
@ -93,14 +93,14 @@ extension NSTextField {
For more information take a look at `DelegateProxyType` protocol documentation.
*/
public var rx_delegate: DelegateProxy {
return proxyForObject(RxTextFieldDelegateProxy.self, self)
return RxTextFieldDelegateProxy.proxyForObject(self)
}
/**
Reactive wrapper for `text` property.
*/
public var rx_text: ControlProperty<String> {
let delegate = proxyForObject(RxTextFieldDelegateProxy.self, self)
let delegate = RxTextFieldDelegateProxy.proxyForObject(self)
let source = Observable.deferred { [weak self] in
delegate.textSubject.startWith(self?.stringValue ?? "")

View File

@ -22,7 +22,7 @@ extension NSTextStorage {
For more information take a look at `DelegateProxyType` protocol documentation.
*/
public var rx_delegate:DelegateProxy {
return proxyForObject(RxTextStorageDelegateProxy.self, self)
return RxTextStorageDelegateProxy.proxyForObject(self)
}
/**

View File

@ -0,0 +1,38 @@
//
// RxPickerViewDelegateProxy.swift
// Rx
//
// Created by Segii Shulga on 5/12/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import Foundation
#if !RX_NO_MODULE
import RxSwift
#endif
import UIKit
public class RxPickerViewDelegateProxy
: DelegateProxy
, DelegateProxyType
, UIPickerViewDelegate {
/**
For more information take a look at `DelegateProxyType`.
*/
public class func setCurrentDelegate(delegate: AnyObject?, toObject object: AnyObject) {
let pickerView: UIPickerView = castOrFatalError(object)
pickerView.delegate = castOptionalOrFatalError(delegate)
}
/**
For more information take a look at `DelegateProxyType`.
*/
public class func currentDelegateFor(object: AnyObject) -> AnyObject? {
let pickerView: UIPickerView = castOrFatalError(object)
return pickerView.delegate
}
}
#endif

View File

@ -17,6 +17,7 @@
/**
For more information take a look at `DelegateProxyType`.
*/
@available(iOS 8.0, *)
public class RxSearchControllerDelegateProxy
: DelegateProxy
, DelegateProxyType

View File

@ -0,0 +1,33 @@
//
// UIAlertAction+Rx.swift
// Rx
//
// Created by Andrew Breckenridge on 5/7/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import Foundation
#if os(iOS) || os(tvOS)
import Foundation
import UIKit
#if !RX_NO_MODULE
import RxSwift
#endif
extension UIAlertAction {
/**
Bindable sink for `enabled` property.
*/
public var rx_enabled: AnyObserver<Bool> {
return UIBindingObserver(UIElement: self) { alertAction, value in
alertAction.enabled = value
}.asObserver()
}
}
#endif

View File

@ -25,9 +25,9 @@ extension UICollectionView {
- parameter cellFactory: Transform between sequence elements and view cells.
- returns: Disposable object that can be used to unbind.
*/
public func rx_itemsWithCellFactory<S: Sequence, O: ObservableType where O.E == S>
public func rx_itemsWithCellFactory<S: SequenceType, O: ObservableType where O.E == S>
(source: O)
-> (cellFactory: (UICollectionView, Int, S.Iterator.Element) -> UICollectionViewCell)
-> (cellFactory: (UICollectionView, Int, S.Generator.Element) -> UICollectionViewCell)
-> Disposable {
return { cellFactory in
let dataSource = RxCollectionViewReactiveArrayDataSourceSequenceWrapper<S>(cellFactory: cellFactory)
@ -45,10 +45,10 @@ extension UICollectionView {
- parameter cellType: Type of table view cell.
- returns: Disposable object that can be used to unbind.
*/
public func rx_itemsWithCellIdentifier<S: Sequence, Cell: UICollectionViewCell, O : ObservableType where O.E == S>
public func rx_itemsWithCellIdentifier<S: SequenceType, Cell: UICollectionViewCell, O : ObservableType where O.E == S>
(cellIdentifier: String, cellType: Cell.Type = Cell.self)
-> (source: O)
-> (configureCell: (Int, S.Iterator.Element, Cell) -> Void)
-> (configureCell: (Int, S.Generator.Element, Cell) -> Void)
-> Disposable {
return { source in
return { configureCell in
@ -71,7 +71,10 @@ extension UICollectionView {
- parameter source: Observable sequence of items.
- returns: Disposable object that can be used to unbind.
*/
public func rx_itemsWithDataSource<DataSource: protocol<RxCollectionViewDataSourceType, UICollectionViewDataSource>, S: Sequence, O: ObservableType where DataSource.Element == S, O.E == S>
public func rx_itemsWithDataSource<
DataSource: protocol<RxCollectionViewDataSourceType, UICollectionViewDataSource>,
O: ObservableType where DataSource.Element == O.E
>
(dataSource: DataSource)
-> (source: O)
-> Disposable {
@ -112,7 +115,7 @@ extension UICollectionView {
For more information take a look at `DelegateProxyType` protocol documentation.
*/
public var rx_dataSource: DelegateProxy {
return proxyForObject(RxCollectionViewDataSourceProxy.self, self)
return RxCollectionViewDataSourceProxy.proxyForObject(self)
}
/**
@ -125,8 +128,7 @@ extension UICollectionView {
*/
public func rx_setDataSource(dataSource: UICollectionViewDataSource)
-> Disposable {
let proxy = proxyForObject(RxCollectionViewDataSourceProxy.self, self)
return installDelegate(proxy, delegate: dataSource, retainDelegate: false, onProxyForObject: self)
return RxCollectionViewDataSourceProxy.installForwardDelegate(dataSource, retainDelegate: false, onProxyForObject: self)
}
/**

View File

@ -20,7 +20,7 @@ extension UIControl {
Bindable sink for `enabled` property.
*/
public var rx_enabled: AnyObserver<Bool> {
return UIBindingObserver<UIControl, Bool>(UIElement: self) { control, value in
return UIBindingObserver(UIElement: self) { control, value in
control.enabled = value
}.asObserver()
}
@ -29,7 +29,7 @@ extension UIControl {
Bindable sink for `selected` property.
*/
public var rx_selected: AnyObserver<Bool> {
return UIBindingObserver<UIControl, Bool>(UIElement: self) { control, selected in
return UIBindingObserver(UIElement: self) { control, selected in
control.selected = selected
}.asObserver()
}
@ -44,19 +44,17 @@ extension UIControl {
MainScheduler.ensureExecutingOnScheduler()
guard let control = self else {
observer.on(event: .Completed)
observer.on(.Completed)
return NopDisposable.instance
}
let controlTarget = ControlTarget(control: control, controlEvents: controlEvents) {
control in
observer.on(event: .Next())
observer.on(.Next())
}
return AnonymousDisposable {
controlTarget.dispose()
}
}.takeUntil(other: rx_deallocated)
return AnonymousDisposable(controlTarget.dispose)
}.takeUntil(rx_deallocated)
return ControlEvent(events: source)
}
@ -68,23 +66,21 @@ extension UIControl {
static func rx_value<C: AnyObject, T: Equatable>(control: C, getter: (C) -> T, setter: (C, T) -> Void) -> ControlProperty<T> {
let source: Observable<T> = Observable.create { [weak weakControl = control] observer in
guard let control = weakControl else {
observer.on(event: .Completed)
observer.on(.Completed)
return NopDisposable.instance
}
observer.on(event: .Next(getter(control)))
observer.on(.Next(getter(control)))
let controlTarget = ControlTarget(control: control as! UIControl, controlEvents: [.allEditingEvents, .valueChanged]) { _ in
let controlTarget = ControlTarget(control: control as! UIControl, controlEvents: [.AllEditingEvents, .ValueChanged]) { _ in
if let control = weakControl {
observer.on(event: .Next(getter(control)))
observer.on(.Next(getter(control)))
}
}
return AnonymousDisposable {
controlTarget.dispose()
}
return AnonymousDisposable(controlTarget.dispose)
}
.takeUntil(other: (control as! NSObject).rx_deallocated)
.takeUntil((control as! NSObject).rx_deallocated)
let bindingObserver = UIBindingObserver(UIElement: control, binding: setter)

View File

@ -15,15 +15,15 @@ import RxSwift
// This should be only used from `MainScheduler`
class GestureTarget: RxTarget {
typealias Callback = (UIGestureRecognizer) -> Void
class GestureTarget<Recognizer: UIGestureRecognizer>: RxTarget {
typealias Callback = (Recognizer) -> Void
let selector = #selector(ControlTarget.eventHandler(_:))
weak var gestureRecognizer: UIGestureRecognizer?
weak var gestureRecognizer: Recognizer?
var callback: Callback?
init(_ gestureRecognizer: UIGestureRecognizer, callback: Callback) {
init(_ gestureRecognizer: Recognizer, callback: Callback) {
self.gestureRecognizer = gestureRecognizer
self.callback = callback
@ -51,13 +51,15 @@ class GestureTarget: RxTarget {
}
}
extension UIGestureRecognizer {
extension UIGestureRecognizer: Reactive { }
extension Reactive where Self: UIGestureRecognizer {
/**
Reactive wrapper for gesture recognizer events.
*/
public var rx_event: ControlEvent<UIGestureRecognizer> {
let source: Observable<UIGestureRecognizer> = Observable.create { [weak self] observer in
public var rx_event: ControlEvent<Self> {
let source: Observable<Self> = Observable.create { [weak self] observer in
MainScheduler.ensureExecutingOnScheduler()
guard let control = self else {

View File

@ -24,7 +24,7 @@ import Foundation
For more information take a look at `DelegateProxyType` protocol documentation.
*/
public var rx_delegate: DelegateProxy {
return proxyForObject(RxImagePickerDelegateProxy.self, self)
return RxImagePickerDelegateProxy.proxyForObject(self)
}
/**

View File

@ -0,0 +1,29 @@
//
// UINavigationItem+Rx.swift
// Rx
//
// Created by kumapo on 2016/05/09.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import Foundation
import UIKit
#if !RX_NO_MODULE
import RxSwift
#endif
extension UINavigationItem {
/**
Bindable sink for `title` property.
*/
public var rx_title: AnyObserver<String?> {
return UIBindingObserver(UIElement: self) { navigationItem, text in
navigationItem.title = text
}.asObserver()
}
}
#endif

View File

@ -0,0 +1,29 @@
//
// UIPageControl+Rx.swift
// Rx
//
// Created by Francesco Puntillo on 14/04/2016.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import Foundation
#if !RX_NO_MODULE
import RxSwift
#endif
import UIKit
extension UIPageControl {
/**
Bindable sink for `currentPage` property.
*/
public var rx_currentPage: AnyObserver<Int> {
return UIBindingObserver(UIElement: self) { controller, page in
controller.currentPage = page
}.asObserver()
}
}
#endif

View File

@ -0,0 +1,36 @@
//
// UIPickerView+Rx.swift
// Rx
//
// Created by Segii Shulga on 5/12/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
#if os(iOS)
import Foundation
#if !RX_NO_MODULE
import RxSwift
#endif
import UIKit
extension UIPickerView {
/**
Reactive wrapper for `delegate`.
For more information take a look at `DelegateProxyType` protocol documentation.
*/
public var rx_delegate: DelegateProxy {
return RxPickerViewDelegateProxy.proxyForObject(self)
}
public var rx_itemSelected: ControlEvent<(row: Int, component: Int)> {
let source = rx_delegate
.observe(#selector(UIPickerViewDelegate.pickerView(_:didSelectRow:inComponent:)))
.map {
return (try castOrThrow(Int.self, $0[1]), try castOrThrow(Int.self, $0[2]))
}
return ControlEvent(events: source)
}
}
#endif

View File

@ -31,14 +31,14 @@ extension UIScrollView {
For more information take a look at `DelegateProxyType` protocol documentation.
*/
public var rx_delegate: DelegateProxy {
return proxyForObject(RxScrollViewDelegateProxy.self, self)
return RxScrollViewDelegateProxy.proxyForObject(self)
}
/**
Reactive wrapper for `contentOffset`.
*/
public var rx_contentOffset: ControlProperty<CGPoint> {
let proxy = proxyForObject(RxScrollViewDelegateProxy.self, self)
let proxy = RxScrollViewDelegateProxy.proxyForObject(self)
let bindingObserver = UIBindingObserver(UIElement: self) { scrollView, contentOffset in
scrollView.contentOffset = contentOffset
@ -57,8 +57,7 @@ extension UIScrollView {
*/
public func rx_setDelegate(delegate: UIScrollViewDelegate)
-> Disposable {
let proxy = proxyForObject(RxScrollViewDelegateProxy.self, self)
return installDelegate(proxy: proxy, delegate: delegate, retainDelegate: false, onProxyForObject: self)
return RxScrollViewDelegateProxy.installForwardDelegate(delegate, retainDelegate: false, onProxyForObject: self)
}
}

View File

@ -35,7 +35,7 @@ extension UISearchBar {
For more information take a look at `DelegateProxyType` protocol documentation.
*/
public var rx_delegate: DelegateProxy {
return proxyForObject(RxSearchBarDelegateProxy.self, self)
return RxSearchBarDelegateProxy.proxyForObject(self)
}
/**

View File

@ -16,13 +16,14 @@ import Foundation
#endif
import UIKit
@available(iOS 8.0, *)
extension UISearchController {
/**
Reactive wrapper for `delegate`.
For more information take a look at `DelegateProxyType` protocol documentation.
*/
public var rx_delegate: DelegateProxy {
return proxyForObject(RxSearchControllerDelegateProxy.self, self)
return RxSearchControllerDelegateProxy.proxyForObject(self)
}
/**
Reactive wrapper for `delegate` message.

View File

@ -25,14 +25,14 @@ extension UITableView {
- parameter cellFactory: Transform between sequence elements and view cells.
- returns: Disposable object that can be used to unbind.
*/
public func rx_itemsWithCellFactory<S: Sequence, O: ObservableType where O.E == S>
public func rx_itemsWithCellFactory<S: SequenceType, O: ObservableType where O.E == S>
(source: O)
-> (cellFactory: (UITableView, Int, S.Iterator.Element) -> UITableViewCell)
-> (cellFactory: (UITableView, Int, S.Generator.Element) -> UITableViewCell)
-> Disposable {
return { cellFactory in
let dataSource = RxTableViewReactiveArrayDataSourceSequenceWrapper<S>(cellFactory: cellFactory)
return self.rx_itemsWithDataSource(dataSource: dataSource)(source: source)
return self.rx_itemsWithDataSource(dataSource)(source: source)
}
}
@ -45,20 +45,20 @@ extension UITableView {
- parameter cellType: Type of table view cell.
- returns: Disposable object that can be used to unbind.
*/
public func rx_itemsWithCellIdentifier<S: Sequence, Cell: UITableViewCell, O : ObservableType where O.E == S>
public func rx_itemsWithCellIdentifier<S: SequenceType, Cell: UITableViewCell, O : ObservableType where O.E == S>
(cellIdentifier: String, cellType: Cell.Type = Cell.self)
-> (source: O)
-> (configureCell: (Int, S.Iterator.Element, Cell) -> Void)
-> (configureCell: (Int, S.Generator.Element, Cell) -> Void)
-> Disposable {
return { source in
return { configureCell in
let dataSource = RxTableViewReactiveArrayDataSourceSequenceWrapper<S> { (tv, i, item) in
let indexPath = NSIndexPath(forItem: i, inSection: 0)
let cell = tv.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! Cell
let cell = tv.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! Cell
configureCell(i, item, cell)
return cell
}
return self.rx_itemsWithDataSource(dataSource: dataSource)(source: source)
return self.rx_itemsWithDataSource(dataSource)(source: source)
}
}
}
@ -70,12 +70,15 @@ extension UITableView {
- parameter source: Observable sequence of items.
- returns: Disposable object that can be used to unbind.
*/
public func rx_itemsWithDataSource<DataSource: protocol<RxTableViewDataSourceType, UITableViewDataSource>, S: Sequence, O: ObservableType where DataSource.Element == S, O.E == S>
public func rx_itemsWithDataSource<
DataSource: protocol<RxTableViewDataSourceType, UITableViewDataSource>,
O: ObservableType where DataSource.Element == O.E
>
(dataSource: DataSource)
-> (source: O)
-> Disposable {
return { source in
return source.subscribeProxyDataSourceForObject(object: self, dataSource: dataSource, retainDataSource: false) { [weak self] (_: RxTableViewDataSourceProxy, event) -> Void in
return source.subscribeProxyDataSourceForObject(self, dataSource: dataSource, retainDataSource: false) { [weak self] (_: RxTableViewDataSourceProxy, event) -> Void in
guard let tableView = self else {
return
}
@ -111,7 +114,7 @@ extension UITableView {
For more information take a look at `DelegateProxyType` protocol documentation.
*/
public var rx_dataSource: DelegateProxy {
return proxyForObject(RxTableViewDataSourceProxy.self, self)
return RxTableViewDataSourceProxy.proxyForObject(self)
}
/**
@ -124,9 +127,7 @@ extension UITableView {
*/
public func rx_setDataSource(dataSource: UITableViewDataSource)
-> Disposable {
let proxy = proxyForObject(RxTableViewDataSourceProxy.self, self)
return installDelegate(proxy: proxy, delegate: dataSource, retainDelegate: false, onProxyForObject: self)
return RxTableViewDataSourceProxy.installForwardDelegate(dataSource, retainDelegate: false, onProxyForObject: self)
}
// events
@ -135,7 +136,7 @@ extension UITableView {
Reactive wrapper for `delegate` message `tableView:didSelectRowAtIndexPath:`.
*/
public var rx_itemSelected: ControlEvent<NSIndexPath> {
let source = rx_delegate.observe(selector: #selector(UITableViewDelegate.tableView(_:didSelectRowAt:)))
let source = rx_delegate.observe(#selector(UITableViewDelegate.tableView(_:didSelectRowAtIndexPath:)))
.map { a in
return try castOrThrow(NSIndexPath.self, a[1])
}
@ -147,7 +148,7 @@ extension UITableView {
Reactive wrapper for `delegate` message `tableView:didDeselectRowAtIndexPath:`.
*/
public var rx_itemDeselected: ControlEvent<NSIndexPath> {
let source = rx_delegate.observe(selector: #selector(UITableViewDelegate.tableView(_:didDeselectRowAt:)))
let source = rx_delegate.observe(#selector(UITableViewDelegate.tableView(_:didDeselectRowAtIndexPath:)))
.map { a in
return try castOrThrow(NSIndexPath.self, a[1])
}
@ -159,7 +160,7 @@ extension UITableView {
Reactive wrapper for `delegate` message `tableView:accessoryButtonTappedForRowWithIndexPath:`.
*/
public var rx_itemAccessoryButtonTapped: ControlEvent<NSIndexPath> {
let source: Observable<NSIndexPath> = rx_delegate.observe(selector: #selector(UITableViewDelegate.tableView(_:accessoryButtonTappedForRowWith:)))
let source: Observable<NSIndexPath> = rx_delegate.observe(#selector(UITableViewDelegate.tableView(_:accessoryButtonTappedForRowWithIndexPath:)))
.map { a in
return try castOrThrow(NSIndexPath.self, a[1])
}
@ -171,9 +172,9 @@ extension UITableView {
Reactive wrapper for `delegate` message `tableView:commitEditingStyle:forRowAtIndexPath:`.
*/
public var rx_itemInserted: ControlEvent<NSIndexPath> {
let source = rx_dataSource.observe(selector: #selector(UITableViewDataSource.tableView(_:commit:forRowAt:)))
let source = rx_dataSource.observe(#selector(UITableViewDataSource.tableView(_:commitEditingStyle:forRowAtIndexPath:)))
.filter { a in
return UITableViewCellEditingStyle(rawValue: (try castOrThrow(NSNumber.self, a[1])).intValue) == .insert
return UITableViewCellEditingStyle(rawValue: (try castOrThrow(NSNumber.self, a[1])).integerValue) == .Insert
}
.map { a in
return (try castOrThrow(NSIndexPath.self, a[2]))
@ -186,9 +187,9 @@ extension UITableView {
Reactive wrapper for `delegate` message `tableView:commitEditingStyle:forRowAtIndexPath:`.
*/
public var rx_itemDeleted: ControlEvent<NSIndexPath> {
let source = rx_dataSource.observe(selector: #selector(UITableViewDataSource.tableView(_:commit:forRowAt:)))
let source = rx_dataSource.observe(#selector(UITableViewDataSource.tableView(_:commitEditingStyle:forRowAtIndexPath:)))
.filter { a in
return UITableViewCellEditingStyle(rawValue: (try castOrThrow(NSNumber.self, a[1])).intValue) == .delete
return UITableViewCellEditingStyle(rawValue: (try castOrThrow(NSNumber.self, a[1])).integerValue) == .Delete
}
.map { a in
return try castOrThrow(NSIndexPath.self, a[2])
@ -201,7 +202,7 @@ extension UITableView {
Reactive wrapper for `delegate` message `tableView:moveRowAtIndexPath:toIndexPath:`.
*/
public var rx_itemMoved: ControlEvent<ItemMovedEvent> {
let source: Observable<ItemMovedEvent> = rx_dataSource.observe(selector: #selector(UITableViewDataSource.tableView(_:moveRowAt:to:)))
let source: Observable<ItemMovedEvent> = rx_dataSource.observe(#selector(UITableViewDataSource.tableView(_:moveRowAtIndexPath:toIndexPath:)))
.map { a in
return (try castOrThrow(NSIndexPath.self, a[1]), try castOrThrow(NSIndexPath.self, a[2]))
}
@ -213,7 +214,7 @@ extension UITableView {
Reactive wrapper for `delegate` message `tableView:willDisplayCell:forRowAtIndexPath:`.
*/
public var rx_willDisplayCell: ControlEvent<WillDisplayCellEvent> {
let source: Observable<DidEndDisplayingCellEvent> = rx_delegate.observe(selector: #selector(UITableViewDelegate.tableView(_:willDisplay:forRowAt:)))
let source: Observable<DidEndDisplayingCellEvent> = rx_delegate.observe(#selector(UITableViewDelegate.tableView(_:willDisplayCell:forRowAtIndexPath:)))
.map { a in
return (try castOrThrow(UITableViewCell.self, a[1]), try castOrThrow(NSIndexPath.self, a[2]))
}
@ -225,7 +226,7 @@ extension UITableView {
Reactive wrapper for `delegate` message `tableView:didEndDisplayingCell:forRowAtIndexPath:`.
*/
public var rx_didEndDisplayingCell: ControlEvent<DidEndDisplayingCellEvent> {
let source: Observable<DidEndDisplayingCellEvent> = rx_delegate.observe(selector: #selector(UITableViewDelegate.tableView(_:didEndDisplaying:forRowAt:)))
let source: Observable<DidEndDisplayingCellEvent> = rx_delegate.observe(#selector(UITableViewDelegate.tableView(_:didEndDisplayingCell:forRowAtIndexPath:)))
.map { a in
return (try castOrThrow(UITableViewCell.self, a[1]), try castOrThrow(NSIndexPath.self, a[2]))
}
@ -250,7 +251,7 @@ extension UITableView {
return Observable.empty()
}
return Observable.just(try view.rx_modelAtIndexPath(indexPath: indexPath))
return Observable.just(try view.rx_modelAtIndexPath(indexPath))
}
return ControlEvent(events: source)
@ -273,7 +274,7 @@ extension UITableView {
return Observable.empty()
}
return Observable.just(try view.rx_modelAtIndexPath(indexPath: indexPath))
return Observable.just(try view.rx_modelAtIndexPath(indexPath))
}
return ControlEvent(events: source)
@ -285,7 +286,7 @@ extension UITableView {
public func rx_modelAtIndexPath<T>(indexPath: NSIndexPath) throws -> T {
let dataSource: SectionedViewDataSourceType = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_items*` methods was used.")
let element = try dataSource.modelAtIndexPath(indexPath: indexPath)
let element = try dataSource.modelAtIndexPath(indexPath)
return castOrFatalError(element)
}

View File

@ -14,7 +14,7 @@ import RxSwift
#endif
import UIKit
extension UITextField {
extension UITextField : RxTextInput {
/**
Reactive wrapper for `text` property.
@ -25,7 +25,12 @@ extension UITextField {
getter: { textField in
textField.text ?? ""
}, setter: { textField, value in
textField.text = value
// This check is important because setting text value always clears control state
// including marked text selection which is imporant for proper input
// when IME input method is used.
if textField.text != value {
textField.text = value
}
}
)
}

View File

@ -16,7 +16,7 @@ import RxSwift
extension UITextView {
extension UITextView : RxTextInput {
/**
Factory method that enables subclasses to implement their own `rx_delegate`.
@ -53,7 +53,12 @@ extension UITextView {
}
let bindingObserver = UIBindingObserver(UIElement: self) { (textView, text: String) in
textView.text = text
// This check is important because setting text value always clears control state
// including marked text selection which is imporant for proper input
// when IME input method is used.
if textView.text != text {
textView.text = text
}
}
return ControlProperty(values: source, valueSink: bindingObserver)

View File

@ -0,0 +1,29 @@
//
// UIViewController+Rx.swift
// Rx
//
// Created by Kyle Fuller on 27/05/2016.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import Foundation
#if os(iOS)
import UIKit
#if !RX_NO_MODULE
import RxSwift
#endif
extension UIViewController {
/**
Bindable sink for `title`.
*/
public var rx_title: AnyObserver<String> {
return UIBindingObserver(UIElement: self) { viewController, title in
viewController.title = title
}.asObserver()
}
}
#endif

View File

@ -1,44 +0,0 @@
//
// 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: Sequence, E.Iterator.Element : AnimatableSectionModelType {
typealias Section = E.Iterator.Element
public func differentiateForSectionedView()
-> Observable<[Changeset<Section>]> {
return self.asObservable().multicast({
return PublishSubject()
}) { (sharedSource: Observable<E>) in
let newValues = sharedSource.skip(1)
let initialValueSequence: Observable<[Changeset<Section>]>= sharedSource
.take(1)
.map { [Changeset.initialValue(Array($0))] }
let differences = Observable.zip(sharedSource, newValues) { oldSections, newSections -> [Changeset<Section>] in
do {
return try differencesForSectionedView(Array(oldSections), finalSections: Array(newSections))
}
// in case of error, print it to terminal only because this is binding to UI Step
catch let e {
rxDebugFatalError(e)
return [Changeset.initialValue(Array(newSections))]
}
}
return Observable.of(initialValueSequence, differences).merge()
}
}
}

View File

@ -13,33 +13,46 @@ import RxSwift
import RxCocoa
#endif
public class RxCollectionViewSectionedAnimatedDataSource<S: SectionModelType>
public class RxCollectionViewSectionedAnimatedDataSource<S: AnimatableSectionModelType>
: CollectionViewSectionedDataSource<S>
, RxCollectionViewDataSourceType {
public typealias Element = [Changeset<S>]
public var animationConfiguration: AnimationConfiguration? = nil
public typealias Element = [S]
public var animationConfiguration = AnimationConfiguration()
// For some inexplicable reason, when doing animated updates first time
// it crashes. Still need to figure out that one.
var set = false
var dataSet = false
public override init() {
super.init()
}
public func collectionView(collectionView: UICollectionView, observedEvent: Event<Element>) {
UIBindingObserver(UIElement: self) { dataSource, element in
for c in element {
if !dataSource.set {
dataSource.setSections(c.finalSections)
collectionView.reloadData()
dataSource.set = true
return
}
dataSource.setSections(c.finalSections)
collectionView.performBatchUpdates(c, animationConfiguration: self.animationConfiguration)
UIBindingObserver(UIElement: self) { dataSource, newSections in
if !self.dataSet {
self.dataSet = true
dataSource.setSections(newSections)
collectionView.reloadData()
}
else {
dispatch_async(dispatch_get_main_queue()) {
let oldSections = dataSource.sectionModels
do {
let differences = try differencesForSectionedView(oldSections, finalSections: newSections)
for difference in differences {
dataSource.setSections(difference.finalSections)
collectionView.performBatchUpdates(difference, animationConfiguration: self.animationConfiguration)
}
}
catch let e {
rxDebugFatalError(e)
self.setSections(newSections)
collectionView.reloadData()
}
}
}
}.on(observedEvent)
}
}

View File

@ -13,26 +13,43 @@ import RxSwift
import RxCocoa
#endif
public class RxTableViewSectionedAnimatedDataSource<S: SectionModelType>
public class RxTableViewSectionedAnimatedDataSource<S: AnimatableSectionModelType>
: RxTableViewSectionedDataSource<S>
, RxTableViewDataSourceType {
public typealias Element = [Changeset<S>]
public var animationConfiguration: AnimationConfiguration? = nil
public typealias Element = [S]
public var animationConfiguration = AnimationConfiguration()
var dataSet = false
public override init() {
super.init()
}
public func tableView(tableView: UITableView, observedEvent: Event<Element>) {
UIBindingObserver(UIElement: self) { dataSource, element in
for c in element {
dataSource.setSections(c.finalSections)
if c.reloadData {
tableView.reloadData()
}
else {
tableView.performBatchUpdates(c, animationConfiguration: self.animationConfiguration)
UIBindingObserver(UIElement: self) { dataSource, newSections in
if !self.dataSet {
self.dataSet = true
dataSource.setSections(newSections)
tableView.reloadData()
}
else {
dispatch_async(dispatch_get_main_queue()) {
let oldSections = dataSource.sectionModels
do {
let differences = try differencesForSectionedView(oldSections, finalSections: newSections)
for difference in differences {
dataSource.setSections(difference.finalSections)
tableView.performBatchUpdates(difference, animationConfiguration: self.animationConfiguration)
}
}
catch let e {
rxDebugFatalError(e)
self.setSections(newSections)
tableView.reloadData()
}
}
}
}.on(observedEvent)

View File

@ -14,39 +14,41 @@ import RxCocoa
#endif
extension UITableView {
@available(*, deprecated=0.7, renamed="rx_itemsWithDataSource", message="You can just use normal `rx_itemsWithDataSource` extension.")
public func rx_itemsAnimatedWithDataSource<
DataSource: protocol<RxTableViewDataSourceType, UITableViewDataSource>,
O: ObservableConvertibleType,
Section: AnimatableSectionModelType
S: SequenceType,
O: ObservableType
where
DataSource.Element == [Changeset<Section>],
O.E == [Section]
DataSource.Element == S,
O.E == S,
S.Generator.Element: AnimatableSectionModelType
>
(dataSource: DataSource)
-> (source: O)
-> Disposable {
return { source in
let differences = source.differentiateForSectionedView()
return self.rx_itemsWithDataSource(dataSource)(source: differences)
return self.rx_itemsWithDataSource(dataSource)(source: source)
}
}
}
extension UICollectionView {
@available(*, deprecated=0.7, renamed="rx_itemsWithDataSource", message="You can just use normal `rx_itemsWithDataSource` extension.")
public func rx_itemsAnimatedWithDataSource<
DataSource: protocol<RxCollectionViewDataSourceType, UICollectionViewDataSource>,
O: ObservableConvertibleType,
Section: AnimatableSectionModelType
S: SequenceType,
O: ObservableType
where
DataSource.Element == [Changeset<Section>],
O.E == [Section]
DataSource.Element == S,
O.E == S,
S.Generator.Element: AnimatableSectionModelType
>
(dataSource: DataSource)
-> (source: O)
-> Disposable {
return { source in
let differences = source.differentiateForSectionedView()
return self.rx_itemsWithDataSource(dataSource)(source: differences)
return self.rx_itemsWithDataSource(dataSource)(source: source)
}
}
}

View File

@ -8,24 +8,23 @@
import Foundation
public struct AnimatableSectionModel<Section: Hashable, ItemType: Hashable>
: Hashable
, AnimatableSectionModelType
public struct AnimatableSectionModel<Section: IdentifiableType, ItemType: protocol<IdentifiableType, Equatable>>
: AnimatableSectionModelType
, CustomStringConvertible {
public typealias Item = IdentifiableValue<ItemType>
public typealias Identity = Section
public typealias Item = ItemType
public typealias Identity = Section.Identity
public var model: Section
public var items: [Item]
public var identity: Section {
return model
public var identity: Section.Identity {
return model.identity
}
public init(model: Section, items: [ItemType]) {
self.model = model
self.items = items.map(IdentifiableValue.init)
self.items = items
}
public init(original: AnimatableSectionModel, items: [Item]) {
@ -38,10 +37,6 @@ public struct AnimatableSectionModel<Section: Hashable, ItemType: Hashable>
}
public var hashValue: Int {
return self.model.hashValue
return self.model.identity.hashValue
}
}
public func == <S, I>(lhs: AnimatableSectionModel<S, I>, rhs: AnimatableSectionModel<S, I>) -> Bool {
return lhs.model == rhs.model
}

View File

@ -12,6 +12,4 @@ public protocol AnimatableSectionModelType
: SectionModelType
, IdentifiableType {
associatedtype Item : IdentifiableType, Equatable
init(original: Self, items: [Item])
}

View File

@ -0,0 +1,29 @@
//
// Array+Extensions.swift
// RxDataSources
//
// Created by Krunoslav Zaher on 4/26/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import Foundation
import Foundation
extension Array where Element: SectionModelType {
mutating func moveFromSourceIndexPath(sourceIndexPath: NSIndexPath, destinationIndexPath: NSIndexPath) {
let sourceSection = self[sourceIndexPath.section]
var sourceItems = sourceSection.items
let sourceItem = sourceItems.removeAtIndex(sourceIndexPath.item)
let sourceSectionNew = Element(original: sourceSection, items: sourceItems)
self[sourceIndexPath.section] = sourceSectionNew
let destinationSection = self[destinationIndexPath.section]
var destinationItems = destinationSection.items
destinationItems.insert(sourceItem, atIndex: destinationIndexPath.item)
self[destinationIndexPath.section] = Element(original: destinationSection, items: destinationItems)
}
}

View File

@ -18,6 +18,7 @@ public struct Changeset<S: SectionModelType> {
public let reloadData: Bool
public let originalSections: [S]
public let finalSections: [S]
public let insertedSections: [Int]
@ -31,6 +32,7 @@ public struct Changeset<S: SectionModelType> {
public let updatedItems: [ItemPath]
init(reloadData: Bool = false,
originalSections: [S] = [],
finalSections: [S] = [],
insertedSections: [Int] = [],
deletedSections: [Int] = [],
@ -44,6 +46,7 @@ public struct Changeset<S: SectionModelType> {
) {
self.reloadData = reloadData
self.originalSections = originalSections
self.finalSections = finalSections
self.insertedSections = insertedSections

View File

@ -24,43 +24,43 @@ public class _CollectionViewSectionedDataSource
return _numberOfSectionsInCollectionView(collectionView)
}
func _collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
func _rx_collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 0
}
public func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return _collectionView(collectionView, numberOfItemsInSection: section)
return _rx_collectionView(collectionView, numberOfItemsInSection: section)
}
func _collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
func _rx_collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
return (nil as UICollectionViewCell?)!
}
public func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
return _collectionView(collectionView, cellForItemAtIndexPath: indexPath)
return _rx_collectionView(collectionView, cellForItemAtIndexPath: indexPath)
}
func _collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
func _rx_collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
return (nil as UICollectionReusableView?)!
}
public func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
return _collectionView(collectionView, viewForSupplementaryElementOfKind: kind, atIndexPath: indexPath)
return _rx_collectionView(collectionView, viewForSupplementaryElementOfKind: kind, atIndexPath: indexPath)
}
func _collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool {
return false
func _rx_collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
public func collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool {
return _collectionView(collectionView, canMoveItemAtIndexPath: indexPath)
return _rx_collectionView(collectionView, canMoveItemAtIndexPath: indexPath)
}
func _collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
func _rx_collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
}
public func collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
_collectionView(collectionView, moveItemAtIndexPath: sourceIndexPath, toIndexPath: destinationIndexPath)
_rx_collectionView(collectionView, moveItemAtIndexPath: sourceIndexPath, toIndexPath: destinationIndexPath)
}
}
@ -82,9 +82,14 @@ public class CollectionViewSectionedDataSource<S: SectionModelType>
public typealias SectionModelSnapshot = SectionModel<S, I>
private var _sectionModels: [SectionModelSnapshot] = []
public var sectionModels: [S] {
return _sectionModels.map { Section(original: $0.model, items: $0.items) }
}
public func sectionAtIndex(section: Int) -> S {
return self._sectionModels[section].model
let sectionModel = self._sectionModels[section]
return S(original: sectionModel.model, items: sectionModel.items)
}
public func itemAtIndexPath(indexPath: NSIndexPath) -> I {
@ -129,33 +134,30 @@ public class CollectionViewSectionedDataSource<S: SectionModelType>
return _sectionModels.count
}
override func _collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
override func _rx_collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return _sectionModels[section].items.count
}
override func _collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
override func _rx_collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
precondition(indexPath.item < _sectionModels[indexPath.section].items.count)
return cellFactory(self, collectionView, indexPath, itemAtIndexPath(indexPath))
}
override func _collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
override func _rx_collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
return supplementaryViewFactory(self, collectionView, kind, indexPath)
}
override func _collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool {
override func _rx_collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool {
guard let canMoveItem = canMoveItemAtIndexPath?(self, indexPath: indexPath) else {
return super._collectionView(collectionView, canMoveItemAtIndexPath: indexPath)
return super._rx_collectionView(collectionView, canMoveItemAtIndexPath: indexPath)
}
return canMoveItem
}
override func _collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
guard let _ = moveItem?(self, sourceIndexPath:sourceIndexPath, destinationIndexPath: destinationIndexPath) else {
super._collectionView(collectionView, moveItemAtIndexPath: sourceIndexPath, toIndexPath: destinationIndexPath)
return
}
override func _rx_collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
self._sectionModels.moveFromSourceIndexPath(sourceIndexPath, destinationIndexPath: destinationIndexPath)
}
}

View File

@ -20,7 +20,7 @@ public struct SectionModel<Section, ItemType>
}
public var items: [Item]
public init(model: Section, items: [Item]) {
self.model = model
self.items = items
@ -31,3 +31,9 @@ public struct SectionModel<Section, ItemType>
}
}
extension SectionModel {
public init(original: SectionModel<Section, Item>, items: [Item]) {
self.model = original.model
self.items = items
}
}

View File

@ -12,4 +12,6 @@ public protocol SectionModelType {
associatedtype Item
var items: [Item] { get }
init(original: Self, items: [Item])
}

View File

@ -25,52 +25,52 @@ public class _TableViewSectionedDataSource
return _numberOfSectionsInTableView(tableView)
}
func _tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
func _rx_tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 0
}
public func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return _tableView(tableView, numberOfRowsInSection: section)
return _rx_tableView(tableView, numberOfRowsInSection: section)
}
func _tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
func _rx_tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return (nil as UITableViewCell?)!
}
public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return _tableView(tableView, cellForRowAtIndexPath: indexPath)
return _rx_tableView(tableView, cellForRowAtIndexPath: indexPath)
}
func _tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
func _rx_tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return nil
}
public func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return _tableView(tableView, titleForHeaderInSection: section)
return _rx_tableView(tableView, titleForHeaderInSection: section)
}
func _tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
func _rx_tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return nil
}
public func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return _tableView(tableView, titleForFooterInSection: section)
return _rx_tableView(tableView, titleForFooterInSection: section)
}
func _tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return false
func _rx_tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
public func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return _tableView(tableView, canEditRowAtIndexPath: indexPath)
return _rx_tableView(tableView, canEditRowAtIndexPath: indexPath)
}
func _tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
func _rx_tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return false
}
public func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return _tableView(tableView, canMoveRowAtIndexPath: indexPath)
return _rx_tableView(tableView, canMoveRowAtIndexPath: indexPath)
}
func _sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
@ -81,14 +81,20 @@ public class _TableViewSectionedDataSource
return _sectionIndexTitlesForTableView(tableView)
}
func _tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
func _rx_tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
return 0
}
public func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
return _tableView(tableView, sectionForSectionIndexTitle: title, atIndex: index)
return _rx_tableView(tableView, sectionForSectionIndexTitle: title, atIndex: index)
}
func _rx_tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
}
public func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
_rx_tableView(tableView, moveRowAtIndexPath: sourceIndexPath, toIndexPath: destinationIndexPath)
}
}
public class RxTableViewSectionedDataSource<S: SectionModelType>
@ -110,17 +116,24 @@ public class RxTableViewSectionedDataSource<S: SectionModelType>
private var _sectionModels: [SectionModelSnapshot] = []
public var sectionModels: [S] {
return _sectionModels.map { $0.model }
return _sectionModels.map { Section(original: $0.model, items: $0.items) }
}
public func sectionAtIndex(section: Int) -> S {
return self._sectionModels[section].model
let sectionModel = _sectionModels[section]
return Section(original: sectionModel.model, items: sectionModel.items)
}
public func itemAtIndexPath(indexPath: NSIndexPath) -> I {
return self._sectionModels[indexPath.section].items[indexPath.item]
}
public func setItem(item item: I, indexPath: NSIndexPath) {
var section = self._sectionModels[indexPath.section]
section.items[indexPath.item] = item
self._sectionModels[indexPath.section] = section
}
public func modelAtIndexPath(indexPath: NSIndexPath) throws -> Any {
return itemAtIndexPath(indexPath)
}
@ -129,7 +142,6 @@ public class RxTableViewSectionedDataSource<S: SectionModelType>
self._sectionModels = sections.map { SectionModelSnapshot(model: $0, items: $0.items) }
}
public var configureCell: CellFactory! = nil
public var titleForHeaderInSection: ((RxTableViewSectionedDataSource<S>, section: Int) -> String?)?
@ -160,39 +172,43 @@ public class RxTableViewSectionedDataSource<S: SectionModelType>
return _sectionModels.count
}
override func _tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
override func _rx_tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return _sectionModels[section].items.count
}
override func _tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
override func _rx_tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
precondition(indexPath.item < _sectionModels[indexPath.section].items.count)
return configureCell(self, tableView, indexPath, itemAtIndexPath(indexPath))
}
override func _tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
override func _rx_tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return titleForHeaderInSection?(self, section: section)
}
override func _tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
override func _rx_tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return titleForFooterInSection?(self, section: section)
}
override func _tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
override func _rx_tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
guard let canEditRow = canEditRowAtIndexPath?(self, indexPath: indexPath) else {
return super._tableView(tableView, canMoveRowAtIndexPath: indexPath)
return super._rx_tableView(tableView, canEditRowAtIndexPath: indexPath)
}
return canEditRow
}
override func _tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
override func _rx_tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
guard let canMoveRow = canMoveRowAtIndexPath?(self, indexPath: indexPath) else {
return super._tableView(tableView, canMoveRowAtIndexPath: indexPath)
return super._rx_tableView(tableView, canMoveRowAtIndexPath: indexPath)
}
return canMoveRow
}
override func _rx_tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
self._sectionModels.moveFromSourceIndexPath(sourceIndexPath, destinationIndexPath: destinationIndexPath)
}
override func _sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
guard let titles = sectionIndexTitles?(self) else {
@ -202,9 +218,9 @@ public class RxTableViewSectionedDataSource<S: SectionModelType>
return titles
}
override func _tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
guard let section = sectionForSectionIndexTitle?(self, title: title, index: index) else {
return super._tableView(tableView, sectionForSectionIndexTitle: title, atIndex: index)
override func _rx_tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
guard let section = sectionForSectionIndexTitle?(self, title: title, index: index) else {
return super._rx_tableView(tableView, sectionForSectionIndexTitle: title, atIndex: index)
}
return section

View File

@ -51,9 +51,9 @@ extension UITableView : SectionedViewType {
self.reloadSections(indexSet(sections), withRowAnimation: animationStyle)
}
public func performBatchUpdates<S: SectionModelType>(changes: Changeset<S>, animationConfiguration: AnimationConfiguration?=nil) {
public func performBatchUpdates<S: SectionModelType>(changes: Changeset<S>, animationConfiguration: AnimationConfiguration) {
self.beginUpdates()
_performBatchUpdates(self, changes: changes, animationConfiguration: animationConfiguration)
_performBatchUpdates(self, changes: changes, animationConfiguration: animationConfiguration)
self.endUpdates()
}
}
@ -91,9 +91,9 @@ extension UICollectionView : SectionedViewType {
self.reloadSections(indexSet(sections))
}
public func performBatchUpdates<S: SectionModelType>(changes: Changeset<S>, animationConfiguration:AnimationConfiguration?=nil) {
public func performBatchUpdates<S: SectionModelType>(changes: Changeset<S>, animationConfiguration: AnimationConfiguration) {
self.performBatchUpdates({ () -> Void in
_performBatchUpdates(self, changes: changes)
_performBatchUpdates(self, changes: changes, animationConfiguration: animationConfiguration)
}, completion: { (completed: Bool) -> Void in
})
}
@ -110,13 +110,12 @@ public protocol SectionedViewType {
func moveSection(from: Int, to: Int)
func reloadSections(sections: [Int], animationStyle: UITableViewRowAnimation)
func performBatchUpdates<S>(changes: Changeset<S>, animationConfiguration: AnimationConfiguration?)
func performBatchUpdates<S>(changes: Changeset<S>, animationConfiguration: AnimationConfiguration)
}
func _performBatchUpdates<V: SectionedViewType, S: SectionModelType>(view: V, changes: Changeset<S>, animationConfiguration :AnimationConfiguration?=nil) {
func _performBatchUpdates<V: SectionedViewType, S: SectionModelType>(view: V, changes: Changeset<S>, animationConfiguration:AnimationConfiguration) {
typealias I = S.Item
let animationConfiguration = animationConfiguration ?? AnimationConfiguration()
view.deleteSections(changes.deletedSections, animationStyle: animationConfiguration.deleteAnimation)
// Updated sections doesn't mean reload entire section, somebody needs to update the section view manually
// otherwise all cells will be reloaded for nothing.

View File

@ -127,8 +127,6 @@
C864BAD81C3332F10083833C /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864BAD11C3332F10083833C /* DetailViewController.swift */; };
C864BAD91C3332F10083833C /* RandomUserAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864BAD21C3332F10083833C /* RandomUserAPI.swift */; };
C864BADA1C3332F10083833C /* RandomUserAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864BAD21C3332F10083833C /* RandomUserAPI.swift */; };
C864BADB1C3332F10083833C /* String+extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864BAD31C3332F10083833C /* String+extensions.swift */; };
C864BADC1C3332F10083833C /* String+extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864BAD31C3332F10083833C /* String+extensions.swift */; };
C864BADD1C3332F10083833C /* TableViewWithEditingCommandsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864BAD41C3332F10083833C /* TableViewWithEditingCommandsViewController.swift */; };
C864BADE1C3332F10083833C /* TableViewWithEditingCommandsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864BAD41C3332F10083833C /* TableViewWithEditingCommandsViewController.swift */; };
C864BADF1C3332F10083833C /* UIImageView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C864BAD51C3332F10083833C /* UIImageView+Extensions.swift */; };
@ -148,6 +146,7 @@
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 */; };
C88F76861CE53B1300D5A014 /* RxTextInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88F76851CE53B1300D5A014 /* RxTextInput.swift */; };
C890A65D1AEC084100AFF7E6 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C890A65C1AEC084100AFF7E6 /* ViewController.swift */; };
C891A2C91C07160C00DDD09D /* Timeout.swift in Sources */ = {isa = PBXBuildFile; fileRef = C891A2C81C07160C00DDD09D /* Timeout.swift */; };
C894649E1BC6C2B00055219D /* Cancelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89464281BC6C2B00055219D /* Cancelable.swift */; };
@ -286,7 +285,6 @@
C8B290D91C959D2900E923D0 /* SectionModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B41C959D2900E923D0 /* SectionModelType.swift */; };
C8B290DB1C959D2900E923D0 /* TableViewSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B51C959D2900E923D0 /* TableViewSectionedDataSource.swift */; };
C8B290DD1C959D2900E923D0 /* UI+SectionedViewType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B61C959D2900E923D0 /* UI+SectionedViewType.swift */; };
C8B290DF1C959D2900E923D0 /* ObservableConvertibleType+Differentiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B81C959D2900E923D0 /* ObservableConvertibleType+Differentiator.swift */; };
C8B290E11C959D2900E923D0 /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B91C959D2900E923D0 /* RxCollectionViewSectionedAnimatedDataSource.swift */; };
C8B290E31C959D2900E923D0 /* RxCollectionViewSectionedReloadDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290BA1C959D2900E923D0 /* RxCollectionViewSectionedReloadDataSource.swift */; };
C8B290E51C959D2900E923D0 /* RxTableViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290BB1C959D2900E923D0 /* RxTableViewSectionedAnimatedDataSource.swift */; };
@ -400,7 +398,6 @@
C8CC3F4F1C95D16C00ABA17E /* SectionModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B41C959D2900E923D0 /* SectionModelType.swift */; };
C8CC3F501C95D16C00ABA17E /* TableViewSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B51C959D2900E923D0 /* TableViewSectionedDataSource.swift */; };
C8CC3F511C95D16C00ABA17E /* UI+SectionedViewType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B61C959D2900E923D0 /* UI+SectionedViewType.swift */; };
C8CC3F521C95D17000ABA17E /* ObservableConvertibleType+Differentiator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B81C959D2900E923D0 /* ObservableConvertibleType+Differentiator.swift */; };
C8CC3F531C95D17000ABA17E /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290B91C959D2900E923D0 /* RxCollectionViewSectionedAnimatedDataSource.swift */; };
C8CC3F541C95D17000ABA17E /* RxCollectionViewSectionedReloadDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290BA1C959D2900E923D0 /* RxCollectionViewSectionedReloadDataSource.swift */; };
C8CC3F551C95D17000ABA17E /* RxTableViewSectionedAnimatedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B290BB1C959D2900E923D0 /* RxTableViewSectionedAnimatedDataSource.swift */; };
@ -418,6 +415,8 @@
C8DF92EA1B0B38C0009BCF9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; };
C8DF92EB1B0B38C0009BCF9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E91B0B38C0009BCF9A /* Images.xcassets */; };
C8E9D2AF1BD3FD960079D0DB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80397391BD3E17D009D8B26 /* ActivityIndicator.swift */; };
C8EBA6031CD3E30C00E745F3 /* Array+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EBA6021CD3E30C00E745F3 /* Array+Extensions.swift */; };
C8EBA6041CD3E30C00E745F3 /* Array+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EBA6021CD3E30C00E745F3 /* Array+Extensions.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 */; };
@ -442,6 +441,7 @@
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 */; };
D2F4611B1CD7AC9000527B4D /* Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2F461051CD7AC4D00527B4D /* Reactive.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -691,7 +691,6 @@
C84CC5831BDD484400E06A64 /* SubscriptionDisposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionDisposable.swift; sourceTree = "<group>"; };
C864BAD11C3332F10083833C /* DetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = "<group>"; };
C864BAD21C3332F10083833C /* RandomUserAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomUserAPI.swift; sourceTree = "<group>"; };
C864BAD31C3332F10083833C /* String+extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+extensions.swift"; sourceTree = "<group>"; };
C864BAD41C3332F10083833C /* TableViewWithEditingCommandsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewWithEditingCommandsViewController.swift; sourceTree = "<group>"; };
C864BAD51C3332F10083833C /* UIImageView+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImageView+Extensions.swift"; sourceTree = "<group>"; };
C864BAD61C3332F10083833C /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = "<group>"; };
@ -700,6 +699,7 @@
C86E2F3C1AE5A0CA00C31024 /* WikipediaPage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = WikipediaPage.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
C86E2F3D1AE5A0CA00C31024 /* WikipediaSearchResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = WikipediaSearchResult.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
C88BB8DC1B07E6C90064D411 /* RxExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RxExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
C88F76851CE53B1300D5A014 /* RxTextInput.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTextInput.swift; sourceTree = "<group>"; };
C890A65C1AEC084100AFF7E6 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
C891A2C81C07160C00DDD09D /* Timeout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Timeout.swift; sourceTree = "<group>"; };
C89464281BC6C2B00055219D /* Cancelable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cancelable.swift; sourceTree = "<group>"; };
@ -839,7 +839,6 @@
C8B290B41C959D2900E923D0 /* SectionModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionModelType.swift; sourceTree = "<group>"; };
C8B290B51C959D2900E923D0 /* TableViewSectionedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewSectionedDataSource.swift; sourceTree = "<group>"; };
C8B290B61C959D2900E923D0 /* UI+SectionedViewType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UI+SectionedViewType.swift"; sourceTree = "<group>"; };
C8B290B81C959D2900E923D0 /* ObservableConvertibleType+Differentiator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ObservableConvertibleType+Differentiator.swift"; sourceTree = "<group>"; };
C8B290B91C959D2900E923D0 /* RxCollectionViewSectionedAnimatedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewSectionedAnimatedDataSource.swift; sourceTree = "<group>"; };
C8B290BA1C959D2900E923D0 /* RxCollectionViewSectionedReloadDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxCollectionViewSectionedReloadDataSource.swift; sourceTree = "<group>"; };
C8B290BB1C959D2900E923D0 /* RxTableViewSectionedAnimatedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewSectionedAnimatedDataSource.swift; sourceTree = "<group>"; };
@ -951,6 +950,7 @@
C8DF92E91B0B38C0009BCF9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
C8DF92F01B0B3E67009BCF9A /* Info-OSX.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-OSX.plist"; sourceTree = "<group>"; };
C8DF92F21B0B3E71009BCF9A /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = "<group>"; };
C8EBA6021CD3E30C00E745F3 /* Array+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Extensions.swift"; sourceTree = "<group>"; };
C8F6A1361BEF9DD4007DF367 /* RetryWhen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RetryWhen.swift; sourceTree = "<group>"; };
C8F8C4891C277F460047640B /* CalculatorState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalculatorState.swift; sourceTree = "<group>"; };
C8F8C49C1C277F4F0047640B /* CalculatorAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalculatorAction.swift; sourceTree = "<group>"; };
@ -963,6 +963,7 @@
CBEE77531BD8C7B700AD584C /* ToArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToArray.swift; sourceTree = "<group>"; };
D2245A0B1BD564A700E7146F /* WithLatestFrom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WithLatestFrom.swift; sourceTree = "<group>"; };
D2AF91881BD2C51900A008C1 /* Using.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Using.swift; sourceTree = "<group>"; };
D2F461051CD7AC4D00527B4D /* Reactive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reactive.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -1224,7 +1225,6 @@
children = (
C864BAD11C3332F10083833C /* DetailViewController.swift */,
C864BAD21C3332F10083833C /* RandomUserAPI.swift */,
C864BAD31C3332F10083833C /* String+extensions.swift */,
C864BAD41C3332F10083833C /* TableViewWithEditingCommandsViewController.swift */,
C864BAD51C3332F10083833C /* UIImageView+Extensions.swift */,
C864BAD61C3332F10083833C /* User.swift */,
@ -1549,6 +1549,7 @@
C8B290A61C959D2900E923D0 /* DataSources */ = {
isa = PBXGroup;
children = (
C8EBA6021CD3E30C00E745F3 /* Array+Extensions.swift */,
C8B290A71C959D2900E923D0 /* AnimatableSectionModel.swift */,
C8B290A81C959D2900E923D0 /* AnimatableSectionModelType+ItemPath.swift */,
C8B290A91C959D2900E923D0 /* AnimatableSectionModelType.swift */,
@ -1572,7 +1573,6 @@
C8B290B71C959D2900E923D0 /* DataSources+Rx */ = {
isa = PBXGroup;
children = (
C8B290B81C959D2900E923D0 /* ObservableConvertibleType+Differentiator.swift */,
C8B290B91C959D2900E923D0 /* RxCollectionViewSectionedAnimatedDataSource.swift */,
C8B290BA1C959D2900E923D0 /* RxCollectionViewSectionedReloadDataSource.swift */,
C8B290BB1C959D2900E923D0 /* RxTableViewSectionedAnimatedDataSource.swift */,
@ -1630,6 +1630,8 @@
C8CC3E6B1C95CB5300ABA17E /* Common */ = {
isa = PBXGroup;
children = (
C88F76851CE53B1300D5A014 /* RxTextInput.swift */,
D2F461051CD7AC4D00527B4D /* Reactive.swift */,
C8CC3E6C1C95CB5300ABA17E /* _RX.h */,
C8CC3E6D1C95CB5300ABA17E /* _RX.m */,
C8CC3E6E1C95CB5300ABA17E /* _RXDelegateProxy.h */,
@ -2146,7 +2148,6 @@
C84015751C34353D009D2E77 /* DispatchQueueSchedulerQOS.swift in Sources */,
C8CC3F4A1C95D16C00ABA17E /* IdentifiableType.swift in Sources */,
C84CC58B1BDD486300E06A64 /* LockOwnerType.swift in Sources */,
C864BADC1C3332F10083833C /* String+extensions.swift in Sources */,
C8CC3EFB1C95CB5300ABA17E /* ItemEvents.swift in Sources */,
C8CC3F1D1C95CB5300ABA17E /* UITextField+Rx.swift in Sources */,
C8CC3ED81C95CB5300ABA17E /* ControlEvent+Driver.swift in Sources */,
@ -2278,7 +2279,6 @@
C83D73DF1C1DBC2A003DC470 /* InvocableScheduledItem.swift in Sources */,
C822B1E41C14E4810088A01A /* SimpleTableViewExampleViewController.swift in Sources */,
C8CC3F481C95D16C00ABA17E /* DataSources.swift in Sources */,
C8CC3F521C95D17000ABA17E /* ObservableConvertibleType+Differentiator.swift in Sources */,
C8297E401B6CF905000589EA /* ImageService.swift in Sources */,
C8CC3EE41C95CB5300ABA17E /* KVORepresentable+CoreGraphics.swift in Sources */,
C8CC3F4D1C95D16C00ABA17E /* Optional+Extensions.swift in Sources */,
@ -2300,6 +2300,7 @@
C8F8C49E1C277F4F0047640B /* CalculatorAction.swift in Sources */,
C89464A31BC6C2B00055219D /* InfiniteSequence.swift in Sources */,
CB30D9EE1BF106260084C1C0 /* SingleAsync.swift in Sources */,
C8EBA6041CD3E30C00E745F3 /* Array+Extensions.swift in Sources */,
C89464F51BC6C2B00055219D /* AnyObserver.swift in Sources */,
C89464D71BC6C2B00055219D /* Range.swift in Sources */,
C84CC52E1BDC344100E06A64 /* ElementAt.swift in Sources */,
@ -2329,6 +2330,7 @@
C89464F71BC6C2B00055219D /* ObserverBase.swift in Sources */,
C8CC3F441C95D16C00ABA17E /* AnimatableSectionModelType.swift in Sources */,
C8CC3F111C95CB5300ABA17E /* UIImageView+Rx.swift in Sources */,
C88F76861CE53B1300D5A014 /* RxTextInput.swift in Sources */,
C89464E31BC6C2B00055219D /* TakeUntil.swift in Sources */,
C8CC3F421C95D16C00ABA17E /* AnimatableSectionModel.swift in Sources */,
C89464FB1BC6C2B00055219D /* Rx.swift in Sources */,
@ -2421,6 +2423,7 @@
C8CC3F0C1C95CB5300ABA17E /* UICollectionView+Rx.swift in Sources */,
C8297E561B6CF905000589EA /* WikipediaPage.swift in Sources */,
C8297E571B6CF905000589EA /* Randomizer.swift in Sources */,
D2F4611B1CD7AC9000527B4D /* Reactive.swift in Sources */,
C89464C31BC6C2B00055219D /* Concat.swift in Sources */,
C89464F41BC6C2B00055219D /* ObservableType.swift in Sources */,
C89464CE1BC6C2B00055219D /* Generate.swift in Sources */,
@ -2484,7 +2487,6 @@
C809E97A1BE6841C0058D948 /* Wireframe.swift in Sources */,
C8B290C71C959D2900E923D0 /* Changeset.swift in Sources */,
C843A0931C1CE58700CBA4BD /* UINavigationController+Extensions.swift in Sources */,
C864BADB1C3332F10083833C /* String+extensions.swift in Sources */,
C822B1E71C14E7250088A01A /* SimpleTableViewExampleSectionedViewController.swift in Sources */,
C83367251AD029AE00C668A7 /* ImageService.swift in Sources */,
C86E2F471AE5A0CA00C31024 /* WikipediaSearchResult.swift in Sources */,
@ -2505,6 +2507,7 @@
C8B290E11C959D2900E923D0 /* RxCollectionViewSectionedAnimatedDataSource.swift in Sources */,
C8B290E91C959D2900E923D0 /* UISectionedViewType+RxAnimatedDataSource.swift in Sources */,
C890A65D1AEC084100AFF7E6 /* ViewController.swift in Sources */,
C8EBA6031CD3E30C00E745F3 /* Array+Extensions.swift in Sources */,
C8C46DAA1B47F7110020D71E /* WikipediaSearchCell.swift in Sources */,
C8B290C91C959D2900E923D0 /* CollectionViewSectionedDataSource.swift in Sources */,
B1604CB51BE49F8D002E1279 /* DownloadableImage.swift in Sources */,
@ -2518,7 +2521,6 @@
C86E2F461AE5A0CA00C31024 /* WikipediaPage.swift in Sources */,
C8B290E71C959D2900E923D0 /* RxTableViewSectionedReloadDataSource.swift in Sources */,
C809E97D1BE697100058D948 /* UIImage+Extensions.swift in Sources */,
C8B290DF1C959D2900E923D0 /* ObservableConvertibleType+Differentiator.swift in Sources */,
C8B290CF1C959D2900E923D0 /* IdentifiableType.swift in Sources */,
C8A2A2CB1B404A1200F11F09 /* Randomizer.swift in Sources */,
C8BCD3EA1C14B02A005F1280 /* SimpleValidationViewController.swift in Sources */,

View File

@ -140,7 +140,7 @@ class APIWrappersViewController: ViewController {
// also test two way binding
let textValue = Variable("")
textField.rx_text <-> textValue
textField <-> textValue
textValue.asObservable()
.subscribeNext { [weak self] x in
@ -162,7 +162,7 @@ class APIWrappersViewController: ViewController {
// also test two way binding
let textViewValue = Variable("")
textView.rx_text <-> textViewValue
textView <-> textViewValue
textViewValue.asObservable()
.subscribeNext { [weak self] x in

View File

@ -48,10 +48,10 @@ class GeolocationViewController: ViewController {
let geolocationService = GeolocationService.instance
geolocationService.autorized
geolocationService.authorized
.drive(noGeolocationView.rx_driveAuthorization)
.addDisposableTo(disposeBag)
/*
geolocationService.location
.drive(label.rx_driveCoordinates)
.addDisposableTo(disposeBag)
@ -67,7 +67,6 @@ class GeolocationViewController: ViewController {
self?.openAppPreferences()
}
.addDisposableTo(disposeBag)
*/
}
private func openAppPreferences() {

View File

@ -85,7 +85,7 @@ class PartialUpdatesViewController : ViewController {
skinTableViewDataSource(reloadDataSource)
self.sections.asObservable()
.bindTo(partialUpdatesTableViewOutlet.rx_itemsAnimatedWithDataSource(tvAnimatedDataSource))
.bindTo(partialUpdatesTableViewOutlet.rx_itemsWithDataSource(tvAnimatedDataSource))
.addDisposableTo(disposeBag)
self.sections.asObservable()

View File

@ -1,11 +0,0 @@
//
// String+extensions.swift
// RxExample
//
// Created by carlos on 28/5/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
extension String {
}

View File

@ -12,12 +12,66 @@ import RxSwift
import RxCocoa
#endif
import UIKit
// Two way binding operator between control property and variable, that's all it takes {
infix operator <-> {
}
func nonMarkedText(textInput: UITextInput) -> String? {
let start = textInput.beginningOfDocument
let end = textInput.endOfDocument
guard let rangeAll = textInput.textRangeFromPosition(start, toPosition: end),
text = textInput.textInRange(rangeAll) else {
return nil
}
guard let markedTextRange = textInput.markedTextRange else {
return text
}
guard let startRange = textInput.textRangeFromPosition(start, toPosition: markedTextRange.start),
endRange = textInput.textRangeFromPosition(markedTextRange.end, toPosition: end) else {
return text
}
return (textInput.textInRange(startRange) ?? "") + (textInput.textInRange(endRange) ?? "")
}
func <-> (textInput: RxTextInput, variable: Variable<String>) -> Disposable {
let bindToUIDisposable = variable.asObservable()
.bindTo(textInput.rx_text)
let bindToVariable = textInput.rx_text
.subscribe(onNext: { [weak textInput] n in
guard let textInput = textInput else {
return
}
let nonMarkedTextValue = nonMarkedText(textInput)
if nonMarkedTextValue != variable.value {
variable.value = nonMarkedTextValue ?? ""
}
}, onCompleted: {
bindToUIDisposable.dispose()
})
return StableCompositeDisposable.create(bindToUIDisposable, bindToVariable)
}
func <-> <T>(property: ControlProperty<T>, variable: Variable<T>) -> Disposable {
if T.self == String.self {
#if DEBUG
fatalError("It is ok to delete this message, but this is here to warn that you are maybe trying to bind to some `rx_text` property directly to variable.\n" +
"That will usually work ok, but for some languages that use IME, that simplistic method could cause unexpected issues because it will return intermediate results while text is being inputed.\n" +
"REMEDY: Just use `textField <-> variable` instead of `textField.rx_text <-> variable`.\n" +
"Find out more here: https://github.com/ReactiveX/RxSwift/issues/649\n"
)
#endif
}
let bindToUIDisposable = variable.asObservable()
.bindTo(property)
let bindToVariable = property

View File

@ -12,7 +12,7 @@ import RxSwift
import RxCocoa
#endif
struct ActivityToken<E> : ObservableConvertibleType, Disposable {
private struct ActivityToken<E> : ObservableConvertibleType, Disposable {
private let _source: Observable<E>
private let _dispose: AnonymousDisposable
@ -36,14 +36,14 @@ 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
public class ActivityIndicator : DriverConvertibleType {
public typealias E = Bool
private let _lock = NSRecursiveLock()
private let _variable = Variable(0)
private let _loading: Driver<Bool>
init() {
public init() {
_loading = _variable.asObservable()
.map { $0 > 0 }
.distinctUntilChanged()
@ -57,12 +57,12 @@ class ActivityIndicator : DriverConvertibleType {
>>>>>>> Changes for Swift 3.0.
}
static func ifItStillErrors(error: ErrorType) -> Driver<Bool> {
private static func ifItStillErrors(error: ErrorType) -> Driver<Bool> {
_ = fatalError("Loader can't fail")
}
func trackActivity<O: ObservableConvertibleType>(source: O) -> Observable<O.E> {
private func trackActivityOfObservable<O: ObservableConvertibleType>(source: O) -> Observable<O.E> {
return Observable.using({ () -> ActivityToken<O.E> in
self.increment()
return ActivityToken(source: source.asObservable(), disposeAction: self.decrement)
@ -83,13 +83,13 @@ class ActivityIndicator : DriverConvertibleType {
_lock.unlock()
}
func asDriver() -> Driver<E> {
public func asDriver() -> Driver<E> {
return _loading
}
}
extension ObservableConvertibleType {
func trackActivity(activityIndicator: ActivityIndicator) -> Observable<E> {
return activityIndicator.trackActivity(self)
public extension ObservableConvertibleType {
public func trackActivity(activityIndicator: ActivityIndicator) -> Observable<E> {
return activityIndicator.trackActivityOfObservable(self)
}
}

View File

@ -16,7 +16,7 @@ import CoreLocation
class GeolocationService {
static let instance = GeolocationService()
private (set) var autorized: Driver<Bool>
private (set) var authorized: Driver<Bool>
private (set) var location: Driver<CLLocationCoordinate2D>
private let locationManager = CLLocationManager()
@ -26,7 +26,7 @@ class GeolocationService {
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
autorized = Observable.deferred { [weak locationManager] in
authorized = Observable.deferred { [weak locationManager] in
let status = CLLocationManager.authorizationStatus()
guard let locationManager = locationManager else {
return Observable.just(status)

View File

@ -10,6 +10,22 @@ import Foundation
typealias NumberSection = AnimatableSectionModel<String, Int>
extension String : IdentifiableType {
public typealias Identity = String
public var identity: String {
return self
}
}
extension Int : IdentifiableType {
public typealias Identity = Int
public var identity: Int {
return self
}
}
let insertItems = true
let deleteItems = true
let moveItems = true
@ -74,7 +90,7 @@ class Randomizer {
if rng.get_random() % 2 == 0 {
let itemIndex = rng.get_random() % (itemCount + 1)
if insertItems {
sections[sectionIndex].items.insert(IdentifiableValue(value: unusedValue), atIndex: itemIndex)
sections[sectionIndex].items.insert(unusedValue, atIndex: itemIndex)
}
else {
nextUnusedItems.append(unusedValue)
@ -83,14 +99,14 @@ class Randomizer {
// update
else {
if itemCount == 0 {
sections[sectionIndex].items.insert(IdentifiableValue(value: unusedValue), atIndex: 0)
sections[sectionIndex].items.insert(unusedValue, atIndex: 0)
continue
}
let itemIndex = rng.get_random() % itemCount
if reloadItems {
nextUnusedItems.append(sections[sectionIndex].items.removeAtIndex(itemIndex).value)
sections[sectionIndex].items.insert(IdentifiableValue(value: unusedValue), atIndex: itemIndex)
nextUnusedItems.append(sections[sectionIndex].items.removeAtIndex(itemIndex))
sections[sectionIndex].items.insert(unusedValue, atIndex: itemIndex)
}
else {
@ -151,7 +167,7 @@ class Randomizer {
let sourceItemIndex = rng.get_random() % sectionItemCount
if deleteItems {
nextUnusedItems.append(sections[sourceSectionIndex].items.removeAtIndex(sourceItemIndex).value)
nextUnusedItems.append(sections[sourceSectionIndex].items.removeAtIndex(sourceItemIndex))
}
}
@ -188,7 +204,7 @@ class Randomizer {
let section = sections.removeAtIndex(sectionIndex)
for item in section.items {
nextUnusedItems.append(item.value)
nextUnusedItems.append(item)
}
nextUnusedSections.append(section.model)

View File

@ -23,7 +23,7 @@ public class RootViewController : UITableViewController {
DefaultWireframe.sharedInstance
MainScheduler.instance
let geoService = GeolocationService.instance
geoService.autorized.driveNext { _ in
geoService.authorized.driveNext { _ in
}.dispose()
geoService.location.driveNext { _ in

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "RxSwift"
s.version = "2.4"
s.version = "2.5.0"
s.summary = "RxSwift is a Swift implementation of Reactive Extensions"
s.description = <<-DESC
This is a Swift port of [ReactiveX.io](https://github.com/ReactiveX)

View File

@ -37,23 +37,17 @@ protocol Lock {
}
func performLocked(@noescape action: () -> Void) {
pthread_spin_lock(&_lock)
lock(); defer { unlock() }
action()
pthread_spin_unlock(&_lock)
}
func calculateLocked<T>(@noescape action: () -> T) -> T {
pthread_spin_lock(&_lock)
let result = action()
pthread_spin_unlock(&_lock)
return result
lock(); defer { unlock() }
return action()
}
func calculateLockedOrFail<T>(@noescape action: () throws -> T) throws -> T {
pthread_spin_lock(&_lock)
defer {
pthread_spin_unlock(&_lock)
}
lock(); defer { unlock() }
let result = try action()
return result
}
@ -69,24 +63,18 @@ protocol Lock {
#endif
extension NSRecursiveLock : Lock {
func performLocked(action: @noescape () -> Void) {
self.lock()
func performLocked(@noescape action: () -> Void) {
lock(); defer { unlock() }
action()
self.unlock()
}
func calculateLocked<T>(action: @noescape () -> T) -> T {
self.lock()
let result = action()
self.unlock()
return result
func calculateLocked<T>(@noescape action: () -> T) -> T {
lock(); defer { unlock() }
return action()
}
func calculateLockedOrFail<T>(action: @noescape () throws -> T) throws -> T {
self.lock()
defer {
self.unlock()
}
func calculateLockedOrFail<T>(@noescape action: () throws -> T) throws -> T {
lock(); defer { unlock() }
let result = try action()
return result
}

View File

@ -8,12 +8,8 @@
import Foundation
/**
Respresents disposable resource.
*/
/// Respresents a disposable resource.
public protocol Disposable {
/**
Dispose resource.
*/
/// Dispose resource.
func dispose()
}

View File

@ -31,17 +31,34 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable {
Initializes a new instance of composite disposable with the specified number of disposables.
*/
public init(_ disposable1: Disposable, _ disposable2: Disposable) {
_disposables!.insert(element: disposable1)
_disposables!.insert(element: disposable2)
// This overload is here to make sure we are using optimized version up to 4 arguments.
_disposables!.insert(disposable1)
_disposables!.insert(disposable2)
}
/**
Initializes a new instance of composite disposable with the specified number of disposables.
*/
public init(_ disposable1: Disposable, _ disposable2: Disposable, _ disposable3: Disposable) {
_disposables!.insert(element: disposable1)
_disposables!.insert(element: disposable2)
_disposables!.insert(element: disposable3)
// This overload is here to make sure we are using optimized version up to 4 arguments.
_disposables!.insert(disposable1)
_disposables!.insert(disposable2)
_disposables!.insert(disposable3)
}
/**
Initializes a new instance of composite disposable with the specified number of disposables.
*/
public init(_ disposable1: Disposable, _ disposable2: Disposable, _ disposable3: Disposable, _ disposable4: Disposable, _ disposables: Disposable...) {
// This overload is here to make sure we are using optimized version up to 4 arguments.
_disposables!.insert(disposable1)
_disposables!.insert(disposable2)
_disposables!.insert(disposable3)
_disposables!.insert(disposable4)
for disposable in disposables {
_disposables!.insert(disposable)
}
}
/**
@ -49,7 +66,7 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable {
*/
public init(disposables: [Disposable]) {
for disposable in disposables {
_disposables!.insert(element: disposable)
_disposables!.insert(disposable)
}
}
@ -61,7 +78,7 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable {
disposed `nil` will be returned.
*/
public func addDisposable(disposable: Disposable) -> DisposeKey? {
let key = _addDisposable(disposable: disposable)
let key = _addDisposable(disposable)
if key == nil {
disposable.dispose()
@ -73,7 +90,7 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable {
private func _addDisposable(disposable: Disposable) -> DisposeKey? {
_lock.lock(); defer { _lock.unlock() }
return _disposables?.insert(element: disposable)
return _disposables?.insert(disposable)
}
/**
@ -90,12 +107,12 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable {
- parameter disposeKey: Key used to identify disposable to be removed.
*/
public func removeDisposable(disposeKey: DisposeKey) {
_removeDisposable(disposeKey: disposeKey)?.dispose()
_removeDisposable(disposeKey)?.dispose()
}
private func _removeDisposable(disposeKey: DisposeKey) -> Disposable? {
_lock.lock(); defer { _lock.unlock() }
return _disposables?.removeKey(key: disposeKey)
return _disposables?.removeKey(disposeKey)
}
/**
@ -103,7 +120,7 @@ public class CompositeDisposable : DisposeBase, Disposable, Cancelable {
*/
public func dispose() {
if let disposables = _dispose() {
disposeAllIn(bag: disposables)
disposeAllIn(disposables)
}
}

View File

@ -8,34 +8,25 @@
import Foundation
/**
Represents sequence event
Represents a sequence event.
Sequence grammar:
Next\* (Error | Completed)
*/
public enum Event<Element> : CustomDebugStringConvertible {
/**
Next element is produced
*/
public enum Event<Element> {
/// Next element is produced.
case Next(Element)
/**
Sequence terminates with error
*/
case Error(ErrorProtocol)
/**
Sequence completes sucessfully
*/
/// Sequence terminated with an error.
case Error(ErrorType)
/// Sequence completed successfully.
case Completed
}
extension Event {
/**
- returns: Description of event
*/
extension Event : CustomDebugStringConvertible {
/// - returns: Description of event.
public var debugDescription: String {
switch self {
case .Next(let value):
@ -49,9 +40,7 @@ extension Event {
}
extension Event {
/**
- returns: Is `Completed` or `Error` event
*/
/// - returns: Is `Completed` or `Error` event.
public var isStopEvent: Bool {
switch self {
case .Next: return false
@ -59,9 +48,7 @@ extension Event {
}
}
/**
- returns: If `Next` event, returns element value.
*/
/// - returns: If `Next` event, returns element value.
public var element: Element? {
if case .Next(let value) = self {
return value
@ -69,13 +56,11 @@ extension Event {
return nil
}
/**
- returns: If `Error` event, returns error.
*/
public var error: ErrorProtocol? {
/// - returns: If `Error` event, returns error.
public var error: ErrorType? {
if case .Error(let error) = self {
return error
}
return nil
}
}
}

View File

@ -13,13 +13,14 @@ extension String {
This is needed because on Linux Swift doesn't have `rangeOfString(..., options: .BackwardsSearch)`
*/
func lastIndexOf(character: Character) -> Index? {
var last: Index?
for i in characters.indices.indices {
if self[i] == character {
last = i
var index = endIndex
while index > startIndex {
index = index.predecessor()
if self[index] == character {
return index
}
}
return last
return nil
}
}

Some files were not shown because too many files have changed in this diff Show More