From 9b8175b995d2376697f64815bdfd6f5c00f1ce25 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Fri, 30 Dec 2016 11:27:56 +0100 Subject: [PATCH 01/12] Add `groupBy` operator from REX --- Sources/SignalProducer.swift | 58 +++++++++++++++++++ .../SignalProducerLiftingSpec.swift | 42 ++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/Sources/SignalProducer.swift b/Sources/SignalProducer.swift index 0a09a4183..a9b997427 100644 --- a/Sources/SignalProducer.swift +++ b/Sources/SignalProducer.swift @@ -1929,6 +1929,64 @@ extension SignalProducerProtocol { } } } + + /// Creates a new `SignalProducer` that will bucket each received value into + /// a group based on the key returned from `grouping`. + /// + /// Termination events on the original signal are also forwarded to each + /// producer group. + /// + /// - parameters: + /// - grouping: a closure that determines the grouping key for a given value + /// + /// - returns: A producer of producers amits one producer for each group and forwards + /// each value from the original producer to the inner producer corresponding + /// to the group to which the value belongs to (as determined by the key) + public func group(by grouping: @escaping (Value) -> Key) -> SignalProducer<(Key, SignalProducer), Error> { + public func groupBy(_ grouping: @escaping (Value) -> Key) -> SignalProducer<(Key, SignalProducer), Error> { + return SignalProducer<(Key, SignalProducer), Error> { observer, disposable in + var groups: [Key: Signal.Observer] = [:] + + let lock = NSRecursiveLock() + lock.name = "me.neilpa.rex.groupBy" + + self.start { event in + switch event { + case let .value(value): + let key = grouping(value) + + lock.lock() + var group = groups[key] + if group == nil { + let (signal, innerObserver) = Signal.pipe() + let producer = SignalProducer(signal).replayLazily(upTo: Int.max) + + // Start the buffering immediately. + producer.start() + observer.send(value: (key, producer)) + + groups[key] = innerObserver + group = innerObserver + } + lock.unlock() + + group!.send(value: value) + + case let .failed(error): + observer.send(error: error) + groups.values.forEach { $0.send(error: error) } + + case .completed: + observer.sendCompleted() + groups.values.forEach { $0.sendCompleted() } + + case .interrupted: + observer.sendInterrupted() + groups.values.forEach { $0.sendInterrupted() } + } + } + } + } } extension SignalProducerProtocol where Value == Bool { diff --git a/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift b/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift index 8a721afaf..622bf549a 100644 --- a/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift +++ b/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift @@ -1926,5 +1926,47 @@ class SignalProducerLiftingSpec: QuickSpec { expect(latestValues?.1) == 2 } } + + describe("groupBy") { + let (signal, observer) = Signal.pipe() + let producer = SignalProducer(signal) + var evens: [Int] = [] + var odds: [Int] = [] + let disposable = CompositeDisposable() + var interrupted = false + var completed = false + + disposable += producer + .groupBy { $0 % 2 == 0 } + .start(Observer(value: { key, group in + if key { + group.startWithValues { evens.append($0) } + } else { + group.startWithValues { odds.append($0) } + } + },completed: { + completed = true + }, interrupted: { + interrupted = true + })) + + observer.send(value: 1) + expect(evens) == [] + expect(odds) == [1] + + observer.send(value: 2) + expect(evens) == [2] + expect(odds) == [1] + + observer.send(value: 3) + expect(evens) == [2] + expect(odds) == [1, 3] + + disposable.dispose() + + observer.send(value: 1) + expect(interrupted) == true + expect(completed) == false + } } } From 918e119ed2a16d4d05f0f3fd07cdb94807e047b1 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Fri, 30 Dec 2016 11:47:43 +0100 Subject: [PATCH 02/12] Use `Atomic` for `groupBy` --- Sources/SignalProducer.swift | 44 ++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/Sources/SignalProducer.swift b/Sources/SignalProducer.swift index a9b997427..37aa50632 100644 --- a/Sources/SignalProducer.swift +++ b/Sources/SignalProducer.swift @@ -1942,47 +1942,41 @@ extension SignalProducerProtocol { /// - returns: A producer of producers amits one producer for each group and forwards /// each value from the original producer to the inner producer corresponding /// to the group to which the value belongs to (as determined by the key) - public func group(by grouping: @escaping (Value) -> Key) -> SignalProducer<(Key, SignalProducer), Error> { public func groupBy(_ grouping: @escaping (Value) -> Key) -> SignalProducer<(Key, SignalProducer), Error> { return SignalProducer<(Key, SignalProducer), Error> { observer, disposable in - var groups: [Key: Signal.Observer] = [:] - - let lock = NSRecursiveLock() - lock.name = "me.neilpa.rex.groupBy" + let groups = Atomic<[Key: Signal.Observer]>([:]) self.start { event in switch event { case let .value(value): let key = grouping(value) - - lock.lock() - var group = groups[key] - if group == nil { - let (signal, innerObserver) = Signal.pipe() - let producer = SignalProducer(signal).replayLazily(upTo: Int.max) - - // Start the buffering immediately. - producer.start() - observer.send(value: (key, producer)) - - groups[key] = innerObserver - group = innerObserver + var group: Signal.Observer? + groups.modify { + group = $0[key] + if group == nil { + let (signal, innerObserver) = Signal.pipe() + let producer = SignalProducer(signal).replayLazily(upTo: Int.max) + + // Start the buffering immediately. + producer.start() + observer.send(value: (key, producer)) + + $0[key] = innerObserver + group = innerObserver + } } - lock.unlock() - - group!.send(value: value) - + group!.send(value: value) case let .failed(error): observer.send(error: error) - groups.values.forEach { $0.send(error: error) } + groups.value.values.forEach { $0.send(error: error) } case .completed: observer.sendCompleted() - groups.values.forEach { $0.sendCompleted() } + groups.value.values.forEach { $0.sendCompleted() } case .interrupted: observer.sendInterrupted() - groups.values.forEach { $0.sendInterrupted() } + groups.value.values.forEach { $0.sendInterrupted() } } } } From a5754110a1f355c4c048b9a8006567d71279ad23 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Fri, 30 Dec 2016 16:49:05 +0100 Subject: [PATCH 03/12] Extend test to catch disposal misbehavior --- .../SignalProducerLiftingSpec.swift | 131 +++++++++++++----- 1 file changed, 93 insertions(+), 38 deletions(-) diff --git a/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift b/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift index 622bf549a..5c051c053 100644 --- a/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift +++ b/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift @@ -1928,45 +1928,100 @@ class SignalProducerLiftingSpec: QuickSpec { } describe("groupBy") { - let (signal, observer) = Signal.pipe() - let producer = SignalProducer(signal) - var evens: [Int] = [] - var odds: [Int] = [] - let disposable = CompositeDisposable() - var interrupted = false - var completed = false - - disposable += producer - .groupBy { $0 % 2 == 0 } - .start(Observer(value: { key, group in - if key { - group.startWithValues { evens.append($0) } - } else { - group.startWithValues { odds.append($0) } + it("should group events by their key") { + let (signal, observer) = Signal.pipe() + let producer = SignalProducer(signal) + var evens: [Int] = [] + var odds: [Int] = [] + let disposable = CompositeDisposable() + + disposable += producer + .groupBy { $0 % 2 == 0 } + .startWithValues { key, group in + if key { + group.startWithValues { evens.append($0)} + } else { + group.startWithValues { odds.append($0)} + } + } + + observer.send(value: 1) + expect(evens) == [] + expect(odds) == [1] + + observer.send(value: 2) + expect(evens) == [2] + expect(odds) == [1] + + observer.send(value: 3) + expect(evens) == [2] + expect(odds) == [1, 3] + + disposable.dispose() + + observer.send(value: 4) + expect(evens) == [2] + expect(odds) == [1, 3] + } + it("should terminate correctly on disposal") { + let (signal, observer) = Signal.pipe() + let producer = SignalProducer(signal) + let disposable = CompositeDisposable() + var interrupted = false + var evensInterrupted = false + var oddsInterrupted = false + var completed = false + var evensCompleted = false + var oddsCompleted = false + + disposable += producer + .groupBy { $0 % 2 == 0 } + .start { event in + switch event { + case let .value(key, group): + if key { + group.start { event in + switch event { + case .completed: + evensCompleted = true + case .interrupted: + evensInterrupted = true + case .value, .failed: + break + } + } + } else { + group.start { event in + switch event { + case .completed: + oddsCompleted = true + case .interrupted: + oddsInterrupted = true + case .value, .failed: + break + } + } + } + case .completed: + completed = true + case .interrupted: + interrupted = true + case .failed: + break + } } - },completed: { - completed = true - }, interrupted: { - interrupted = true - })) - - observer.send(value: 1) - expect(evens) == [] - expect(odds) == [1] - - observer.send(value: 2) - expect(evens) == [2] - expect(odds) == [1] - - observer.send(value: 3) - expect(evens) == [2] - expect(odds) == [1, 3] - - disposable.dispose() - - observer.send(value: 1) - expect(interrupted) == true - expect(completed) == false + + observer.send(value: 1) + observer.send(value: 2) + + disposable.dispose() + expect(interrupted) == true + expect(evensInterrupted) == true + expect(oddsInterrupted) == true + expect(completed) == false + expect(evensCompleted) == false + expect(oddsCompleted) == false + } } } } From 679f8004a7a2213532027a6582bf0329029935c8 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Fri, 30 Dec 2016 17:03:23 +0100 Subject: [PATCH 04/12] Fix disposal misbehavior of inner producer --- Sources/SignalProducer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SignalProducer.swift b/Sources/SignalProducer.swift index 37aa50632..06744cc50 100644 --- a/Sources/SignalProducer.swift +++ b/Sources/SignalProducer.swift @@ -1946,7 +1946,7 @@ extension SignalProducerProtocol { return SignalProducer<(Key, SignalProducer), Error> { observer, disposable in let groups = Atomic<[Key: Signal.Observer]>([:]) - self.start { event in + disposable += self.start { event in switch event { case let .value(value): let key = grouping(value) From dbaa6cf3ff823063e3e32cfc922ae8a567ea0742 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Fri, 30 Dec 2016 17:04:12 +0100 Subject: [PATCH 05/12] Dispose group producers immediately --- Sources/SignalProducer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SignalProducer.swift b/Sources/SignalProducer.swift index 06744cc50..a4ff0bd22 100644 --- a/Sources/SignalProducer.swift +++ b/Sources/SignalProducer.swift @@ -1958,7 +1958,7 @@ extension SignalProducerProtocol { let producer = SignalProducer(signal).replayLazily(upTo: Int.max) // Start the buffering immediately. - producer.start() + producer.start().dispose() observer.send(value: (key, producer)) $0[key] = innerObserver From 0f0f48bf134e7b65db603f5fd1f6d983d9f63f54 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Fri, 30 Dec 2016 17:14:48 +0100 Subject: [PATCH 06/12] Review changes to `groupBy` --- Sources/SignalProducer.swift | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Sources/SignalProducer.swift b/Sources/SignalProducer.swift index a4ff0bd22..e4c7e898e 100644 --- a/Sources/SignalProducer.swift +++ b/Sources/SignalProducer.swift @@ -1950,10 +1950,10 @@ extension SignalProducerProtocol { switch event { case let .value(value): let key = grouping(value) - var group: Signal.Observer? - groups.modify { - group = $0[key] - if group == nil { + let group: Signal.Observer = groups.modify { groups in + if let group = groups[key] { + return group + } else { let (signal, innerObserver) = Signal.pipe() let producer = SignalProducer(signal).replayLazily(upTo: Int.max) @@ -1961,11 +1961,12 @@ extension SignalProducerProtocol { producer.start().dispose() observer.send(value: (key, producer)) - $0[key] = innerObserver - group = innerObserver + groups[key] = innerObserver + return innerObserver } } - group!.send(value: value) + group.send(value: value) + case let .failed(error): observer.send(error: error) groups.value.values.forEach { $0.send(error: error) } From 5bea41e878e581984d9828fb10e9e3f5d16a0bc3 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Fri, 30 Dec 2016 18:07:08 +0100 Subject: [PATCH 07/12] Implement test cases for all event types for `groupBy` --- .../SignalProducerLiftingSpec.swift | 155 ++++++++++++++---- 1 file changed, 123 insertions(+), 32 deletions(-) diff --git a/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift b/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift index 5c051c053..d3e6f22a2 100644 --- a/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift +++ b/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift @@ -1933,9 +1933,7 @@ class SignalProducerLiftingSpec: QuickSpec { let producer = SignalProducer(signal) var evens: [Int] = [] var odds: [Int] = [] - let disposable = CompositeDisposable() - - disposable += producer + let disposable = producer .groupBy { $0 % 2 == 0 } .startWithValues { key, group in if key { @@ -1963,44 +1961,24 @@ class SignalProducerLiftingSpec: QuickSpec { expect(evens) == [2] expect(odds) == [1, 3] } + it("should terminate correctly on disposal") { let (signal, observer) = Signal.pipe() let producer = SignalProducer(signal) - let disposable = CompositeDisposable() + var completed = false var interrupted = false var evensInterrupted = false var oddsInterrupted = false - var completed = false - var evensCompleted = false - var oddsCompleted = false - disposable += producer + let disposable = producer .groupBy { $0 % 2 == 0 } .start { event in switch event { case let .value(key, group): if key { - group.start { event in - switch event { - case .completed: - evensCompleted = true - case .interrupted: - evensInterrupted = true - case .value, .failed: - break - } - } + group.startWithInterrupted { evensInterrupted = true } } else { - group.start { event in - switch event { - case .completed: - oddsCompleted = true - case .interrupted: - oddsInterrupted = true - case .value, .failed: - break - } - } + group.startWithInterrupted { oddsInterrupted = true } } case .completed: completed = true @@ -2009,19 +1987,132 @@ class SignalProducerLiftingSpec: QuickSpec { case .failed: break } - } + } observer.send(value: 1) observer.send(value: 2) disposable.dispose() + + expect(completed) == false expect(interrupted) == true expect(evensInterrupted) == true expect(oddsInterrupted) == true - expect(completed) == false - expect(evensCompleted) == false - expect(oddsCompleted) == false } } + + it("should terminate correctly when receiving an interrupted event") { + let (signal, observer) = Signal.pipe() + let producer = SignalProducer(signal) + var completed = false + var interrupted = false + var evensInterrupted = false + var oddsInterrupted = false + + producer + .groupBy { $0 % 2 == 0 } + .start { event in + switch event { + case let .value(key, group): + if key { + group.startWithInterrupted { evensInterrupted = true } + } else { + group.startWithInterrupted { oddsInterrupted = true } + } + case .completed: + completed = true + case .interrupted: + interrupted = true + case .failed: + break + } + } + + observer.send(value: 1) + observer.send(value: 2) + observer.sendInterrupted() + + expect(completed) == false + expect(interrupted) == true + expect(evensInterrupted) == true + expect(oddsInterrupted) == true + } + + it("should terminate correctly receiving a completed event") { + let (signal, observer) = Signal.pipe() + let producer = SignalProducer(signal) + var completed = false + var interrupted = false + var evenCompleted = false + var oddCompleted = false + + producer + .groupBy { $0 % 2 == 0 } + .start { event in + switch event { + case let .value(key, group): + if key { + group.startWithCompleted { evenCompleted = true } + } else { + group.startWithCompleted { oddCompleted = true } + } + case .completed: + completed = true + case .interrupted: + interrupted = true + case .failed: + break + } + } + + observer.send(value: 1) + observer.send(value: 2) + observer.sendCompleted() + + expect(completed) == true + expect(interrupted) == false + expect(evenCompleted) == true + expect(oddCompleted) == true + } + + it("should terminate correctly receiving an error event") { + let (signal, observer) = Signal.pipe() + let producer = SignalProducer(signal) + var interrupted = false + var completed = false + var error: TestError? = nil + var evensError: TestError? = nil + var oddsError: TestError? = nil + + producer + .groupBy { $0 % 2 == 0 } + .start { event in + switch event { + case let .value(key, group): + if key { + group.startWithFailed { evensError = $0 } + } else { + group.startWithFailed { oddsError = $0 } + } + case .completed: + completed = true + case .interrupted: + interrupted = true + case let .failed(e): + error = e + } + } + + observer.send(value: 1) + observer.send(value: 2) + + observer.send(error: .error1) + + expect(interrupted) == false + expect(completed) == false + expect(error) == .error1 + expect(evensError) == .error1 + expect(oddsError) == .error1 + } } } From d53d3965b146e78231b056f3d45dcb0494d4bc4d Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Fri, 30 Dec 2016 18:08:50 +0100 Subject: [PATCH 08/12] Change `groupBy(_:) to group(by:) --- Sources/SignalProducer.swift | 2 +- .../ReactiveSwiftTests/SignalProducerLiftingSpec.swift | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/SignalProducer.swift b/Sources/SignalProducer.swift index e4c7e898e..084472d52 100644 --- a/Sources/SignalProducer.swift +++ b/Sources/SignalProducer.swift @@ -1942,7 +1942,7 @@ extension SignalProducerProtocol { /// - returns: A producer of producers amits one producer for each group and forwards /// each value from the original producer to the inner producer corresponding /// to the group to which the value belongs to (as determined by the key) - public func groupBy(_ grouping: @escaping (Value) -> Key) -> SignalProducer<(Key, SignalProducer), Error> { + public func group(by grouping: @escaping (Value) -> Key) -> SignalProducer<(Key, SignalProducer), Error> { return SignalProducer<(Key, SignalProducer), Error> { observer, disposable in let groups = Atomic<[Key: Signal.Observer]>([:]) diff --git a/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift b/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift index d3e6f22a2..81959dd9f 100644 --- a/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift +++ b/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift @@ -1934,7 +1934,7 @@ class SignalProducerLiftingSpec: QuickSpec { var evens: [Int] = [] var odds: [Int] = [] let disposable = producer - .groupBy { $0 % 2 == 0 } + .group { $0 % 2 == 0 } .startWithValues { key, group in if key { group.startWithValues { evens.append($0)} @@ -1971,7 +1971,7 @@ class SignalProducerLiftingSpec: QuickSpec { var oddsInterrupted = false let disposable = producer - .groupBy { $0 % 2 == 0 } + .group { $0 % 2 == 0 } .start { event in switch event { case let .value(key, group): @@ -2010,7 +2010,7 @@ class SignalProducerLiftingSpec: QuickSpec { var oddsInterrupted = false producer - .groupBy { $0 % 2 == 0 } + .group { $0 % 2 == 0 } .start { event in switch event { case let .value(key, group): @@ -2047,7 +2047,7 @@ class SignalProducerLiftingSpec: QuickSpec { var oddCompleted = false producer - .groupBy { $0 % 2 == 0 } + .group { $0 % 2 == 0 } .start { event in switch event { case let .value(key, group): @@ -2085,7 +2085,7 @@ class SignalProducerLiftingSpec: QuickSpec { var oddsError: TestError? = nil producer - .groupBy { $0 % 2 == 0 } + .group { $0 % 2 == 0 } .start { event in switch event { case let .value(key, group): From 2587a6cf089ecfee2f716538702517c73e77d054 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Fri, 30 Dec 2016 18:10:07 +0100 Subject: [PATCH 09/12] Remove explicit type annotation that can be inferred --- Sources/SignalProducer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SignalProducer.swift b/Sources/SignalProducer.swift index 084472d52..961aa8da3 100644 --- a/Sources/SignalProducer.swift +++ b/Sources/SignalProducer.swift @@ -1943,7 +1943,7 @@ extension SignalProducerProtocol { /// each value from the original producer to the inner producer corresponding /// to the group to which the value belongs to (as determined by the key) public func group(by grouping: @escaping (Value) -> Key) -> SignalProducer<(Key, SignalProducer), Error> { - return SignalProducer<(Key, SignalProducer), Error> { observer, disposable in + return SignalProducer { observer, disposable in let groups = Atomic<[Key: Signal.Observer]>([:]) disposable += self.start { event in From f4ae59509c0849971a75bd6800be4d297b5f2a51 Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Fri, 30 Dec 2016 18:11:53 +0100 Subject: [PATCH 10/12] Realign braces --- .../ReactiveSwiftTests/SignalProducerLiftingSpec.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift b/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift index 81959dd9f..7b4a3473b 100644 --- a/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift +++ b/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift @@ -1941,7 +1941,7 @@ class SignalProducerLiftingSpec: QuickSpec { } else { group.startWithValues { odds.append($0)} } - } + } observer.send(value: 1) expect(evens) == [] @@ -1987,7 +1987,7 @@ class SignalProducerLiftingSpec: QuickSpec { case .failed: break } - } + } observer.send(value: 1) observer.send(value: 2) @@ -2026,7 +2026,7 @@ class SignalProducerLiftingSpec: QuickSpec { case .failed: break } - } + } observer.send(value: 1) observer.send(value: 2) @@ -2063,7 +2063,7 @@ class SignalProducerLiftingSpec: QuickSpec { case .failed: break } - } + } observer.send(value: 1) observer.send(value: 2) @@ -2101,7 +2101,7 @@ class SignalProducerLiftingSpec: QuickSpec { case let .failed(e): error = e } - } + } observer.send(value: 1) observer.send(value: 2) From 56ee8f68183545fb712c6ca1d81ec7ee7789c41b Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Fri, 30 Dec 2016 18:30:49 +0100 Subject: [PATCH 11/12] Fix naming for testcase --- Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift b/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift index 7b4a3473b..bee10b0f7 100644 --- a/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift +++ b/Tests/ReactiveSwiftTests/SignalProducerLiftingSpec.swift @@ -2075,7 +2075,7 @@ class SignalProducerLiftingSpec: QuickSpec { expect(oddCompleted) == true } - it("should terminate correctly receiving an error event") { + it("should terminate correctly receiving a failed event") { let (signal, observer) = Signal.pipe() let producer = SignalProducer(signal) var interrupted = false From 1d527247d468fb61484a40586b34973a2dc5b20c Mon Sep 17 00:00:00 2001 From: Markus Chmelar Date: Thu, 16 Feb 2017 17:36:00 +0100 Subject: [PATCH 12/12] fix typo in documentation --- Sources/SignalProducer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SignalProducer.swift b/Sources/SignalProducer.swift index 961aa8da3..249fa39c3 100644 --- a/Sources/SignalProducer.swift +++ b/Sources/SignalProducer.swift @@ -1939,7 +1939,7 @@ extension SignalProducerProtocol { /// - parameters: /// - grouping: a closure that determines the grouping key for a given value /// - /// - returns: A producer of producers amits one producer for each group and forwards + /// - returns: A producer of producers that emits one producer for each group and forwards /// each value from the original producer to the inner producer corresponding /// to the group to which the value belongs to (as determined by the key) public func group(by grouping: @escaping (Value) -> Key) -> SignalProducer<(Key, SignalProducer), Error> {