Skip to content

Commit fe43d83

Browse files
committed
Fix Effect.throttle (#654)
* Fix Effect.throttle Fixes #540. * Simplify * Publicize
1 parent 92cbdf6 commit fe43d83

File tree

4 files changed

+73
-14
lines changed

4 files changed

+73
-14
lines changed

.swiftpm/xcode/xcshareddata/xcschemes/ComposableArchitecture.xcscheme

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
buildConfiguration = "Debug"
2727
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
2828
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29-
shouldUseLaunchSchemeArgsEnv = "YES">
29+
shouldUseLaunchSchemeArgsEnv = "YES"
30+
codeCoverageEnabled = "YES">
3031
<Testables>
3132
<TestableReference
3233
skipped = "NO">

Sources/ComposableArchitecture/Debugging/ReducerDebugging.swift

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,24 @@ import Dispatch
66
public enum ActionFormat {
77
/// Prints the action in a single line by only specifying the labels of the associated values:
88
///
9-
/// ```swift
10-
/// Action.screenA(.row(index:, action: .textChanged(query:)))
11-
/// ```
9+
/// ```swift
10+
/// Action.screenA(.row(index:, action: .textChanged(query:)))
11+
/// ```
1212
///
1313
case labelsOnly
1414
/// Prints the action in a multiline, pretty-printed format, including all the labels of
1515
/// any associated values, as well as the data held in the associated values:
1616
///
17-
/// ```swift
18-
/// Action.screenA(
19-
/// ScreenA.row(
20-
/// index: 1,
21-
/// action: RowAction.textChanged(
22-
/// query: "Hi"
23-
/// )
24-
/// )
17+
/// ```swift
18+
/// Action.screenA(
19+
/// ScreenA.row(
20+
/// index: 1,
21+
/// action: RowAction.textChanged(
22+
/// query: "Hi"
2523
/// )
26-
/// ```
24+
/// )
25+
/// )
26+
/// ```
2727
///
2828
case prettyPrint
2929
}

Sources/ComposableArchitecture/Internal/Throttling.swift renamed to Sources/ComposableArchitecture/Effects/Throttling.swift

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Foundation
33
import ReactiveSwift
44

55
extension Effect {
6-
/// Turns an effect into one that can be throttled.
6+
/// Throttles an effect so that it only publishes one output per given interval.
77
///
88
/// - Parameters:
99
/// - id: The effect's identifier.
@@ -14,7 +14,11 @@ extension Effect {
1414
/// `false`, the producer emits the first element received during the interval.
1515
/// - Returns: An effect that emits either the most-recent or first element received during the
1616
/// specified interval.
17+
<<<<<<< ours:Sources/ComposableArchitecture/Internal/Throttling.swift
1718
func throttle(
19+
=======
20+
public func throttle<S>(
21+
>>>>>>> theirs:Sources/ComposableArchitecture/Effects/Throttling.swift
1822
id: AnyHashable,
1923
interval: TimeInterval,
2024
on scheduler: DateScheduler,
@@ -27,23 +31,41 @@ extension Effect {
2731
return Effect(value: value)
2832
}
2933

34+
<<<<<<< ours:Sources/ComposableArchitecture/Internal/Throttling.swift
3035
guard
3136
scheduler.currentDate.timeIntervalSince1970 - throttleTime.timeIntervalSince1970 < interval
3237
else {
3338
throttleTimes[id] = scheduler.currentDate
39+
=======
40+
let value = latest ? value : (throttleValues[id] as! Output? ?? value)
41+
throttleValues[id] = value
42+
43+
guard throttleTime.distance(to: scheduler.now) < interval else {
44+
throttleTimes[id] = scheduler.now
45+
>>>>>>> theirs:Sources/ComposableArchitecture/Effects/Throttling.swift
3446
throttleValues[id] = nil
3547
return Effect(value: value)
3648
}
3749

50+
<<<<<<< ours:Sources/ComposableArchitecture/Internal/Throttling.swift
3851
let value = latest ? value : (throttleValues[id] as! Value? ?? value)
3952
throttleValues[id] = value
4053

4154
return Effect(value: value)
55+
=======
56+
return Just(value)
57+
>>>>>>> theirs:Sources/ComposableArchitecture/Effects/Throttling.swift
4258
.delay(
4359
throttleTime.addingTimeInterval(interval).timeIntervalSince1970
4460
- scheduler.currentDate.timeIntervalSince1970,
4561
on: scheduler
4662
)
63+
<<<<<<< ours:Sources/ComposableArchitecture/Internal/Throttling.swift
64+
=======
65+
.handleEvents(receiveOutput: { _ in throttleTimes[id] = scheduler.now })
66+
.setFailureType(to: Failure.self)
67+
.eraseToAnyPublisher()
68+
>>>>>>> theirs:Sources/ComposableArchitecture/Effects/Throttling.swift
4769
}
4870
.cancellable(id: id, cancelInFlight: true)
4971
}

Tests/ComposableArchitectureTests/Internal/EffectThrottleTests.swift renamed to Tests/ComposableArchitectureTests/EffectThrottleTests.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,40 @@ final class EffectThrottleTests: XCTestCase {
125125
// A second value is emitted right away.
126126
XCTAssertEqual(values, [1, 2])
127127
}
128+
129+
func testThrottleEmitsFirstValueOnce() {
130+
var values: [Int] = []
131+
var effectRuns = 0
132+
133+
func runThrottledEffect(value: Int) {
134+
struct CancelToken: Hashable {}
135+
136+
Deferred { () -> Just<Int> in
137+
effectRuns += 1
138+
return Just(value)
139+
}
140+
.eraseToEffect()
141+
.throttle(
142+
id: CancelToken(), for: 1, scheduler: scheduler.eraseToAnyScheduler(), latest: false
143+
)
144+
.sink { values.append($0) }
145+
.store(in: &self.cancellables)
146+
}
147+
148+
runThrottledEffect(value: 1)
149+
150+
// A value emits right away.
151+
XCTAssertEqual(values, [1])
152+
153+
scheduler.advance(by: 0.5)
154+
155+
runThrottledEffect(value: 2)
156+
157+
scheduler.advance(by: 0.5)
158+
159+
runThrottledEffect(value: 3)
160+
161+
// A second value is emitted right away.
162+
XCTAssertEqual(values, [1, 2])
163+
}
128164
}

0 commit comments

Comments
 (0)