From 69b157f3568d924718e7738dac6c5a2d1641cb9f Mon Sep 17 00:00:00 2001 From: Ayal Spitz Date: Sat, 14 Oct 2017 11:34:22 -0400 Subject: [PATCH 01/10] Added the ability to set a default value for the sampler to return in the event that there were no new elements between sampler ticks --- RxSwift/Observables/Sample.swift | 18 +++++--- .../RxSwiftTests/Observable+SampleTests.swift | 45 +++++++++++++++++++ 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/RxSwift/Observables/Sample.swift b/RxSwift/Observables/Sample.swift index a7d3bb381..8087878d8 100644 --- a/RxSwift/Observables/Sample.swift +++ b/RxSwift/Observables/Sample.swift @@ -13,16 +13,18 @@ extension ObservableType { Upon each sampling tick, the latest element (if any) in the source sequence during the last sampling interval is sent to the resulting sequence. - **In case there were no new elements between sampler ticks, no element is sent to the resulting sequence.** + **In case there were no new elements between sampler ticks, if a default value has been set then it is sent + to the resulting sequence otherwise no element is sent.** - seealso: [sample operator on reactivex.io](http://reactivex.io/documentation/operators/sample.html) - parameter sampler: Sampling tick sequence. + defaultValue: a value to return if there are new new elements between sampler ticks - returns: Sampled observable sequence. */ - public func sample(_ sampler: Source) + public func sample(_ sampler: Source, defaultValue: Element? = nil) -> Observable { - return Sample(source: self.asObservable(), sampler: sampler.asObservable()) + return Sample(source: self.asObservable(), sampler: sampler.asObservable(), defaultValue: defaultValue) } } @@ -54,6 +56,8 @@ final private class SamplerSink if let element = parent.element { self.parent.element = nil self.parent.forwardOn(.next(element)) + } else if let element = self.parent.parent.defaultValue { + self.parent.forwardOn(.next(element)) } if self.parent.atEnd { @@ -75,7 +79,7 @@ final private class SampleSequenceSink typealias Element = Observer.Element typealias Parent = Sample - private let parent: Parent + fileprivate let parent: Parent let lock = RecursiveLock() @@ -119,10 +123,12 @@ final private class SampleSequenceSink final private class Sample: Producer { fileprivate let source: Observable fileprivate let sampler: Observable - - init(source: Observable, sampler: Observable) { + fileprivate let _defaultValue: Element? + + init(source: Observable, sampler: Observable, defaultValue: Element? = nil) { self.source = source self.sampler = sampler + self.defaultValue = defaultValue } override func run(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element { diff --git a/Tests/RxSwiftTests/Observable+SampleTests.swift b/Tests/RxSwiftTests/Observable+SampleTests.swift index 1cebe86d5..89e431d75 100644 --- a/Tests/RxSwiftTests/Observable+SampleTests.swift +++ b/Tests/RxSwiftTests/Observable+SampleTests.swift @@ -14,6 +14,51 @@ class ObservableSampleTest : RxTest { } extension ObservableSampleTest { + func testSample_Sampler_Default() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(220, 2), + next(240, 3), + next(290, 4), + next(300, 5), + next(310, 6), + completed(400) + ]) + + let ys = scheduler.createHotObservable([ + next(150, ""), + next(210, "bar"), + next(250, "foo"), + next(260, "qux"), + next(320, "baz"), + completed(500) + ]) + + let res = scheduler.start { + xs.sample(ys, defaultValue: 0) + } + + let correct = [ + next(210, 0), + next(250, 3), + next(260, 0), + next(320, 6), + completed(500) + ] + + XCTAssertEqual(res.events, correct) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 400) + ]) + + XCTAssertEqual(ys.subscriptions, [ + Subscription(200, 500) + ]) + } + func testSample_Sampler_SamplerThrows() { let scheduler = TestScheduler(initialClock: 0) From 23b8d3e24e6c31a5cef6f67b8cafda9c127a73d5 Mon Sep 17 00:00:00 2001 From: Ayal Spitz Date: Sat, 14 Oct 2017 11:34:22 -0400 Subject: [PATCH 02/10] Added the ability to set a default value for the sampler to return in the event that there were no new elements between sampler ticks --- .../RxSwiftTests/Observable+SampleTests.swift | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/Tests/RxSwiftTests/Observable+SampleTests.swift b/Tests/RxSwiftTests/Observable+SampleTests.swift index 89e431d75..5cc844589 100644 --- a/Tests/RxSwiftTests/Observable+SampleTests.swift +++ b/Tests/RxSwiftTests/Observable+SampleTests.swift @@ -59,6 +59,51 @@ extension ObservableSampleTest { ]) } + func testSample_Sampler_SamplerThrows() { + let scheduler = TestScheduler(initialClock: 0) + + let xs = scheduler.createHotObservable([ + next(150, 1), + next(220, 2), + next(240, 3), + next(290, 4), + next(300, 5), + next(310, 6), + completed(400) + ]) + + let ys = scheduler.createHotObservable([ + next(150, ""), + next(210, "bar"), + next(250, "foo"), + next(260, "qux"), + next(320, "baz"), + completed(500) + ]) + + let res = scheduler.start { + xs.sample(ys, defaultValue: 0) + } + + let correct = [ + next(210, 0), + next(250, 3), + next(260, 0), + next(320, 6), + completed(500) + ] + + XCTAssertEqual(res.events, correct) + + XCTAssertEqual(xs.subscriptions, [ + Subscription(200, 400) + ]) + + XCTAssertEqual(ys.subscriptions, [ + Subscription(200, 500) + ]) + } + func testSample_Sampler_SamplerThrows() { let scheduler = TestScheduler(initialClock: 0) From c6d997f7a1ce2c743708c53307d9b2b89d3a545f Mon Sep 17 00:00:00 2001 From: Ayal Spitz Date: Thu, 2 Jan 2020 22:53:09 -0500 Subject: [PATCH 03/10] Updated code for RxSwift 6 --- Sources/AllTestz/main.swift | 2 +- .../RxSwiftTests/Observable+SampleTests.swift | 85 +++++-------------- 2 files changed, 21 insertions(+), 66 deletions(-) diff --git a/Sources/AllTestz/main.swift b/Sources/AllTestz/main.swift index fdb3e3079..ed9b3d9ee 100644 --- a/Sources/AllTestz/main.swift +++ b/Sources/AllTestz/main.swift @@ -2165,4 +2165,4 @@ func XCTMain(_ tests: [() -> Void]) { testCase(SingleTest_.allTests), testCase(VirtualSchedulerTest_.allTests), ]) -//} +//} \ No newline at end of file diff --git a/Tests/RxSwiftTests/Observable+SampleTests.swift b/Tests/RxSwiftTests/Observable+SampleTests.swift index 5cc844589..140c36ce5 100644 --- a/Tests/RxSwiftTests/Observable+SampleTests.swift +++ b/Tests/RxSwiftTests/Observable+SampleTests.swift @@ -18,80 +18,35 @@ extension ObservableSampleTest { let scheduler = TestScheduler(initialClock: 0) let xs = scheduler.createHotObservable([ - next(150, 1), - next(220, 2), - next(240, 3), - next(290, 4), - next(300, 5), - next(310, 6), - completed(400) - ]) - - let ys = scheduler.createHotObservable([ - next(150, ""), - next(210, "bar"), - next(250, "foo"), - next(260, "qux"), - next(320, "baz"), - completed(500) - ]) - - let res = scheduler.start { - xs.sample(ys, defaultValue: 0) - } - - let correct = [ - next(210, 0), - next(250, 3), - next(260, 0), - next(320, 6), - completed(500) - ] - - XCTAssertEqual(res.events, correct) - - XCTAssertEqual(xs.subscriptions, [ - Subscription(200, 400) - ]) - - XCTAssertEqual(ys.subscriptions, [ - Subscription(200, 500) - ]) - } - - func testSample_Sampler_SamplerThrows() { - let scheduler = TestScheduler(initialClock: 0) - - let xs = scheduler.createHotObservable([ - next(150, 1), - next(220, 2), - next(240, 3), - next(290, 4), - next(300, 5), - next(310, 6), - completed(400) + .next(150, 1), + .next(220, 2), + .next(240, 3), + .next(290, 4), + .next(300, 5), + .next(310, 6), + .completed(400) ]) let ys = scheduler.createHotObservable([ - next(150, ""), - next(210, "bar"), - next(250, "foo"), - next(260, "qux"), - next(320, "baz"), - completed(500) + .next(150, ""), + .next(210, "bar"), + .next(250, "foo"), + .next(260, "qux"), + .next(320, "baz"), + .completed(500) ]) let res = scheduler.start { xs.sample(ys, defaultValue: 0) } - let correct = [ - next(210, 0), - next(250, 3), - next(260, 0), - next(320, 6), - completed(500) - ] + let correct = Recorded.events( + .next(210, 0), + .next(250, 3), + .next(260, 0), + .next(320, 6), + .completed(500) + ) XCTAssertEqual(res.events, correct) From 020abfd95fc632656d04a2a366661f5262083aab Mon Sep 17 00:00:00 2001 From: Ayal Spitz Date: Thu, 2 Jan 2020 23:17:56 -0500 Subject: [PATCH 04/10] fixed extra underscore --- RxSwift/Observables/Sample.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RxSwift/Observables/Sample.swift b/RxSwift/Observables/Sample.swift index 8087878d8..a6fc1212f 100644 --- a/RxSwift/Observables/Sample.swift +++ b/RxSwift/Observables/Sample.swift @@ -123,7 +123,7 @@ final private class SampleSequenceSink final private class Sample: Producer { fileprivate let source: Observable fileprivate let sampler: Observable - fileprivate let _defaultValue: Element? + fileprivate let defaultValue: Element? init(source: Observable, sampler: Observable, defaultValue: Element? = nil) { self.source = source From faad814f230eadebe485d35a79655dd6167bf50d Mon Sep 17 00:00:00 2001 From: Ayal Spitz Date: Thu, 2 Jan 2020 23:40:46 -0500 Subject: [PATCH 05/10] Updated test name as well as fixed the 'correct' events to properly account for the `completed` event --- Tests/RxSwiftTests/Observable+SampleTests.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/RxSwiftTests/Observable+SampleTests.swift b/Tests/RxSwiftTests/Observable+SampleTests.swift index 140c36ce5..ed1643f09 100644 --- a/Tests/RxSwiftTests/Observable+SampleTests.swift +++ b/Tests/RxSwiftTests/Observable+SampleTests.swift @@ -14,7 +14,7 @@ class ObservableSampleTest : RxTest { } extension ObservableSampleTest { - func testSample_Sampler_Default() { + func testSample_Sampler_DefaultValue() { let scheduler = TestScheduler(initialClock: 0) let xs = scheduler.createHotObservable([ @@ -45,6 +45,7 @@ extension ObservableSampleTest { .next(250, 3), .next(260, 0), .next(320, 6), + .next(500, 0), .completed(500) ) From 361f8043ef446ba9505b41a22f5ae67e09c5da64 Mon Sep 17 00:00:00 2001 From: freak4pc Date: Fri, 3 Jan 2020 17:11:30 +0200 Subject: [PATCH 06/10] Run package-spm --- Sources/AllTestz/main.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/AllTestz/main.swift b/Sources/AllTestz/main.swift index ed9b3d9ee..cc7da7c34 100644 --- a/Sources/AllTestz/main.swift +++ b/Sources/AllTestz/main.swift @@ -1223,6 +1223,7 @@ final class ObservableSampleTest_ : ObservableSampleTest, RxTestCase { #endif static var allTests: [(String, (ObservableSampleTest_) -> () -> Void)] { return [ + ("testSample_Sampler_DefaultValue", ObservableSampleTest.testSample_Sampler_DefaultValue), ("testSample_Sampler_SamplerThrows", ObservableSampleTest.testSample_Sampler_SamplerThrows), ("testSample_Sampler_Simple1", ObservableSampleTest.testSample_Sampler_Simple1), ("testSample_Sampler_Simple2", ObservableSampleTest.testSample_Sampler_Simple2), @@ -2165,4 +2166,4 @@ func XCTMain(_ tests: [() -> Void]) { testCase(SingleTest_.allTests), testCase(VirtualSchedulerTest_.allTests), ]) -//} \ No newline at end of file +//} From 93ddcf056d5a3741310060fe96c8b1ed56a930a4 Mon Sep 17 00:00:00 2001 From: Ayal Spitz Date: Sat, 4 Jan 2020 08:51:10 -0500 Subject: [PATCH 07/10] Updated CHANGELOG.md pre 'Contributing to RxSwift' --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a947fb0f8..2a214e6d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ All notable changes to this project will be documented in this file. * Add `UITextField.isSecureTextEntry` binder. #1968 * Remove "custom" `Result` in favor of `Foundation.Resault`. #2006 * Fix compilation error in `SharedSequence.createUnsafe`. #2014 +* Added `defaultValue` to `sample` to be returned when no new events occur between sampler ticks. #1457 ## [5.0.1](https://github.com/ReactiveX/RxSwift/releases/tag/5.0.1) From 2e4665e101ee3b08be696b3de7cfeb1147906507 Mon Sep 17 00:00:00 2001 From: Ayal Spitz Date: Sat, 4 Jan 2020 09:00:40 -0500 Subject: [PATCH 08/10] Added .swiftpm to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4f56b6f54..4272d6861 100644 --- a/.gitignore +++ b/.gitignore @@ -52,6 +52,7 @@ Carthage/Build .build/ Packages/ +.swiftpm # AppCode From ed9471a83cb6ac7b3294bd37faad7a3a9c1d0f25 Mon Sep 17 00:00:00 2001 From: Ayal Spitz Date: Mon, 6 Jan 2020 07:01:18 -0500 Subject: [PATCH 09/10] Updated code in response to PR comments --- RxSwift/Observables/Sample.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/RxSwift/Observables/Sample.swift b/RxSwift/Observables/Sample.swift index a6fc1212f..15249478d 100644 --- a/RxSwift/Observables/Sample.swift +++ b/RxSwift/Observables/Sample.swift @@ -13,13 +13,13 @@ extension ObservableType { Upon each sampling tick, the latest element (if any) in the source sequence during the last sampling interval is sent to the resulting sequence. - **In case there were no new elements between sampler ticks, if a default value has been set then it is sent + **In case there were no new elements between sampler ticks, you may provide a default value to be emitted, instead to the resulting sequence otherwise no element is sent.** - seealso: [sample operator on reactivex.io](http://reactivex.io/documentation/operators/sample.html) - parameter sampler: Sampling tick sequence. - defaultValue: a value to return if there are new new elements between sampler ticks + - parameter defaultValue: a value to return if there are no new elements between sampler ticks - returns: Sampled observable sequence. */ public func sample(_ sampler: Source, defaultValue: Element? = nil) @@ -53,11 +53,9 @@ final private class SamplerSink func synchronized_on(_ event: Event) { switch event { case .next, .completed: - if let element = parent.element { + if let element = parent.element ?? self.parent.defaultValue{ self.parent.element = nil self.parent.forwardOn(.next(element)) - } else if let element = self.parent.parent.defaultValue { - self.parent.forwardOn(.next(element)) } if self.parent.atEnd { @@ -80,6 +78,7 @@ final private class SampleSequenceSink typealias Parent = Sample fileprivate let parent: Parent + fileprivate let defaultValue: Element? let lock = RecursiveLock() @@ -89,8 +88,9 @@ final private class SampleSequenceSink private let sourceSubscription = SingleAssignmentDisposable() - init(parent: Parent, observer: Observer, cancel: Cancelable) { + init(parent: Parent, observer: Observer, cancel: Cancelable, defaultValue: Element? = nil) { self.parent = parent + self.defaultValue = defaultValue super.init(observer: observer, cancel: cancel) } @@ -132,7 +132,7 @@ final private class Sample: Producer { } override func run(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element { - let sink = SampleSequenceSink(parent: self, observer: observer, cancel: cancel) + let sink = SampleSequenceSink(parent: self, observer: observer, cancel: cancel, defaultValue: self.defaultValue) let subscription = sink.run() return (sink: sink, subscription: subscription) } From b97b8e3cc09a7846a681bf4c89fc523192f389fb Mon Sep 17 00:00:00 2001 From: Ayal Spitz Date: Wed, 8 Jan 2020 21:31:39 -0500 Subject: [PATCH 10/10] minor tweak --- RxSwift/Observables/Sample.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RxSwift/Observables/Sample.swift b/RxSwift/Observables/Sample.swift index 15249478d..9301c9af7 100644 --- a/RxSwift/Observables/Sample.swift +++ b/RxSwift/Observables/Sample.swift @@ -53,7 +53,7 @@ final private class SamplerSink func synchronized_on(_ event: Event) { switch event { case .next, .completed: - if let element = parent.element ?? self.parent.defaultValue{ + if let element = parent.element ?? self.parent.defaultValue { self.parent.element = nil self.parent.forwardOn(.next(element)) }