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
20 changes: 17 additions & 3 deletions Sources/ComposableArchitecture/Reducer/Reducers/DebugReducer.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import Combine
import Dispatch

extension Reducer {
#if swift(>=5.8)
/// Enhances a reducer with debug logging of received actions and state mutations for the given
Expand Down Expand Up @@ -26,13 +29,19 @@ extension Reducer {
#endif
}

private let printQueue = DispatchQueue(label: "co.pointfree.swift-composable-architecture.printer")

public struct _ReducerPrinter<State, Action> {
private let _printChange: (_ receivedAction: Action, _ oldState: State, _ newState: State) -> Void
@usableFromInline
let queue: DispatchQueue

public init(
printChange: @escaping (_ receivedAction: Action, _ oldState: State, _ newState: State) -> Void
printChange: @escaping (_ receivedAction: Action, _ oldState: State, _ newState: State) -> Void,
queue: DispatchQueue? = nil
) {
self._printChange = printChange
self.queue = queue ?? printQueue
}

public func printChange(receivedAction: Action, oldState: State, newState: State) {
Expand Down Expand Up @@ -81,8 +90,13 @@ public struct _PrintChangesReducer<Base: Reducer>: Reducer {
let oldState = state
let effects = self.base.reduce(into: &state, action: action)
return effects.merge(
with: .run { [newState = state] _ in
printer.printChange(receivedAction: action, oldState: oldState, newState: newState)
with: .publisher { [newState = state, queue = printer.queue] in
Deferred<Empty<Action, Never>> {
queue.async {
printer.printChange(receivedAction: action, oldState: oldState, newState: newState)
}
return Empty()
}
}
)
}
Expand Down
76 changes: 72 additions & 4 deletions Tests/ComposableArchitectureTests/DebugTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@
}

@MainActor
func testDebugReducer() async {
struct DebuggedReducer: Reducer {
func testDebugReducer() async throws {
struct Feature: Reducer {
typealias State = Int
typealias Action = Bool
func reduce(into state: inout Int, action: Bool) -> Effect<Bool> {
Expand All @@ -108,8 +108,76 @@
}
}

let store = TestStore(initialState: 0) { DebuggedReducer()._printChanges() }
await store.send(true) { $0 = 1 }
let logs = LockIsolated<String>("")
let printer = _ReducerPrinter<Feature.State, Feature.Action>(
printChange: { action, oldState, newState in
logs.withValue { _ = dump(action, to: &$0) }
}
)

let store = Store(initialState: 0) { Feature()._printChanges(printer) }
store.send(true)
try await Task.sleep(nanoseconds: 300_000_000)
XCTAssertNoDifference(
logs.value,
"""
- true

"""
)
}

func testDebugReducer_Order() {
struct Feature: Reducer {
typealias State = Int
typealias Action = Bool
func reduce(into state: inout Int, action: Bool) -> Effect<Bool> {
state += action ? 1 : -1
return .run { _ in await Task.yield() }
}
}

let logs = LockIsolated<String>("")
let printer = _ReducerPrinter<Feature.State, Feature.Action>(
printChange: { action, oldState, newState in
logs.withValue { _ = dump(action, to: &$0) }
}
)

let store = Store(initialState: 0) {
Feature()
._printChanges(printer)
._printChanges(printer)
._printChanges(printer)
._printChanges(printer)
}
store.send(true)
store.send(false)
store.send(true)
store.send(false)
_ = XCTWaiter.wait(for: [self.expectation(description: "wait")], timeout: 0.3)
XCTAssertNoDifference(
logs.value,
"""
- true
- true
- true
- true
- false
- false
- false
- false
- true
- true
- true
- true
- false
- false
- false
- false

"""
)
}
}
#endif