-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
DebugReducer.swift
122 lines (111 loc) · 3.7 KB
/
DebugReducer.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
/// printer.
///
/// > Note: Printing is only done in `DEBUG` configurations.
///
/// - Parameter printer: A printer for printing debug messages.
/// - Returns: A reducer that prints debug messages for all received actions.
@inlinable
@warn_unqualified_access
@_documentation(visibility:public)
public func _printChanges(
_ printer: _ReducerPrinter<State, Action>? = .customDump
) -> _PrintChangesReducer<Self> {
_PrintChangesReducer<Self>(base: self, printer: printer)
}
#else
@inlinable
@warn_unqualified_access
public func _printChanges(
_ printer: _ReducerPrinter<State, Action>? = .customDump
) -> _PrintChangesReducer<Self> {
_PrintChangesReducer<Self>(base: self, printer: printer)
}
#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,
queue: DispatchQueue? = nil
) {
self._printChange = printChange
self.queue = queue ?? printQueue
}
public func printChange(receivedAction: Action, oldState: State, newState: State) {
self._printChange(receivedAction, oldState, newState)
}
}
extension _ReducerPrinter {
public static var customDump: Self {
Self { receivedAction, oldState, newState in
var target = ""
target.write("received action:\n")
CustomDump.customDump(receivedAction, to: &target, indent: 2)
target.write("\n")
target.write(diff(oldState, newState).map { "\($0)\n" } ?? " (No state changes)\n")
print(target)
}
}
public static var actionLabels: Self {
Self { receivedAction, _, _ in
print("received action: \(debugCaseOutput(receivedAction))")
}
}
}
public struct _PrintChangesReducer<Base: Reducer>: Reducer {
@usableFromInline
let base: Base
@usableFromInline
let printer: _ReducerPrinter<Base.State, Base.Action>?
@usableFromInline
init(base: Base, printer: _ReducerPrinter<Base.State, Base.Action>?) {
self.base = base
self.printer = printer
}
#if DEBUG
public func reduce(
into state: inout Base.State, action: Base.Action
) -> Effect<Base.Action> {
if let printer = self.printer {
return withSharedChangeTracking { changeTracker in
let oldState = state
let effects = self.base.reduce(into: &state, action: action)
return withEscapedDependencies { continuation in
effects.merge(
with: .publisher { [newState = state, queue = printer.queue] in
Deferred<Empty<Action, Never>> {
queue.async {
continuation.yield {
changeTracker.assert {
printer.printChange(
receivedAction: action, oldState: oldState, newState: newState
)
}
}
}
return Empty()
}
}
)
}
}
}
return self.base.reduce(into: &state, action: action)
}
#else
@inlinable
public func reduce(
into state: inout Base.State, action: Base.Action
) -> Effect<Base.Action> {
return self.base.reduce(into: &state, action: action)
}
#endif
}