Skip to content

Commit 6f308d6

Browse files
rocketnikNikolas Mayr
andauthored
Joining empty list of producers fix (#774)
* joining an empty list of producers returns a single event of the collected results, which is the empty array - this becomes relevant, when the list of producers is calculated and you are merging the resulting joined signal with some other signal. the end result will stall, if no event is sent from the joined producers. * updated changelog * no upstream sentinal is optional * updated changelog * typo Co-authored-by: Nikolas Mayr <[email protected]>
1 parent 339c9b2 commit 6f308d6

File tree

4 files changed

+20
-12
lines changed

4 files changed

+20
-12
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
# master
22
*Please add new entries at the top.*
33

4+
1. Joining an empty sequence of producers can now send an event on the joined signal producer by providing the `noUpstreamSentinel` parameter. This becomes relevant, when the sequence of producers is calculated from some other Signal and the signal resulting from the joined producers is observed. If no event is sent only when the producers sequence is empty, then the observer gets stalled and e.g. the ui won't update.
5+
46
# 6.2.1
7+
58
1. Improved performance of joining signals by a factor of around 5. This enables joining of 1000 and more signals in a reasonable amount of time.
69
1. Fixed `SignalProducer.debounce` operator that, when started more than once, would not deliver values on producers started after the first time. (#772, kudos to @gpambrozio)
710
1. `FlattenStrategy.throttle` is introduced. (#713, kudos to @inamiy)
@@ -20,6 +23,7 @@
2023
1. Add `<~` binding operator to `Signal.Observer` (#635, kudos to @Marcocanc)
2124

2225
# 6.0.0
26+
2327
1. Dropped support for Swift 4.2 (Xcode 9)
2428
2. Removed dependency on https://github.com/antitypical/Result (#702, kudos to @NachoSoto and @mdiep)
2529

@@ -30,6 +34,7 @@
3034
* Replace all cases where `AnyError` was used in a `Signal` or `SignalProducer` with `Swift.Error`
3135

3236
# 5.0.1
37+
3338
1. Fix warnings in Xcode 10.2
3439

3540
# 5.0.0

Sources/Property.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -318,13 +318,13 @@ extension PropertyProtocol {
318318

319319
/// Combines the values of all the given producers, in the manner described by
320320
/// `combineLatest(with:)`. Returns nil if the sequence is empty.
321-
public static func combineLatest<S: Sequence>(_ properties: S) -> Property<[S.Iterator.Element.Value]>? where S.Iterator.Element: PropertyProtocol {
321+
public static func combineLatest<S: Sequence>(_ properties: S, noUpstreamSentinel: [S.Iterator.Element.Value]? = nil) -> Property<[S.Iterator.Element.Value]>? where S.Iterator.Element: PropertyProtocol {
322322
let producers = properties.map { $0.producer }
323323
guard !producers.isEmpty else {
324324
return nil
325325
}
326326

327-
return Property(unsafeProducer: SignalProducer.combineLatest(producers))
327+
return Property(unsafeProducer: SignalProducer.combineLatest(producers, noUpstreamSentinel: noUpstreamSentinel))
328328
}
329329

330330
/// Zips the values of all the given properties, in the manner described by
@@ -383,13 +383,13 @@ extension PropertyProtocol {
383383

384384
/// Zips the values of all the given properties, in the manner described by
385385
/// `zip(with:)`. Returns nil if the sequence is empty.
386-
public static func zip<S: Sequence>(_ properties: S) -> Property<[S.Iterator.Element.Value]>? where S.Iterator.Element: PropertyProtocol {
386+
public static func zip<S: Sequence>(_ properties: S, noUpstreamSentinel: [S.Iterator.Element.Value]? = nil) -> Property<[S.Iterator.Element.Value]>? where S.Iterator.Element: PropertyProtocol {
387387
let producers = properties.map { $0.producer }
388388
guard !producers.isEmpty else {
389389
return nil
390390
}
391391

392-
return Property(unsafeProducer: SignalProducer.zip(producers))
392+
return Property(unsafeProducer: SignalProducer.zip(producers, noUpstreamSentinel: noUpstreamSentinel))
393393
}
394394
}
395395

Sources/Signal.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1680,7 +1680,7 @@ extension Signal {
16801680
return true
16811681
}
16821682

1683-
_haveAllSentInitial = values.allSatisfy{ !($0 is Placeholder) }
1683+
_haveAllSentInitial = values.allSatisfy { !($0 is Placeholder) }
16841684
return _haveAllSentInitial
16851685
}
16861686
}

Sources/SignalProducer.swift

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2098,8 +2098,8 @@ extension SignalProducer {
20982098

20992099
/// Combines the values of all the given producers, in the manner described by
21002100
/// `combineLatest(with:)`. Will return an empty `SignalProducer` if the sequence is empty.
2101-
public static func combineLatest<S: Sequence>(_ producers: S) -> SignalProducer<[Value], Error> where S.Iterator.Element: SignalProducerConvertible, S.Iterator.Element.Value == Value, S.Iterator.Element.Error == Error {
2102-
return start(producers, Signal.combineLatest)
2101+
public static func combineLatest<S: Sequence>(_ producers: S, noUpstreamSentinel: [S.Iterator.Element.Value]? = nil) -> SignalProducer<[Value], Error> where S.Iterator.Element: SignalProducerConvertible, S.Iterator.Element.Value == Value, S.Iterator.Element.Error == Error {
2102+
return start(producers, noUpstreamSentinel: noUpstreamSentinel, Signal.combineLatest)
21032103
}
21042104

21052105
/// Zips the values of all the given producers, in the manner described by
@@ -2176,18 +2176,21 @@ extension SignalProducer {
21762176

21772177
/// Zips the values of all the given producers, in the manner described by
21782178
/// `zipWith`. Will return an empty `SignalProducer` if the sequence is empty.
2179-
public static func zip<S: Sequence>(_ producers: S) -> SignalProducer<[Value], Error> where S.Iterator.Element: SignalProducerConvertible, S.Iterator.Element.Value == Value, S.Iterator.Element.Error == Error {
2180-
return start(producers, Signal.zip)
2179+
public static func zip<S: Sequence>(_ producers: S, noUpstreamSentinel: [S.Iterator.Element.Value]? = nil) -> SignalProducer<[Value], Error> where S.Iterator.Element: SignalProducerConvertible, S.Iterator.Element.Value == Value, S.Iterator.Element.Error == Error {
2180+
return start(producers, noUpstreamSentinel: noUpstreamSentinel, Signal.zip)
21812181
}
21822182

2183-
private static func start<S: Sequence>(_ producers: S, _ transform: @escaping (AnySequence<Signal<Value, Error>>) -> Signal<[Value], Error>) -> SignalProducer<[Value], Error> where S.Iterator.Element: SignalProducerConvertible, S.Iterator.Element.Value == Value, S.Iterator.Element.Error == Error
2183+
private static func start<S: Sequence>(_ producers: S, noUpstreamSentinel: [S.Iterator.Element.Value]?, _ transform: @escaping (AnySequence<Signal<Value, Error>>) -> Signal<[Value], Error>) -> SignalProducer<[Value], Error> where S.Iterator.Element: SignalProducerConvertible, S.Iterator.Element.Value == Value, S.Iterator.Element.Error == Error
21842184
{
21852185
return SignalProducer<[Value], Error> { observer, lifetime in
21862186
let setup = producers.map {
21872187
(producer: $0.producer, pipe: Signal<Value, Error>.pipe())
21882188
}
21892189

21902190
guard !setup.isEmpty else {
2191+
if let noUpstreamSentinel = noUpstreamSentinel {
2192+
observer.send(value: noUpstreamSentinel)
2193+
}
21912194
observer.sendCompleted()
21922195
return
21932196
}
@@ -2717,7 +2720,7 @@ extension SignalProducer where Value == Bool {
27172720
///
27182721
/// - returns: A producer that emits the logical AND results.
27192722
public static func all<BooleansCollection: Collection>(_ booleans: BooleansCollection) -> SignalProducer<Value, Error> where BooleansCollection.Element == SignalProducer<Value, Error> {
2720-
return combineLatest(booleans).map { $0.reduce(true) { $0 && $1 } }
2723+
return combineLatest(booleans, noUpstreamSentinel: []).map { $0.reduce(true) { $0 && $1 } }
27212724
}
27222725

27232726
/// Create a producer that computes a logical AND between the latest values of `booleans`.
@@ -2759,7 +2762,7 @@ extension SignalProducer where Value == Bool {
27592762
///
27602763
/// - returns: A producer that emits the logical OR results.
27612764
public static func any<BooleansCollection: Collection>(_ booleans: BooleansCollection) -> SignalProducer<Value, Error> where BooleansCollection.Element == SignalProducer<Value, Error> {
2762-
return combineLatest(booleans).map { $0.reduce(false) { $0 || $1 } }
2765+
return combineLatest(booleans, noUpstreamSentinel: []).map { $0.reduce(false) { $0 || $1 } }
27632766
}
27642767

27652768
/// Create a producer that computes a logical OR between the latest values of `booleans`.

0 commit comments

Comments
 (0)