-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
Copy pathReducerDebugging.swift
167 lines (159 loc) · 5.75 KB
/
ReducerDebugging.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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import CasePaths
import Dispatch
/// Determines how the string description of an action should be printed when using the ``Reducer/debug(prefix:state:action:environment:)``
/// higher-order reducer.
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:)))
/// ```
///
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"
/// )
/// )
/// )
/// ```
///
case prettyPrint
}
extension Reducer {
/// Prints debug messages describing all received actions and state mutations.
///
/// Printing is only done in debug (`#if DEBUG`) builds.
///
/// - Parameters:
/// - prefix: A string with which to prefix all debug messages.
/// - toDebugEnvironment: A function that transforms an environment into a debug environment by
/// describing a print function and a queue to print from. Defaults to a function that ignores
/// the environment and returns a default ``DebugEnvironment`` that uses Swift's `print`
/// function and a background queue.
/// - Returns: A reducer that prints debug messages for all received actions.
public func debug(
_ prefix: String = "",
actionFormat: ActionFormat = .prettyPrint,
environment toDebugEnvironment: @escaping (Environment) -> DebugEnvironment = { _ in
DebugEnvironment()
}
) -> Reducer {
self.debug(
prefix,
state: { $0 },
action: .self,
actionFormat: actionFormat,
environment: toDebugEnvironment
)
}
/// Prints debug messages describing all received actions.
///
/// Printing is only done in debug (`#if DEBUG`) builds.
///
/// - Parameters:
/// - prefix: A string with which to prefix all debug messages.
/// - toDebugEnvironment: A function that transforms an environment into a debug environment by
/// describing a print function and a queue to print from. Defaults to a function that ignores
/// the environment and returns a default ``DebugEnvironment`` that uses Swift's `print`
/// function and a background queue.
/// - Returns: A reducer that prints debug messages for all received actions.
public func debugActions(
_ prefix: String = "",
actionFormat: ActionFormat = .prettyPrint,
environment toDebugEnvironment: @escaping (Environment) -> DebugEnvironment = { _ in
DebugEnvironment()
}
) -> Reducer {
self.debug(
prefix,
state: { _ in () },
action: .self,
actionFormat: actionFormat,
environment: toDebugEnvironment
)
}
/// Prints debug messages describing all received local actions and local state mutations.
///
/// Printing is only done in debug (`#if DEBUG`) builds.
///
/// - Parameters:
/// - prefix: A string with which to prefix all debug messages.
/// - toLocalState: A function that filters state to be printed.
/// - toLocalAction: A case path that filters actions that are printed.
/// - toDebugEnvironment: A function that transforms an environment into a debug environment by
/// describing a print function and a queue to print from. Defaults to a function that ignores
/// the environment and returns a default ``DebugEnvironment`` that uses Swift's `print`
/// function and a background queue.
/// - Returns: A reducer that prints debug messages for all received actions.
public func debug<LocalState, LocalAction>(
_ prefix: String = "",
state toLocalState: @escaping (State) -> LocalState,
action toLocalAction: CasePath<Action, LocalAction>,
actionFormat: ActionFormat = .prettyPrint,
environment toDebugEnvironment: @escaping (Environment) -> DebugEnvironment = { _ in
DebugEnvironment()
}
) -> Reducer {
#if DEBUG
return .init { state, action, environment in
let previousState = toLocalState(state)
let effects = self.run(&state, action, environment)
guard let localAction = toLocalAction.extract(from: action) else { return effects }
let nextState = toLocalState(state)
let debugEnvironment = toDebugEnvironment(environment)
return .merge(
.fireAndForget {
debugEnvironment.queue.async {
let actionOutput =
actionFormat == .prettyPrint
? debugOutput(localAction).indent(by: 2)
: debugCaseOutput(localAction).indent(by: 2)
let stateOutput =
LocalState.self == Void.self
? ""
: debugDiff(previousState, nextState).map { "\($0)\n" } ?? " (No state changes)\n"
debugEnvironment.printer(
"""
\(prefix.isEmpty ? "" : "\(prefix): ")received action:
\(actionOutput)
\(stateOutput)
"""
)
}
},
effects
)
}
#else
return self
#endif
}
}
/// An environment for debug-printing reducers.
public struct DebugEnvironment {
public var printer: (String) -> Void
public var queue: DispatchQueue
public init(
printer: @escaping (String) -> Void = { print($0) },
queue: DispatchQueue
) {
self.printer = printer
self.queue = queue
}
public init(
printer: @escaping (String) -> Void = { print($0) }
) {
self.init(printer: printer, queue: _queue)
}
}
private let _queue = DispatchQueue(
label: "co.pointfree.ComposableArchitecture.DebugEnvironment",
qos: .background
)