@@ -12,6 +12,7 @@ public struct FlattenStrategy {
1212 case concurrent( limit: UInt )
1313 case latest
1414 case race
15+ case throttle
1516 }
1617
1718 fileprivate let kind : Kind
@@ -96,6 +97,21 @@ public struct FlattenStrategy {
9697 /// Any failure from the inner streams is propagated immediately to the flattened
9798 /// stream of values.
9899 public static let race = FlattenStrategy ( kind: . race)
100+
101+ /// Forward only events from the "first inner stream" that sends an event if not exists.
102+ /// Other inner streams is disposed of until the first inner stream is completed.
103+ /// Note that next inner stream after previous completion can become
104+ /// first inner stream again.
105+ ///
106+ /// The flattened stream of values completes only when the stream of streams has completed,
107+ /// and first inner stream has completed if exists.
108+ ///
109+ /// Any interruption of inner streams is propagated immediately to the flattened
110+ /// stream of values.
111+ ///
112+ /// Any failure from the inner streams is propagated immediately to the flattened
113+ /// stream of values.
114+ public static let throttle = FlattenStrategy ( kind: . throttle)
99115}
100116
101117extension Signal where Value: SignalProducerConvertible , Error == Value . Error {
@@ -120,6 +136,9 @@ extension Signal where Value: SignalProducerConvertible, Error == Value.Error {
120136
121137 case . race:
122138 return self . race ( )
139+
140+ case . throttle:
141+ return self . throttle ( )
123142 }
124143 }
125144}
@@ -162,6 +181,9 @@ extension Signal where Value: SignalProducerConvertible, Error == Never, Value.E
162181
163182 case . race:
164183 return self . race ( )
184+
185+ case . throttle:
186+ return self . throttle ( )
165187 }
166188 }
167189}
@@ -205,6 +227,9 @@ extension SignalProducer where Value: SignalProducerConvertible, Error == Value.
205227
206228 case . race:
207229 return self . race ( )
230+
231+ case . throttle:
232+ return self . throttle ( )
208233 }
209234 }
210235}
@@ -247,6 +272,9 @@ extension SignalProducer where Value: SignalProducerConvertible, Error == Never,
247272
248273 case . race:
249274 return self . race ( )
275+
276+ case . throttle:
277+ return self . throttle ( )
250278 }
251279 }
252280}
@@ -819,6 +847,112 @@ private struct RaceState {
819847 var isActivated = false
820848}
821849
850+ extension Signal where Value: SignalProducerConvertible , Error == Value . Error {
851+ /// Returns a signal that forwards values from the "first inner producer" if not exists,
852+ /// ignoring values sent from other inner producers until first inner producer is completed.
853+ /// Note that next inner producer after previous completion can become
854+ /// first inner producer again.
855+ ///
856+ /// An error sent on `self` or the first inner producer will be sent on the
857+ /// returned signal.
858+ ///
859+ /// The returned signal completes when `self` is completed, and also first inner producer
860+ /// is completed if it exists.
861+ fileprivate func throttle( ) -> Signal < Value . Value , Error > {
862+ return Signal < Value . Value , Error > { observer, lifetime in
863+ let relayDisposable = CompositeDisposable ( )
864+ lifetime += relayDisposable
865+ lifetime += self . observeThrottle ( observer, relayDisposable)
866+ }
867+ }
868+
869+ fileprivate func observeThrottle( _ observer: Signal < Value . Value , Error > . Observer , _ relayDisposable: CompositeDisposable ) -> Disposable ? {
870+ let state = Atomic ( ThrottleState ( ) )
871+
872+ return self . observe { event in
873+ switch event {
874+ case let . value( innerProducer) :
875+ let isFirstInnerProducer : Bool = state. modify { state in
876+ guard !state. hasFirstInnerProducer else {
877+ return false
878+ }
879+
880+ state. hasFirstInnerProducer = true
881+ return true
882+ }
883+
884+ // Ignore consecutive `innerProducer`s while `isFirstInnerProducer` is true.
885+ guard isFirstInnerProducer else { return }
886+
887+ innerProducer. producer. startWithSignal { innerSignal, innerDisposable in
888+ relayDisposable. add ( innerDisposable)
889+
890+ innerSignal. observe { event in
891+ switch event {
892+ case . completed:
893+ let shouldComplete : Bool = state. modify { state in
894+ state. hasFirstInnerProducer = false
895+ return state. outerSignalComplete
896+ }
897+
898+ if shouldComplete {
899+ observer. sendCompleted ( )
900+ }
901+
902+ case . value, . failed, . interrupted:
903+ observer. send ( event)
904+ }
905+ }
906+ }
907+
908+ case let . failed( error) :
909+ observer. send ( error: error)
910+
911+ case . completed:
912+ let shouldComplete : Bool = state. modify { state in
913+ state. outerSignalComplete = true
914+ return !state. hasFirstInnerProducer
915+ }
916+
917+ if shouldComplete {
918+ observer. sendCompleted ( )
919+ }
920+
921+ case . interrupted:
922+ observer. sendInterrupted ( )
923+ }
924+ }
925+ }
926+ }
927+
928+ extension SignalProducer where Value: SignalProducerConvertible , Error == Value . Error {
929+ /// Returns a producer that forwards values from the "first inner producer" if not exists,
930+ /// ignoring values sent from other inner producers until first inner producer is completed.
931+ /// Note that next inner producer after previous completion can become first inner producer again.
932+ ///
933+ /// An error sent on `self` or the first inner producer will be sent on the
934+ /// returned producer.
935+ ///
936+ /// The returned signal completes when `self` is completed, and also first inner producer
937+ /// is completed if it exists.
938+ fileprivate func throttle( ) -> SignalProducer < Value . Value , Error > {
939+ return SignalProducer < Value . Value , Error > { observer, lifetime in
940+ let relayDisposable = CompositeDisposable ( )
941+ lifetime += relayDisposable
942+
943+ self . startWithSignal { signal, signalDisposable in
944+ lifetime += signalDisposable
945+ lifetime += signal. observeThrottle ( observer, relayDisposable)
946+ }
947+ }
948+ }
949+ }
950+
951+ private struct ThrottleState {
952+ var outerSignalComplete = false
953+ var hasFirstInnerProducer = false
954+ }
955+
822956extension Signal {
823957 /// Maps each event from `signal` to a new signal, then flattens the
824958 /// resulting producers (into a signal of values), according to the
0 commit comments