@@ -194,26 +194,6 @@ extension Loop {
194
194
) -> Feedback where Effect. Value == Event , Effect. Error == Never {
195
195
return Feedback ( compactingEvents: transform, effects: effects)
196
196
}
197
-
198
- /// Creates a Feedback which re-evaluates the given effect every time the
199
- /// state changes, and the transform consequentially yields a new value
200
- /// distinct from the last yielded value.
201
- ///
202
- /// If the previous effect is still alive when a new one is about to start,
203
- /// the previous one would automatically be cancelled.
204
- ///
205
- /// - parameters:
206
- /// - transform: The transform to apply on the state.
207
- /// - effects: The side effect accepting transformed values produced by
208
- /// `transform` and yielding events that eventually affect
209
- /// the state.
210
- @available ( * , deprecated, renamed: " init(skippingRepeatedState:effects:) " )
211
- public init < Control: Equatable , Effect: SignalProducerConvertible > (
212
- skippingRepeated transform: @escaping ( State ) -> Control ? ,
213
- effects: @escaping ( Control ) -> Effect
214
- ) where Effect. Value == Event , Effect. Error == Never {
215
- self . init ( skippingRepeatedState: transform, effects: effects)
216
- }
217
197
218
198
public init < Control: Equatable , Effect: SignalProducerConvertible > (
219
199
skippingRepeatedState transform: @escaping ( State ) -> Control ? ,
@@ -301,7 +281,6 @@ extension Loop {
301
281
/// - effects: The side effect accepting transformed values produced by
302
282
/// `transform` and yielding events that eventually affect
303
283
/// the state.
304
-
305
284
public static func lensing< Control, Effect: SignalProducerConvertible > (
306
285
state transform: @escaping ( State ) -> Control ? ,
307
286
effects: @escaping ( Control ) -> Effect
@@ -315,61 +294,104 @@ extension Loop {
315
294
) -> Feedback where Effect. Value == Event , Effect. Error == Never {
316
295
Feedback ( extractingPayload: transform, effects: effects)
317
296
}
318
-
319
- /// Creates a Feedback which re-evaluates the given effect every time the
320
- /// given predicate passes.
297
+
298
+ /// Create a Feedback which (re)starts the effect every time `transform` emits a non-nil value after a sequence
299
+ /// of `nil`, and ignore all the non-nil value afterwards. It does so until `transform` starts emitting a `nil`,
300
+ /// at which point the feedback cancels any outstanding effect.
321
301
///
322
- /// If the previous effect is still alive when a new one is about to start,
323
- /// the previous one would automatically be cancelled.
302
+ /// - parameters:
303
+ /// - transform: The transform to select a specific part of the state, or to cancel the outstanding effect
304
+ /// by returning `nil`.
305
+ /// - effects: The side effect accepting the first non-nil value produced by `transform`, and yielding events
306
+ /// that eventually affect the state.
307
+ public init < Value, Effect: SignalProducerConvertible > (
308
+ firstValueAfterNil transform: @escaping ( State ) -> Value ? ,
309
+ effects: @escaping ( Value ) -> Effect
310
+ ) where Effect. Value == Event , Effect. Error == Never {
311
+ self . init (
312
+ compacting: { state in
313
+ state
314
+ . scan ( into: ( false , nil ) ) { ( temp: inout ( lastWasNil: Bool , output: NilEdgeTransition < Value > ? ) , state: State ) in
315
+ let result = transform ( state)
316
+ temp. output = nil
317
+
318
+ switch ( temp. lastWasNil, result) {
319
+ case ( true , . none) , ( false , . some) :
320
+ return
321
+ case let ( true , . some( value) ) :
322
+ temp. lastWasNil = false
323
+ temp. output = . populated( value)
324
+ case ( false , . none) :
325
+ temp. lastWasNil = true
326
+ temp. output = . cleared
327
+ }
328
+ }
329
+ . compactMap { $0. output }
330
+ } ,
331
+ effects: { transition -> SignalProducer < Event , Never > in
332
+ switch transition {
333
+ case let . populated( value) :
334
+ return effects ( value) . producer
335
+ case . cleared:
336
+ return . empty
337
+ }
338
+ }
339
+ )
340
+ }
341
+
342
+ /// Create a feedback which (re)starts the effect every time `transform` emits a non-nil value after a sequence
343
+ /// of `nil`, and ignore all the non-nil value afterwards. It does so until `transform` starts emitting a `nil`,
344
+ /// at which point the feedback cancels any outstanding effect.
324
345
///
325
346
/// - parameters:
326
- /// - predicate: The predicate to apply on the state.
327
- /// - effects: The side effect accepting the state and yielding events
347
+ /// - transform: The transform to select a specific part of the state, or to cancel the outstanding effect
348
+ /// by returning `nil`.
349
+ /// - effects: The side effect accepting the first non-nil value produced by `transform`, and yielding events
328
350
/// that eventually affect the state.
351
+ public static func firstValueAfterNil< Value, Effect: SignalProducerConvertible > (
352
+ _ transform: @escaping ( State ) -> Value ? ,
353
+ effects: @escaping ( Value ) -> Effect
354
+ ) -> Feedback where Effect. Value == Event , Effect. Error == Never {
355
+ self . init ( firstValueAfterNil: transform, effects: effects)
356
+ }
357
+
358
+ /// Creates a Feedback which evaluates the given effect when the predicate transitions to `true`, and
359
+ /// cancels the outstanding effect when the predicate transitions to `false`.
360
+ ///
361
+ /// In other words, this variant treats the output of `predicate` as a binary signal. It starts the effect when
362
+ /// there is a positive edge, and cancels the outstanding effect (if any) when there is a negative edge.
363
+ ///
364
+ /// - parameters:
365
+ /// - predicate: The predicate to indicate whether effects should start or be cancelled.
366
+ /// - effects: The side effect accepting the state and yielding events that eventually affect the state.
329
367
public init < Effect: SignalProducerConvertible > (
330
- predicate: @escaping ( State ) -> Bool ,
368
+ whenBecomesTrue predicate: @escaping ( State ) -> Bool ,
331
369
effects: @escaping ( State ) -> Effect
332
370
) where Effect. Value == Event , Effect. Error == Never {
333
371
self . init (
334
- compactingState : { $0 } ,
372
+ firstValueAfterNil : { predicate ( $0 ) ? $0 : nil } ,
335
373
effects: { state -> SignalProducer < Event , Never > in
336
- predicate ( state ) ? effects ( state) . producer : . empty
374
+ effects ( state) . producer
337
375
}
338
376
)
339
377
}
340
-
341
- /// Creates a Feedback which re- evaluates the given effect every time the
342
- /// given predicate passes .
378
+
379
+ /// Creates a Feedback which evaluates the given effect when the predicate transitions to `true`, and
380
+ /// cancels the outstanding effect when the predicate transitions to `false` .
343
381
///
344
- /// If the previous effect is still alive when a new one is about to start,
345
- /// the previous one would automatically be cancelled .
382
+ /// In other words, this variant treats the output of `predicate` as a binary signal. It starts the effect when
383
+ /// there is a positive edge, and cancels the outstanding effect (if any) when there is a negative edge .
346
384
///
347
385
/// - parameters:
348
- /// - predicate: The predicate to apply on the state.
349
- /// - effects: The side effect accepting the state and yielding events
350
- /// that eventually affect the state.
351
- public static func predicate< Effect: SignalProducerConvertible > (
352
- state predicate: @escaping ( State ) -> Bool ,
386
+ /// - predicate: The predicate to indicate whether effects should start or be cancelled.
387
+ /// - effects: The side effect accepting the state and yielding events that eventually affect the state.
388
+ public static func whenBecomesTrue< Effect: SignalProducerConvertible > (
389
+ _ predicate: @escaping ( State ) -> Bool ,
353
390
effects: @escaping ( State ) -> Effect
354
391
) -> Feedback where Effect. Value == Event , Effect. Error == Never {
355
- Feedback ( predicate : predicate, effects: effects)
392
+ self . init ( whenBecomesTrue : predicate, effects: effects)
356
393
}
357
-
358
- /// Creates a Feedback which re-evaluates the given effect every time the
359
- /// state changes.
360
- ///
361
- /// If the previous effect is still alive when a new one is about to start,
362
- /// the previous one would automatically be cancelled.
363
- ///
364
- /// - parameters:
365
- /// - effects: The side effect accepting the state and yielding events
366
- /// that eventually affect the state.
367
- public init < Effect: SignalProducerConvertible > (
368
- effects: @escaping ( State ) -> Effect
369
- ) where Effect. Value == Event , Effect. Error == Never {
370
- self . init ( compactingState: { $0 } , effects: effects)
371
- }
372
-
394
+
373
395
/// Creates a Feedback which re-evaluates the given effect every time the
374
396
/// state changes with the Event that caused the change.
375
397
///
@@ -443,3 +465,67 @@ extension Loop {
443
465
}
444
466
}
445
467
}
468
+
469
+ extension Loop . Feedback {
470
+ /// Creates a Feedback which re-evaluates the given effect every time the
471
+ /// state changes, and the transform consequentially yields a new value
472
+ /// distinct from the last yielded value.
473
+ ///
474
+ /// If the previous effect is still alive when a new one is about to start,
475
+ /// the previous one would automatically be cancelled.
476
+ ///
477
+ /// - parameters:
478
+ /// - transform: The transform to apply on the state.
479
+ /// - effects: The side effect accepting transformed values produced by
480
+ /// `transform` and yielding events that eventually affect
481
+ /// the state.
482
+ @available ( * , deprecated, renamed: " init(skippingRepeatedState:effects:) " )
483
+ public init < Control: Equatable , Effect: SignalProducerConvertible > (
484
+ skippingRepeated transform: @escaping ( State ) -> Control ? ,
485
+ effects: @escaping ( Control ) -> Effect
486
+ ) where Effect. Value == Event , Effect. Error == Never {
487
+ self . init ( skippingRepeatedState: transform, effects: effects)
488
+ }
489
+
490
+ /// Creates a Feedback which re-evaluates the given effect every time the
491
+ /// given predicate passes.
492
+ ///
493
+ /// If the previous effect is still alive when a new one is about to start,
494
+ /// the previous one would automatically be cancelled.
495
+ ///
496
+ /// - parameters:
497
+ /// - predicate: The predicate to apply on the state.
498
+ /// - effects: The side effect accepting the state and yielding events
499
+ /// that eventually affect the state.
500
+ @available ( * , deprecated, message: " Use `Feedback.init(whenBecomesTrue:effects:)`, or other more appropriate variants. " )
501
+ public init < Effect: SignalProducerConvertible > (
502
+ predicate: @escaping ( State ) -> Bool ,
503
+ effects: @escaping ( State ) -> Effect
504
+ ) where Effect. Value == Event , Effect. Error == Never {
505
+ self . init ( compacting: { $0 } ,
506
+ effects: { state -> SignalProducer < Event , Never > in
507
+ predicate ( state) ? effects ( state) . producer : . empty
508
+ } )
509
+ }
510
+
511
+ /// Creates a Feedback which re-evaluates the given effect every time the
512
+ /// state changes.
513
+ ///
514
+ /// If the previous effect is still alive when a new one is about to start,
515
+ /// the previous one would automatically be cancelled.
516
+ ///
517
+ /// - parameters:
518
+ /// - effects: The side effect accepting the state and yielding events
519
+ /// that eventually affect the state.
520
+ @available ( * , deprecated, message: " Use `Feedback.init(whenBecomesTrue:effects:)`, or other more appropriate variants. " )
521
+ public init < Effect: SignalProducerConvertible > (
522
+ effects: @escaping ( State ) -> Effect
523
+ ) where Effect. Value == Event , Effect. Error == Never {
524
+ self . init ( compacting: { $0 } , effects: effects)
525
+ }
526
+ }
527
+
528
+ private enum NilEdgeTransition < Value> {
529
+ case populated( Value )
530
+ case cleared
531
+ }
0 commit comments