Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
Expand Down
24 changes: 12 additions & 12 deletions Sources/ComposableArchitecture/Debugging/ReducerDebugging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,24 @@ import Dispatch
public enum ActionFormat {
/// Prints the action in a single line by only specifying the labels of the associated values:
///
/// ```swift
/// Action.screenA(.row(index:, action: .textChanged(query:)))
/// ```
/// ```swift
/// Action.screenA(.row(index:, action: .textChanged(query:)))
/// ```
///
case labelsOnly
/// Prints the action in a multiline, pretty-printed format, including all the labels of
/// any associated values, as well as the data held in the associated values:
///
/// ```swift
/// Action.screenA(
/// ScreenA.row(
/// index: 1,
/// action: RowAction.textChanged(
/// query: "Hi"
/// )
/// )
/// ```swift
/// Action.screenA(
/// ScreenA.row(
/// index: 1,
/// action: RowAction.textChanged(
/// query: "Hi"
/// )
/// ```
/// )
/// )
/// ```
///
case prettyPrint
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Combine
import Dispatch

extension Effect {
/// Turns an effect into one that can be throttled.
/// Throttles an effect so that it only publishes one output per given interval.
///
/// - Parameters:
/// - id: The effect's identifier.
Expand All @@ -13,7 +13,7 @@ extension Effect {
/// `false`, the publisher emits the first element received during the interval.
/// - Returns: An effect that emits either the most-recent or first element received during the
/// specified interval.
func throttle<S>(
public func throttle<S>(
id: AnyHashable,
for interval: S.SchedulerTimeType.Stride,
scheduler: S,
Expand All @@ -26,19 +26,20 @@ extension Effect {
return Just(value).setFailureType(to: Failure.self).eraseToAnyPublisher()
}

let value = latest ? value : (throttleValues[id] as! Output? ?? value)
throttleValues[id] = value

guard throttleTime.distance(to: scheduler.now) < interval else {
throttleTimes[id] = scheduler.now
throttleValues[id] = nil
return Just(value).setFailureType(to: Failure.self).eraseToAnyPublisher()
}

let value = latest ? value : (throttleValues[id] as! Output? ?? value)
throttleValues[id] = value

return Just(value)
.delay(
for: scheduler.now.distance(to: throttleTime.advanced(by: interval)), scheduler: scheduler
)
.handleEvents(receiveOutput: { _ in throttleTimes[id] = scheduler.now })
.setFailureType(to: Failure.self)
.eraseToAnyPublisher()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,40 @@ final class EffectThrottleTests: XCTestCase {
// A second value is emitted right away.
XCTAssertEqual(values, [1, 2])
}

func testThrottleEmitsFirstValueOnce() {
var values: [Int] = []
var effectRuns = 0

func runThrottledEffect(value: Int) {
struct CancelToken: Hashable {}

Deferred { () -> Just<Int> in
effectRuns += 1
return Just(value)
}
.eraseToEffect()
.throttle(
id: CancelToken(), for: 1, scheduler: scheduler.eraseToAnyScheduler(), latest: false
)
.sink { values.append($0) }
.store(in: &self.cancellables)
}

runThrottledEffect(value: 1)

// A value emits right away.
XCTAssertEqual(values, [1])

scheduler.advance(by: 0.5)

runThrottledEffect(value: 2)

scheduler.advance(by: 0.5)

runThrottledEffect(value: 3)

// A second value is emitted right away.
XCTAssertEqual(values, [1, 2])
}
}