Updates documentation with API changes.

This commit is contained in:
Krunoslav Zaher 2015-12-30 03:16:20 +01:00
parent 6bc1ae0ff9
commit f4ef06e88d
10 changed files with 106 additions and 114 deletions

View File

@ -97,9 +97,9 @@ protocol ObserverType {
If a sequence terminates in finite time, not calling `dispose` or not using `addDisposableTo(disposeBag)` won't cause any permanent resource leaks, but those resources will be used until sequence completes in some way (finishes producing elements or error happens).
If a sequence doesn't terminate in some way, resources will be allocated permanently unless `dispose` is being called manually, automatically inside of a `disposeBag`, `scopedDispose`, `takeUntil` or some other way.
If a sequence doesn't terminate in some way, resources will be allocated permanently unless `dispose` is being called manually, automatically inside of a `disposeBag`, `takeUntil` or some other way.
**Using dispose bags, scoped dispose or `takeUntil` operator are all robust ways of making sure resources are cleaned up and we recommend using them in production even though sequence will terminate in finite time.**
**Using dispose bags or `takeUntil` operator is a robust way of making sure resources are cleaned up and we recommend using them in production even though sequence will terminate in finite time.**
In case you are curious why `ErrorType` isn't generic, you can find explanation [here](DesignRationale.md#why-error-type-isnt-generic).
@ -110,9 +110,9 @@ There is one additional way an observed sequence can terminate. When you are don
Here is an example with `interval` operator.
```swift
let subscription = Observable.interval(0.3, scheduler)
.subscribe { (e: Event<Int64>) in
print(e)
let subscription = Observable<Int>.interval(0.3, scheduler: scheduler)
.subscribe { event in
print(event)
}
NSThread.sleepForTimeInterval(2)
@ -132,7 +132,7 @@ This will print:
5
```
One thing to note here is that you usually don't want to manually call `dispose` and this is only educational example. Calling dispose manually is usually bad code smell, and there are better ways to dispose subscriptions. You can either use `DisposeBag`, `ScopedDisposable`, `takeUntil` operator or some other mechanism.
One thing to note here is that you usually don't want to manually call `dispose` and this is only educational example. Calling dispose manually is usually bad code smell, and there are better ways to dispose subscriptions. You can either use `DisposeBag`, `takeUntil` operator or some other mechanism.
So can this code print something after `dispose` call executed? The answer is, it depends.
@ -154,10 +154,10 @@ A few more examples just to be sure (`observeOn` is explained [here](Schedulers.
In case you have something like:
```swift
let subscription = Observable.interval(0.3, scheduler)
.observeOn(MainScheduler.sharedInstance)
.subscribe { (e: Event<Int64>) in
print(e)
let subscription = Observable<Int>.interval(0.3, scheduler: scheduler)
.observeOn(MainScheduler.instance)
.subscribe { event in
print(event)
}
// ....
@ -171,10 +171,10 @@ subscription.dispose() // called from main thread
Also in this case:
```swift
let subscription = Observable.interval(0.3, scheduler)
let subscription = Observable<Int>.interval(0.3, scheduler: scheduler)
.observeOn(serialScheduler)
.subscribe { (e: Event<Int64>) in
print(e)
.subscribe { event in
print(event)
}
// ...
@ -201,20 +201,6 @@ That should clear references to old one and cause disposal of resources.
If that explicit manual disposal is still wanted, use `CompositeDisposable`. **It has the wanted behavior but once that `dispose` method is called, it will immediately dispose any newly added disposable.**
### Scoped Dispose
In case disposal is wanted immediately after leaving scope of execution, there is `scopedDispose()`.
```swift
let autoDispose = sequence
.subscribe {
print($0)
}
.scopedDispose()
```
This will dispose the subscription when execution leaves current scope.
### Take until
Additional way to automatically dispose subscription on dealloc is to use `takeUntil` operator.
@ -846,7 +832,7 @@ In case you want to have some resource leak detection logic, the simplest method
/* add somewhere in
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
*/
_ = Observable.interval(1, MainScheduler.sharedInstance)
_ = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
.subscribeNext { _ in
print("Resource count \(RxSwift.resourceCount)")
}
@ -880,10 +866,12 @@ let variable = Variable(0)
print("Before first subscription ---")
variable.asObservable()
.subscribeNext { n in
_ = variable.asObservable()
.subscribe(onNext: { n in
print("First \(n)")
}
}, onCompleted: {
print("Completed 1")
})
print("Before send 1")
@ -891,10 +879,12 @@ variable.value = 1
print("Before second subscription ---")
variable
.subscribeNext { n in
_ = variable.asObservable()
.subscribe(onNext: { n in
print("Second \(n)")
}
}, onCompleted: {
print("Completed 2")
})
variable.value = 2
@ -913,6 +903,8 @@ Second 1
First 2
Second 2
End ---
Completed 1
Completed 2
```
## KVO
@ -1006,7 +998,7 @@ There are certain things that your `Observable`s need to satisfy in the UI layer
It is usually a good idea for you APIs to return results on `MainScheduler`. In case you try to bind something to UI from background thread, in **Debug** build RxCocoa will usually throw an exception to inform you of that.
To fix this you need to add `observeOn(MainScheduler.sharedInstance)`.
To fix this you need to add `observeOn(MainScheduler.instance)`.
**NSURLSession extensions don't return result on `MainScheduler` by default.**

View File

@ -25,7 +25,7 @@ sequence1
.map { n in
print("This is performed on background scheduler")
}
.observeOn(MainScheduler.sharedInstance)
.observeOn(MainScheduler.instance)
.map { n in
print("This is performed on main scheduler")
}

View File

@ -116,8 +116,8 @@ E.g.
```
can't error out = catchError
observe on main scheduler = observeOn(MainScheduler.sharedInstance)
subscribe on main scheduler = subscribeOn(MainScheduler.sharedInstance)
observe on main scheduler = observeOn(MainScheduler.instance)
subscribe on main scheduler = subscribeOn(MainScheduler.instance)
sharing side effects = share* (one of the `share` operators)
```
@ -171,7 +171,7 @@ This is an typical beginner example.
```swift
let results = query.rx_text
.throttle(0.3, scheduler: MainScheduler.sharedInstance)
.throttle(0.3, scheduler: MainScheduler.instance)
.flatMapLatest { query in
fetchAutoCompleteItems(query)
}
@ -202,10 +202,10 @@ A more appropriate version of the code would look like this:
```swift
let results = query.rx_text
.throttle(0.3, scheduler: MainScheduler.sharedInstance)
.throttle(0.3, scheduler: MainScheduler.instance)
.flatMapLatest { query in
fetchAutoCompleteItems(query)
.observeOn(MainScheduler.sharedInstance) // results are returned on MainScheduler
.observeOn(MainScheduler.instance) // results are returned on MainScheduler
.catchErrorJustReturn([]) // in worst case, errors are handled
}
.shareReplay(1) // HTTP requests are shared and results replayed
@ -229,7 +229,7 @@ The following code looks almost the same:
```swift
let results = query.rx_text.asDriver() // This converts normal sequence into `Driver` sequence.
.throttle(0.3, scheduler: MainScheduler.sharedInstance)
.throttle(0.3, scheduler: MainScheduler.instance)
.flatMapLatest { query in
fetchAutoCompleteItems(query)
.asDriver(onErrorJustReturn: []) // Builder just needs info what to return in case of error.
@ -272,7 +272,7 @@ So how to make sure those properties are satisfied? Just use normal Rx operators
```
let safeSequence = xs
.observeOn(MainScheduler.sharedInstance) // 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

View File

@ -3,9 +3,9 @@
[![Travis CI](https://travis-ci.org/ReactiveX/RxSwift.svg?branch=master)](https://travis-ci.org/ReactiveX/RxSwift)
Xcode 7 beta 6 (7A192o) / Swift 2.0 required
Xcode 7 Swift 2.1 required
**This README.md describes beta version of RxSwift 2.0.**
**This README.md describes RxSwift 2.0.0 RC.**
**You can find RxSwift 1.9 for Swift 1.2 [here](https://github.com/ReactiveX/RxSwift/tree/rxswift-1.0).**
@ -38,7 +38,7 @@ You can now just write
* RxCocoa introduces `bindTo` extensions
```swift
combineLatest(firstName.rx_text, lastName.rx_text) { $0 + " " + $1 }
Observable.combineLatest(firstName.rx_text, lastName.rx_text) { $0 + " " + $1 }
.map { "Greeting \($0)" }
.bindTo(greetingLabel.rx_text)
```
@ -145,7 +145,7 @@ When writing embedded UI applications you would ideally want your program interf
These are so called bindings and Rx can help you model your system that way.
```swift
combineLatest(firstName.rx_text, lastName.rx_text) { $0 + " " + $1 }
Observable.combineLatest(firstName.rx_text, lastName.rx_text) { $0 + " " + $1 }
.map { "Greeting \($0)" }
.bindTo(greetingLabel.rx_text)
```
@ -189,7 +189,7 @@ Writing all of this and properly testing it would be tedious. This is that same
```swift
searchTextField.rx_text
.throttle(0.3, MainScheduler.sharedInstance)
.throttle(0.3, scheduler: MainScheduler.instance)
.distinctUntilChanged()
.flatMapLatest { query in
API.getSearchResults(query)
@ -214,7 +214,7 @@ Well, there is of course `zip` operator
let userRequest: Observable<User> = API.getUser("me")
let friendsRequest: Observable<Friends> = API.getFriends("me")
zip(userRequest, friendsRequest) { user, friends in
Observable.zip(userRequest, friendsRequest) { user, friends in
return (user, friends)
}
.subscribeNext { user, friends in
@ -228,10 +228,10 @@ So what if those APIs return results on a background thread, and binding has to
let userRequest: Observable<User> = API.getUser("me")
let friendsRequest: Observable<[Friend]> = API.getFriends("me")
zip(userRequest, friendsRequest) { user, friends in
Observable.zip(userRequest, friendsRequest) { user, friends in
return (user, friends)
}
.observeOn(MainScheduler.sharedInstance)
.observeOn(MainScheduler.instance)
.subscribeNext { user, friends in
// bind them to user interface
}
@ -246,7 +246,7 @@ And what if you need to create your own observable? It's pretty easy. This code
```swift
extension NSURLSession {
public func rx_response(request: NSURLRequest) -> Observable<(NSData, NSURLResponse)> {
return create { observer in
return Observable.create { observer in
let task = self.dataTaskWithRequest(request) { (data, response, error) in
guard let response = response, data = data else {
observer.on(.Error(error ?? RxCocoaURLError.Unknown))
@ -285,17 +285,17 @@ It would be also nice if we could limit the number of concurrent image operation
This is how we can do it using Rx.
```swift
let imageSubscripton = imageURLs
.throttle(0.2, MainScheduler.sharedInstance)
.flatMap { imageURL in
// this is conceptual solution
let imageSubscription = imageURLs
.throttle(0.2, scheduler: MainScheduler.instance)
.flatMapLatest { imageURL in
API.fetchImage(imageURL)
}
.observeOn(operationScheduler)
.map { imageData in
return decodeAndBlurImage(imageData)
}
.observeOn(MainScheduler.sharedInstance)
.observeOn(MainScheduler.instance)
.subscribeNext { blurredImage in
imageView.image = blurredImage
}

View File

@ -15,7 +15,7 @@ import RxSwift
Protocol that enables extension of `ControlEvent`.
*/
public protocol ControlEventType : ObservableType {
/**
- returns: `ControlEvent` interface
*/
@ -31,10 +31,10 @@ public protocol ControlEventType : ObservableType {
- it won't send any initial value on subscription
- it will `Complete` sequence on control being deallocated
- it never errors out
- it delivers events on `MainScheduler.sharedInstance`
- it delivers events on `MainScheduler.instance`
**The implementation of `ControlEvent` will ensure that sequence of events is being subscribed on main scheduler
(`subscribeOn(ConcurrentMainScheduler.sharedInstance)` behavior).**
(`subscribeOn(ConcurrentMainScheduler.instance)` behavior).**
**It is implementor's responsibility to make sure that that all other properties enumerated above are satisfied.**
@ -57,17 +57,17 @@ public struct ControlEvent<PropertyType> : ControlEventType {
public init<Ev: ObservableType where Ev.E == E>(events: Ev) {
_events = events.subscribeOn(ConcurrentMainScheduler.instance)
}
/**
Subscribes an observer to control events.
- parameter observer: Observer to subscribe to events.
- returns: Disposable object that can be used to unsubscribe the observer from receiving control events.
*/
public func subscribe<O : ObserverType where O.E == E>(observer: O) -> Disposable {
return _events.subscribe(observer)
}
/**
- returns: `Observable` interface.
*/
@ -75,7 +75,7 @@ public struct ControlEvent<PropertyType> : ControlEventType {
public func asObservable() -> Observable<E> {
return _events
}
/**
- returns: `ControlEvent` interface.
*/
@ -83,4 +83,4 @@ public struct ControlEvent<PropertyType> : ControlEventType {
public func asControlEvent() -> ControlEvent<E> {
return self
}
}
}

View File

@ -15,7 +15,7 @@ import RxSwift
Protocol that enables extension of `ControlProperty`.
*/
public protocol ControlPropertyType : ObservableType, ObserverType {
/**
- returns: `ControlProperty` interface
*/
@ -29,31 +29,31 @@ public protocol ControlPropertyType : ObservableType, ObserverType {
- it never fails
- `shareReplay(1)` behavior
- it's stateful, upon subscription (calling subscribe) last element is immediatelly replayed if it was produced
- it's stateful, upon subscription (calling subscribe) last element is immediately replayed if it was produced
- it will `Complete` sequence on control being deallocated
- it never errors out
- it delivers events on `MainScheduler.sharedInstance`
- it delivers events on `MainScheduler.instance`
**The implementation of `ControlProperty` will ensure that sequence of values is being subscribed on main scheduler
(`subscribeOn(ConcurrentMainScheduler.sharedInstance)` behavior).**
(`subscribeOn(ConcurrentMainScheduler.instance)` behavior).**
**It is implementor's responsibility to make sure that that all other properties enumerated above are satisfied.**
**If they aren't, then using this unit communicates wrong properties and could potentially break someone's code.**
**In case `values` observable sequence that is being passed into initializer doesn't satisfy all enumerated
properties, please don't use this unit.**
*/
public struct ControlProperty<PropertyType> : ControlPropertyType {
public typealias E = PropertyType
let _values: Observable<PropertyType>
let _valueSink: AnyObserver<PropertyType>
/**
Initializes control property with a observable sequence that represents property values and observer that enables
binding values to property.
- parameter values: Observable sequence that represents property values.
- parameter valueSink: Observer that enables binding values to control property.
- returns: Control property created with a observable sequence of values and an observer that enables binding values
@ -63,17 +63,17 @@ public struct ControlProperty<PropertyType> : ControlPropertyType {
_values = values.subscribeOn(ConcurrentMainScheduler.instance)
_valueSink = valueSink.asObserver()
}
/**
Subscribes an observer to control property values.
- parameter observer: Observer to subscribe to property values.
- returns: Disposable object that can be used to unsubscribe the observer from receiving control property values.
*/
public func subscribe<O : ObserverType where O.E == E>(observer: O) -> Disposable {
return _values.subscribe(observer)
}
/**
- returns: `Observable` interface.
*/
@ -81,7 +81,7 @@ public struct ControlProperty<PropertyType> : ControlPropertyType {
public func asObservable() -> Observable<E> {
return _values
}
/**
- returns: `ControlProperty` interface.
*/
@ -89,10 +89,10 @@ public struct ControlProperty<PropertyType> : ControlPropertyType {
public func asControlProperty() -> ControlProperty<E> {
return self
}
/**
Binds event to user interface.
- In case next element is received, it is being set to control value.
- In case error is received, DEBUG buids raise fatal error, RELEASE builds log event to standard output.
- In case sequence completes, nothing happens.
@ -107,4 +107,4 @@ public struct ControlProperty<PropertyType> : ControlPropertyType {
_valueSink.on(event)
}
}
}
}

View File

@ -15,7 +15,7 @@ import RxSwift
A type that can be converted to `Driver`.
*/
public protocol DriverConvertibleType : ObservableConvertibleType {
/**
Converts self to `Driver`.
*/
@ -34,7 +34,7 @@ extension DriverConvertibleType {
Unit that represents observable sequence with following properties:
- it never fails
- it delivers events on `MainScheduler.sharedInstance`
- it delivers events on `MainScheduler.instance`
- `shareReplayLatestWhileConnected()` behavior
- all observers share sequence computation resources
- it's stateful, upon subscription (calling subscribe) last element is immediatelly replayed if it was produced
@ -42,22 +42,22 @@ extension DriverConvertibleType {
- if there are no subscribers, it will release sequence computation resources
`Driver<Element>` can be considered a builder pattern for observable sequences that drive the application.
If observable sequence has produced at least one element, after new subscription is made last produced element will be
immediately replayed on the same thread on which the subscription was made.
immediately replayed on the same thread on which the subscription was made.
When using `drive*`, `subscribe*` and `bind*` family of methods, they should always be called from main thread.
If `drive*`, `subscribe*` and `bind*` are called from background thread, it is possible that initial replay
If `drive*`, `subscribe*` and `bind*` are called from background thread, it is possible that initial replay
will happen on background thread, and subsequent events will arrive on main thread.
To find out more about units and how to use them, please visit `Documentation/Units.md`.
*/
public struct Driver<Element> : DriverConvertibleType {
public typealias E = Element
let _source: Observable<E>
init(_ source: Observable<E>) {
self._source = source.shareReplayLatestWhileConnected()
}
@ -91,30 +91,30 @@ public struct Driver<Element> : DriverConvertibleType {
extension Driver {
/**
Returns an empty observable sequence, using the specified scheduler to send out the single `Completed` message.
- returns: An observable sequence with no elements.
*/
@warn_unused_result(message="http://git.io/rxs.uo")
public static func empty() -> Driver<E> {
return Driver(raw: Observable.empty().subscribeOn(driverSubscribeOnScheduler))
}
/**
Returns a non-terminating observable sequence, which can be used to denote an infinite duration.
- returns: An observable sequence whose observers will never get called.
*/
@warn_unused_result(message="http://git.io/rxs.uo")
public static func never() -> Driver<E> {
return Driver(raw: Observable.never().subscribeOn(driverSubscribeOnScheduler))
}
/**
Returns an observable sequence that contains a single element.
- parameter element: Single element in the resulting observable sequence.
- returns: An observable sequence containing the single specified element.
*/
@ -140,7 +140,7 @@ extension Driver {
let source = elements.toObservable().subscribeOn(driverSubscribeOnScheduler)
return Driver(raw: source)
}
}
public struct Drive {
@ -171,13 +171,13 @@ public struct Drive {
let source = elements.toObservable().subscribeOn(driverSubscribeOnScheduler)
return Driver(raw: source)
}
}
/**
This method can be used in unit tests to ensure that driver is using mock schedulers instead of
This method can be used in unit tests to ensure that driver is using mock schedulers instead of
maind schedulers.
**This shouldn't be used in normal release builds.**
*/
public func driveOnScheduler(scheduler: SchedulerType, action: () -> ()) {

View File

@ -52,7 +52,7 @@ class ViewController: OSViewController {
/* add somewhere in
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
*/
_ = Observable.interval(1, MainScheduler.sharedInstance)
_ = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
.subscribeNext { _ in
print("Resource count \(RxSwift.resourceCount)")
}

View File

@ -45,7 +45,7 @@ public final class MainScheduler : SerialDispatchQueueScheduler {
*/
public class func ensureExecutingOnScheduler() {
if !NSThread.currentThread().isMainThread {
rxFatalError("Executing on backgound thread. Please use `MainScheduler.sharedInstance.schedule` to schedule work on main thread.")
rxFatalError("Executing on backgound thread. Please use `MainScheduler.instance.schedule` to schedule work on main thread.")
}
}

View File

@ -22,8 +22,8 @@ class DriverTest : RxTest {
// test helpers that make sure that resulting driver operator honors definition
// * only one subscription is made and shared - shareReplay(1)
// * subscription is made on main thread - subscribeOn(ConcurrentMainScheduler.sharedInstance)
// * events are observed on main thread - observeOn(MainScheduler.sharedInstance)
// * subscription is made on main thread - subscribeOn(ConcurrentMainScheduler.instance)
// * events are observed on main thread - observeOn(MainScheduler.instance)
// * it can't error out - it needs to have catch somewhere
extension DriverTest {
@ -67,7 +67,7 @@ extension DriverTest {
subscribing = false
// Subscription should be made on main scheduler
// so this will make sure execution is continued after
// so this will make sure execution is continued after
// subscription because of serial nature of main scheduler.
MainScheduler.instance.schedule(()) { _ in
subscribeFinished.fulfill()
@ -641,7 +641,7 @@ extension DriverTest {
XCTAssertEqual(results, [2, 3, 0])
}
}
// MARK: merge
@ -744,7 +744,7 @@ extension DriverTest {
XCTAssertEqual(results, [1, 3, 2])
}
}
// MARK: concat
@ -826,7 +826,7 @@ extension DriverTest {
XCTAssertTrue(hotObservable1.subscriptions == [UnsunscribedFromHotObservable])
XCTAssertTrue(hotObservable2.subscriptions == [UnsunscribedFromHotObservable])
}
XCTAssertEqual(results, [5, 6, 7, 4, -3])
}
@ -852,7 +852,7 @@ extension DriverTest {
XCTAssertTrue(hotObservable1.subscriptions == [UnsunscribedFromHotObservable])
XCTAssertTrue(hotObservable2.subscriptions == [UnsunscribedFromHotObservable])
}
XCTAssertEqual(results, [5, 6, 7, 4, -3])
}
}
@ -907,7 +907,7 @@ extension DriverTest {
XCTAssertTrue(hotObservable1.subscriptions == [UnsunscribedFromHotObservable])
XCTAssertTrue(hotObservable2.subscriptions == [UnsunscribedFromHotObservable])
}
XCTAssertEqual(results, [5, 7, -3])
}
}
@ -962,8 +962,8 @@ extension DriverTest {
XCTAssertTrue(hotObservable1.subscriptions == [UnsunscribedFromHotObservable])
XCTAssertTrue(hotObservable2.subscriptions == [UnsunscribedFromHotObservable])
}
XCTAssertEqual(results, [4, 5])
}
}
}