ReactiveCocoa / ReactiveSwift

Streams of values over time

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Starting a SignalProducer with debounce twice doesn't deliver values to the second observer

gpambrozio opened this issue · comments

This is related to #718.

I just found something similar on my project and I believe debounce to be broken when you start a producer twice. With latest master, I added the following tests to SignalProducerSpec.swift and they both fail (just the second expectation of each it, not the first):

describe("debounce") {
	context("property - starting twice") {
		it("should deliver the same values") {
			let p1 = MutableProperty(1)
			let p2 = MutableProperty(2)

			let scheduler = TestScheduler()
			let combined = Property.combineLatest(p1, p2)
			let prod = combined
				.producer
				.skipRepeats { $0 == $1 }
				.map { $0 + $1 }
				.debounce(0.1, on: scheduler)

			var v1 = [Int]()
			var v2 = [Int]()
			prod.startWithValues { (v) in
				v1.append(v)
			}
			prod.startWithValues { (v) in
				v2.append(v)
			}

			scheduler.advance(by: .milliseconds(200))
			p1.value = 3
			scheduler.advance(by: .milliseconds(200))
			p2.value = 4
			scheduler.advance(by: .milliseconds(200))
			p2.value = 4
			scheduler.advance(by: .milliseconds(200))

			expect(v1).to(equal([3, 5, 7]))
			expect(v2).to(equal([3, 5, 7]))
		}
	}

	context("pipe - starting twice") {
		it("should deliver the same values") {
			let p1 = SignalProducer<Int, Never>.pipe()
			let p2 = SignalProducer<Int, Never>.pipe()

			let scheduler = TestScheduler()
			let combined = SignalProducer.combineLatest(p1.0, p2.0)
			let prod = combined
				.skipRepeats { $0 == $1 }
				.map { $0 + $1 }
				.debounce(0.1, on: scheduler)

			var v1 = [Int]()
			var v2 = [Int]()
			prod.startWithValues { (v) in
				v1.append(v)
			}
			prod.startWithValues { (v) in
				v2.append(v)
			}

			p1.1.send(value: 1)
			p2.1.send(value: 2)
			scheduler.advance(by: .milliseconds(200))
			p1.1.send(value: 3)
			scheduler.advance(by: .milliseconds(200))
			p2.1.send(value: 4)
			scheduler.advance(by: .milliseconds(200))
			p2.1.send(value: 4)
			scheduler.advance(by: .milliseconds(200))

			expect(v1).to(equal([3, 5, 7]))
			expect(v2).to(equal([3, 5, 7]))

		}
	}
}

If you remove the debounce from the chain they succeed.

I understand why changing it to a Signal would fix this but shouldn't this work for this case as well?

I noticed this on one of my projects after I updated ReactiveSwift from 3.1 to 6.2. On 3.1 this was working as I'd expect but on 6.1 my feature broke because of this.

I'm afraid it might be because of #605 as this is the only change regarding debounce on the changelog but I have not confirmed that yet.

I'm investigating further and if I find something I'll submit a PR but want to have the issue to track and have some more expert eyes on it.

I reverted (most of) #605 and the tests both fail. Not the solution but it shows that this is what broke the behavior and (and least imho) shows it's broken now.

Argh, state is shared across produced signals. That's why it is broken when it is started more than once. 🙈

Moving https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Sources/Event.swift#L873 into the outer closure (not the inner one, which is the event sink) should resolve the issue.

You got it! I'll submit a PR Monday with the fix and added tests. Thanks for the quick help.

Should be fixed as part of #772