From 0e9be4980dda46ef6fc517fd3ac382410849856d Mon Sep 17 00:00:00 2001 From: Thane Gill Date: Mon, 11 Jan 2016 17:21:17 -0800 Subject: [PATCH 1/8] Add doOn shortcut operators --- RxSwift/Observables/Observable+Single.swift | 36 +++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/RxSwift/Observables/Observable+Single.swift b/RxSwift/Observables/Observable+Single.swift index ac45efa9..41fb76f7 100644 --- a/RxSwift/Observables/Observable+Single.swift +++ b/RxSwift/Observables/Observable+Single.swift @@ -113,6 +113,42 @@ extension ObservableType { } } } + + /** + Invokes an action for each Next event in the observable sequence, and propagates all observer messages through the result sequence. + + - parameter onNext: Action to invoke for each element in the observable sequence. + - returns: The source sequence with the side-effecting behavior applied. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func doOnNext(onNext: (E throws -> Void)) + -> Observable { + return self.doOn(onNext: onNext) + } + + /** + Invokes an action for the Error event in the observable sequence, and propagates all observer messages through the result sequence. + + - parameter onError: Action to invoke upon errored termination of the observable sequence. + - returns: The source sequence with the side-effecting behavior applied. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func doOnError(onError: (ErrorType throws -> Void)) + -> Observable { + return self.doOn(onError: onError) + } + + /** + Invokes an action for the Completed event in the observable sequence, and propagates all observer messages through the result sequence. + + - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence. + - returns: The source sequence with the side-effecting behavior applied. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func doOnComplete(onCompleted: (() throws -> Void)) + -> Observable { + return self.doOn(onCompleted: onCompleted) + } } // MARK: startWith From b0a990e9529b6a794636835e5a8828c263f4611c Mon Sep 17 00:00:00 2001 From: Thane Gill Date: Sun, 17 Jan 2016 09:46:14 -0800 Subject: [PATCH 2/8] Use new doOnNext shortcut operator --- RxExample/RxExample/Services/ImageService.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/RxExample/RxExample/Services/ImageService.swift b/RxExample/RxExample/Services/ImageService.swift index 788b15a1..d49303d2 100644 --- a/RxExample/RxExample/Services/ImageService.swift +++ b/RxExample/RxExample/Services/ImageService.swift @@ -75,17 +75,17 @@ class DefaultImageService: ImageService { else { // fetch from network decodedImage = self.$.URLSession.rx_data(NSURLRequest(URL: URL)) - .doOn(onNext: { data in + .doOnNext { data in self._imageDataCache.setObject(data, forKey: URL) - }) + } .flatMap(self.decodeImage) .trackActivity(self.loadingImage) } } - return decodedImage.doOn(onNext: { image in + return decodedImage.doOnNext { image in self._imageCache.setObject(image, forKey: URL) - }) + } } } From 0ce634ca50a3b528c05ddb3b6a8110f6258d354e Mon Sep 17 00:00:00 2001 From: Thane Gill Date: Sun, 17 Jan 2016 11:08:01 -0800 Subject: [PATCH 3/8] Rename tests to doOn instead of Do --- RxSwift/Observables/Observable+Single.swift | 2 +- .../Tests/Observable+SingleTest.swift | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/RxSwift/Observables/Observable+Single.swift b/RxSwift/Observables/Observable+Single.swift index 41fb76f7..2aeba9b7 100644 --- a/RxSwift/Observables/Observable+Single.swift +++ b/RxSwift/Observables/Observable+Single.swift @@ -71,7 +71,7 @@ extension ObservableType { } } -// MARK: do +// MARK: doOn extension ObservableType { diff --git a/Tests/RxSwiftTests/Tests/Observable+SingleTest.swift b/Tests/RxSwiftTests/Tests/Observable+SingleTest.swift index 613cb044..23fc4e01 100644 --- a/Tests/RxSwiftTests/Tests/Observable+SingleTest.swift +++ b/Tests/RxSwiftTests/Tests/Observable+SingleTest.swift @@ -271,9 +271,9 @@ extension ObservableSingleTest { } } -// Do +// doOn extension ObservableSingleTest { - func testDo_shouldSeeAllValues() { + func testDoOn_shouldSeeAllValues() { let scheduler = TestScheduler(initialClock: 0) let xs = scheduler.createHotObservable([ @@ -317,7 +317,7 @@ extension ObservableSingleTest { XCTAssertEqual(xs.subscriptions, correctSubscriptions) } - func testDo_plainAction() { + func testDoOn_plainAction() { let scheduler = TestScheduler(initialClock: 0) let xs = scheduler.createHotObservable([ @@ -356,8 +356,8 @@ extension ObservableSingleTest { XCTAssertEqual(res.events, correctMessages) XCTAssertEqual(xs.subscriptions, correctSubscriptions) } - - func testDo_nextCompleted() { + + func testDoOn_nextCompleted() { let scheduler = TestScheduler(initialClock: 0) let xs = scheduler.createHotObservable([ @@ -404,7 +404,7 @@ extension ObservableSingleTest { XCTAssertEqual(xs.subscriptions, correctSubscriptions) } - func testDo_completedNever() { + func testDoOn_completedNever() { let scheduler = TestScheduler(initialClock: 0) let recordedEvents: [Recorded>] = [ @@ -439,7 +439,7 @@ extension ObservableSingleTest { XCTAssertEqual(xs.subscriptions, correctSubscriptions) } - func testDo_nextError() { + func testDoOn_nextError() { let scheduler = TestScheduler(initialClock: 0) let xs = scheduler.createHotObservable([ @@ -486,7 +486,7 @@ extension ObservableSingleTest { XCTAssertEqual(xs.subscriptions, correctSubscriptions) } - func testDo_nextErrorNot() { + func testDoOn_nextErrorNot() { let scheduler = TestScheduler(initialClock: 0) let xs = scheduler.createHotObservable([ @@ -533,7 +533,7 @@ extension ObservableSingleTest { XCTAssertEqual(xs.subscriptions, correctSubscriptions) } - func testDo_Throws() { + func testDoOn_Throws() { let scheduler = TestScheduler(initialClock: 0) let xs = scheduler.createHotObservable([ From badb02f5c354120d47f2e889c10fc11daa7740b9 Mon Sep 17 00:00:00 2001 From: Thane Gill Date: Sun, 17 Jan 2016 11:37:46 -0800 Subject: [PATCH 4/8] Add 6 tests for doOn* shortcuts --- .../Tests/Observable+SingleTest.swift | 207 ++++++++++++++++++ 1 file changed, 207 insertions(+) diff --git a/Tests/RxSwiftTests/Tests/Observable+SingleTest.swift b/Tests/RxSwiftTests/Tests/Observable+SingleTest.swift index 23fc4e01..2b041e91 100644 --- a/Tests/RxSwiftTests/Tests/Observable+SingleTest.swift +++ b/Tests/RxSwiftTests/Tests/Observable+SingleTest.swift @@ -558,6 +558,213 @@ extension ObservableSingleTest { XCTAssertEqual(res.events, correctMessages) XCTAssertEqual(xs.subscriptions, correctSubscriptions) } + + func testDoOnNext_normal() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5), + completed(250) + ]) + + var numberOfTimesInvoked = 0 + + let res = scheduler.start { xs.doOnNext { error in + numberOfTimesInvoked = numberOfTimesInvoked + 1 + } + } + + let correctMessages = [ + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5), + completed(250) + ] + + let correctSubscriptions = [ + Subscription(200, 250) + ] + + XCTAssertEqual(res.events, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + + XCTAssertEqual(numberOfTimesInvoked, 4) + } + + func testDoOnNext_throws() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5), + completed(250) + ]) + + var numberOfTimesInvoked = 0 + + let res = scheduler.start { xs.doOnNext { error in + if numberOfTimesInvoked > 2 { + throw testError + } + numberOfTimesInvoked = numberOfTimesInvoked + 1 + } + } + + let correctMessages = [ + next(210, 2), + next(220, 3), + next(230, 4), + error(240, testError) + ] + + let correctSubscriptions = [ + Subscription(200, 240) + ] + + XCTAssertEqual(res.events, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + + XCTAssertEqual(numberOfTimesInvoked, 3) + } + + func testDoOnError_normal() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + error(250, testError) + ]) + + var recordedError: ErrorType! + var numberOfTimesInvoked = 0 + + let res = scheduler.start { xs.doOnError { error in + recordedError = error + numberOfTimesInvoked = numberOfTimesInvoked + 1 + } + } + + let correctMessages = [ + next(210, 2), + error(250, testError) + ] + + let correctSubscriptions = [ + Subscription(200, 250) + ] + + XCTAssertEqual(res.events, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + + XCTAssertEqual(recordedError as NSError, testError) + XCTAssertEqual(numberOfTimesInvoked, 1) + } + + func testDoOnError_throws() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + error(250, testError) + ]) + + let res = scheduler.start { xs.doOnError { _ in + throw testError + } + } + + let correctMessages = [ + next(210, 2), + error(250, testError) + ] + + let correctSubscriptions = [ + Subscription(200, 250) + ] + + XCTAssertEqual(res.events, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } + + func testDoOnComplete_normal() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5), + completed(250) + ]) + + var didComplete = false + + let res = scheduler.start { xs.doOnComplete { error in + didComplete = true + } + } + + let correctMessages = [ + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5), + completed(250) + ] + + let correctSubscriptions = [ + Subscription(200, 250) + ] + + XCTAssertEqual(res.events, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + + XCTAssertEqual(didComplete, true) + } + + func testDoOnComplete_throws() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5), + completed(250) + ]) + + let res = scheduler.start { xs.doOnComplete { error in + throw testError + } + } + + let correctMessages = [ + next(210, 2), + next(220, 3), + next(230, 4), + next(240, 5), + error(250, testError) + ] + + let correctSubscriptions = [ + Subscription(200, 250) + ] + + XCTAssertEqual(res.events, correctMessages) + XCTAssertEqual(xs.subscriptions, correctSubscriptions) + } } // retry From b225572d9a991ad9413ec13ad7415a76fc953047 Mon Sep 17 00:00:00 2001 From: Thane Gill Date: Sun, 17 Jan 2016 12:41:40 -0800 Subject: [PATCH 5/8] Add doOn shortcuts to Driver --- .../CocoaUnits/Driver/Driver+Operators.swift | 24 +++++++++ Tests/RxCocoaTests/Driver+Test.swift | 49 +++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift index d56ffde3..24ba5b4a 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift @@ -136,6 +136,30 @@ extension DriverConvertibleType { return Driver(source) } + + /** + Invokes an action for each Next event in the observable sequence, and propagates all observer messages through the result sequence. + + - parameter onNext: Action to invoke for each element in the observable sequence. + - returns: The source sequence with the side-effecting behavior applied. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func doOnNext(onNext: (E -> Void)) + -> Driver { + return self.doOn(onNext: onNext) + } + + /** + Invokes an action for the Completed event in the observable sequence, and propagates all observer messages through the result sequence. + + - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence. + - returns: The source sequence with the side-effecting behavior applied. + */ + @warn_unused_result(message="http://git.io/rxs.uo") + public func doOnComplete(onCompleted: (() -> Void)) + -> Driver { + return self.doOn(onCompleted: onCompleted) + } } extension DriverConvertibleType { diff --git a/Tests/RxCocoaTests/Driver+Test.swift b/Tests/RxCocoaTests/Driver+Test.swift index 007483ea..a3c61d4f 100644 --- a/Tests/RxCocoaTests/Driver+Test.swift +++ b/Tests/RxCocoaTests/Driver+Test.swift @@ -537,6 +537,55 @@ extension DriverTest { let expectedEvents = [.Next(1), .Next(2), .Next(-1), .Completed] as [Event] XCTAssertEqual(events, expectedEvents) } + + + func testAsDriver_doOnNext() { + let hotObservable = BackgroundThreadPrimitiveHotObservable() + + var events = [Int]() + + let driver = hotObservable.asDriver(onErrorJustReturn: -1).doOnNext { e in + XCTAssertTrue(isMainThread()) + events.append(e) + } + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(1)) + hotObservable.on(.Next(2)) + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [1, 2, -1]) + let expectedEvents = [1, 2, -1] + XCTAssertEqual(events, expectedEvents) + } + + func testAsDriver_doOnComplete() { + let hotObservable = BackgroundThreadPrimitiveHotObservable() + + var completed = false + let driver = hotObservable.asDriver(onErrorJustReturn: -1).doOnComplete { e in + XCTAssertTrue(isMainThread()) + completed = true + } + + let results = subscribeTwiceOnBackgroundSchedulerAndOnlyOneSubscription(driver) { + XCTAssertTrue(hotObservable.subscriptions == [SubscribedToHotObservable]) + + hotObservable.on(.Next(1)) + hotObservable.on(.Next(2)) + hotObservable.on(.Error(testError)) + + XCTAssertTrue(hotObservable.subscriptions == [UnsunscribedFromHotObservable]) + } + + XCTAssertEqual(results, [1, 2, -1]) + XCTAssertEqual(completed, true) + } } // MARK: distinct until change From 7a57b9360de4723f067599b2d1738f0c7a2b4046 Mon Sep 17 00:00:00 2001 From: Thane Gill Date: Sun, 17 Jan 2016 13:14:48 -0800 Subject: [PATCH 6/8] Remove onError from Driver --- RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift index 24ba5b4a..b462946d 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift @@ -129,10 +129,10 @@ extension DriverConvertibleType { - returns: The source sequence with the side-effecting behavior applied. */ @warn_unused_result(message="http://git.io/rxs.uo") - public func doOn(onNext onNext: (E -> Void)? = nil, onError: (ErrorType -> Void)? = nil, onCompleted: (() -> Void)? = nil) + public func doOn(onNext onNext: (E -> Void)? = nil, onCompleted: (() -> Void)? = nil) -> Driver { let source = self.asObservable() - .doOn(onNext: onNext, onError: onError, onCompleted: onCompleted) + .doOn(onNext: onNext, onCompleted: onCompleted) return Driver(source) } From e9057b98c2e7620795d96be178c42be2521467d4 Mon Sep 17 00:00:00 2001 From: Thane Gill Date: Wed, 20 Jan 2016 14:28:15 -0800 Subject: [PATCH 7/8] Rename doOnComplete to doOnCompleted --- RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift | 2 +- RxSwift/Observables/Observable+Single.swift | 2 +- Tests/RxCocoaTests/Driver+Test.swift | 4 ++-- Tests/RxSwiftTests/Tests/Observable+SingleTest.swift | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift index b462946d..02948480 100644 --- a/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift +++ b/RxCocoa/Common/CocoaUnits/Driver/Driver+Operators.swift @@ -156,7 +156,7 @@ extension DriverConvertibleType { - returns: The source sequence with the side-effecting behavior applied. */ @warn_unused_result(message="http://git.io/rxs.uo") - public func doOnComplete(onCompleted: (() -> Void)) + public func doOnCompleted(onCompleted: (() -> Void)) -> Driver { return self.doOn(onCompleted: onCompleted) } diff --git a/RxSwift/Observables/Observable+Single.swift b/RxSwift/Observables/Observable+Single.swift index 2aeba9b7..969f641d 100644 --- a/RxSwift/Observables/Observable+Single.swift +++ b/RxSwift/Observables/Observable+Single.swift @@ -145,7 +145,7 @@ extension ObservableType { - returns: The source sequence with the side-effecting behavior applied. */ @warn_unused_result(message="http://git.io/rxs.uo") - public func doOnComplete(onCompleted: (() throws -> Void)) + public func doOnCompleted(onCompleted: (() throws -> Void)) -> Observable { return self.doOn(onCompleted: onCompleted) } diff --git a/Tests/RxCocoaTests/Driver+Test.swift b/Tests/RxCocoaTests/Driver+Test.swift index a3c61d4f..03f13505 100644 --- a/Tests/RxCocoaTests/Driver+Test.swift +++ b/Tests/RxCocoaTests/Driver+Test.swift @@ -564,11 +564,11 @@ extension DriverTest { XCTAssertEqual(events, expectedEvents) } - func testAsDriver_doOnComplete() { + func testAsDriver_doOnCompleted() { let hotObservable = BackgroundThreadPrimitiveHotObservable() var completed = false - let driver = hotObservable.asDriver(onErrorJustReturn: -1).doOnComplete { e in + let driver = hotObservable.asDriver(onErrorJustReturn: -1).doOnCompleted { e in XCTAssertTrue(isMainThread()) completed = true } diff --git a/Tests/RxSwiftTests/Tests/Observable+SingleTest.swift b/Tests/RxSwiftTests/Tests/Observable+SingleTest.swift index 2b041e91..e5d83495 100644 --- a/Tests/RxSwiftTests/Tests/Observable+SingleTest.swift +++ b/Tests/RxSwiftTests/Tests/Observable+SingleTest.swift @@ -696,7 +696,7 @@ extension ObservableSingleTest { XCTAssertEqual(xs.subscriptions, correctSubscriptions) } - func testDoOnComplete_normal() { + func testDoOnCompleted_normal() { let scheduler = TestScheduler(initialClock: 0) let xs = scheduler.createHotObservable([ @@ -710,7 +710,7 @@ extension ObservableSingleTest { var didComplete = false - let res = scheduler.start { xs.doOnComplete { error in + let res = scheduler.start { xs.doOnCompleted { error in didComplete = true } } @@ -733,7 +733,7 @@ extension ObservableSingleTest { XCTAssertEqual(didComplete, true) } - func testDoOnComplete_throws() { + func testDoOnCompleted_throws() { let scheduler = TestScheduler(initialClock: 0) let xs = scheduler.createHotObservable([ @@ -745,7 +745,7 @@ extension ObservableSingleTest { completed(250) ]) - let res = scheduler.start { xs.doOnComplete { error in + let res = scheduler.start { xs.doOnCompleted { error in throw testError } } From b4eb61419b7b779af5a4be8da57f42ebe6d5af3d Mon Sep 17 00:00:00 2001 From: Thane Gill Date: Wed, 20 Jan 2016 14:31:39 -0800 Subject: [PATCH 8/8] [testDoOnError_throws] Use different error than original one --- Tests/RxSwiftTests/Tests/Observable+SingleTest.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/RxSwiftTests/Tests/Observable+SingleTest.swift b/Tests/RxSwiftTests/Tests/Observable+SingleTest.swift index e5d83495..8cedce6f 100644 --- a/Tests/RxSwiftTests/Tests/Observable+SingleTest.swift +++ b/Tests/RxSwiftTests/Tests/Observable+SingleTest.swift @@ -679,13 +679,13 @@ extension ObservableSingleTest { ]) let res = scheduler.start { xs.doOnError { _ in - throw testError + throw testError1 } } let correctMessages = [ next(210, 2), - error(250, testError) + error(250, testError1) ] let correctSubscriptions = [