diff --git a/Documentation/GettingStarted.md b/Documentation/GettingStarted.md index c656fc64..b91af9e5 100644 --- a/Documentation/GettingStarted.md +++ b/Documentation/GettingStarted.md @@ -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) in - print(e) +let subscription = Observable.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) in - print(e) +let subscription = Observable.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.interval(0.3, scheduler: scheduler) .observeOn(serialScheduler) - .subscribe { (e: Event) 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.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.** diff --git a/Documentation/Schedulers.md b/Documentation/Schedulers.md index 80b5c576..95c4589c 100644 --- a/Documentation/Schedulers.md +++ b/Documentation/Schedulers.md @@ -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") } diff --git a/Documentation/Units.md b/Documentation/Units.md index da85c43a..b0fa337d 100644 --- a/Documentation/Units.md +++ b/Documentation/Units.md @@ -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 diff --git a/README.md b/README.md index e4ba3826..e814054e 100644 --- a/README.md +++ b/README.md @@ -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 = API.getUser("me") let friendsRequest: Observable = 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 = 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 } diff --git a/RxCocoa/Common/CocoaUnits/ControlEvent.swift b/RxCocoa/Common/CocoaUnits/ControlEvent.swift index b85395f2..8157389e 100644 --- a/RxCocoa/Common/CocoaUnits/ControlEvent.swift +++ b/RxCocoa/Common/CocoaUnits/ControlEvent.swift @@ -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 : ControlEventType { public init(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(observer: O) -> Disposable { return _events.subscribe(observer) } - + /** - returns: `Observable` interface. */ @@ -75,7 +75,7 @@ public struct ControlEvent : ControlEventType { public func asObservable() -> Observable { return _events } - + /** - returns: `ControlEvent` interface. */ @@ -83,4 +83,4 @@ public struct ControlEvent : ControlEventType { public func asControlEvent() -> ControlEvent { return self } -} \ No newline at end of file +} diff --git a/RxCocoa/Common/CocoaUnits/ControlProperty.swift b/RxCocoa/Common/CocoaUnits/ControlProperty.swift index fc89f62e..a432f0ea 100644 --- a/RxCocoa/Common/CocoaUnits/ControlProperty.swift +++ b/RxCocoa/Common/CocoaUnits/ControlProperty.swift @@ -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 : ControlPropertyType { public typealias E = PropertyType - + let _values: Observable let _valueSink: AnyObserver /** 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 : 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(observer: O) -> Disposable { return _values.subscribe(observer) } - + /** - returns: `Observable` interface. */ @@ -81,7 +81,7 @@ public struct ControlProperty : ControlPropertyType { public func asObservable() -> Observable { return _values } - + /** - returns: `ControlProperty` interface. */ @@ -89,10 +89,10 @@ public struct ControlProperty : ControlPropertyType { public func asControlProperty() -> ControlProperty { 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 : ControlPropertyType { _valueSink.on(event) } } -} \ No newline at end of file +} diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver.swift index 0348f631..04976af3 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/Driver.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver.swift @@ -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` 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 : DriverConvertibleType { public typealias E = Element - + let _source: Observable - + init(_ source: Observable) { self._source = source.shareReplayLatestWhileConnected() } @@ -91,30 +91,30 @@ public struct Driver : 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 { 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 { 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: () -> ()) { diff --git a/RxExample/RxExample/ViewController.swift b/RxExample/RxExample/ViewController.swift index f3cb84b3..cd7b0233 100644 --- a/RxExample/RxExample/ViewController.swift +++ b/RxExample/RxExample/ViewController.swift @@ -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.interval(1, scheduler: MainScheduler.instance) .subscribeNext { _ in print("Resource count \(RxSwift.resourceCount)") } diff --git a/RxSwift/Schedulers/MainScheduler.swift b/RxSwift/Schedulers/MainScheduler.swift index 574df25a..4648e3b9 100644 --- a/RxSwift/Schedulers/MainScheduler.swift +++ b/RxSwift/Schedulers/MainScheduler.swift @@ -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.") } } diff --git a/Tests/RxCocoaTests/Driver+Test.swift b/Tests/RxCocoaTests/Driver+Test.swift index f7d9064f..7738edf3 100644 --- a/Tests/RxCocoaTests/Driver+Test.swift +++ b/Tests/RxCocoaTests/Driver+Test.swift @@ -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]) } -} \ No newline at end of file +}