Skip to content

Commit d234dd5

Browse files
committed
updated reducer debugging
1 parent 79e03e4 commit d234dd5

File tree

5 files changed

+106
-117
lines changed

5 files changed

+106
-117
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# ``ComposableArchitecture/ReducerProtocol/debug()``
2+
3+
## Topics
4+
5+
### Custom strategies
6+
7+
- ``ReducerProtocol/debug(_:)``
8+
- ``DebugStrategy``
9+
- ``CustomDumpStrategy``
10+
- ``ActionLabelsStrategy``

Sources/ComposableArchitecture/Documentation.docc/Extensions/ReducerProtocol.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
### Reducer modifiers
3030

31-
- ``debug(_:state:action:actionFormat:to:)``
31+
- ``debug()``
3232
- ``dependency(_:_:)``
3333
- ``dependencies(_:)``
3434
- ``signpost(_:log:)``

Sources/ComposableArchitecture/Documentation.docc/Extensions/ReducerProtocolDebug.md

Lines changed: 0 additions & 8 deletions
This file was deleted.

Sources/ComposableArchitecture/Reducer/AnyReducer/AnyReducerDebug.swift

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,60 @@ extension AnyReducer {
222222
}
223223
}
224224

225+
/// Determines how the string description of an action should be printed when using the
226+
/// ``ReducerProtocol/debug(_:state:action:actionFormat:to:)`` higher-order reducer.
227+
@available(
228+
iOS,
229+
deprecated: 9999.0,
230+
message: """
231+
This API that used this type has been soft-deprecated in favor of 'ReducerProtocol.debug'. Read the migration guide for more information: https://pointfreeco.github.io/swift-composable-architecture/protocol/documentation/composablearchitecture/reducerprotocols
232+
"""
233+
)
234+
@available(
235+
macOS,
236+
deprecated: 9999.0,
237+
message: """
238+
This API that used this type has been soft-deprecated in favor of 'ReducerProtocol.debug'. Read the migration guide for more information: https://pointfreeco.github.io/swift-composable-architecture/protocol/documentation/composablearchitecture/reducerprotocols
239+
"""
240+
)
241+
@available(
242+
tvOS,
243+
deprecated: 9999.0,
244+
message: """
245+
This API that used this type has been soft-deprecated in favor of 'ReducerProtocol.debug'. Read the migration guide for more information: https://pointfreeco.github.io/swift-composable-architecture/protocol/documentation/composablearchitecture/reducerprotocols
246+
"""
247+
)
248+
@available(
249+
watchOS,
250+
deprecated: 9999.0,
251+
message: """
252+
This API that used this type has been soft-deprecated in favor of 'ReducerProtocol.debug'. Read the migration guide for more information: https://pointfreeco.github.io/swift-composable-architecture/protocol/documentation/composablearchitecture/reducerprotocols
253+
"""
254+
)
255+
public enum ActionFormat: Sendable {
256+
/// Prints the action in a single line by only specifying the labels of the associated values:
257+
///
258+
/// ```swift
259+
/// Action.screenA(.row(index:, action: .textChanged(query:)))
260+
/// ```
261+
case labelsOnly
262+
263+
/// Prints the action in a multiline, pretty-printed format, including all the labels of
264+
/// any associated values, as well as the data held in the associated values:
265+
///
266+
/// ```swift
267+
/// Action.screenA(
268+
/// ScreenA.row(
269+
/// index: 1,
270+
/// action: RowAction.textChanged(
271+
/// query: "Hi"
272+
/// )
273+
/// )
274+
/// )
275+
/// ```
276+
case prettyPrint
277+
}
278+
225279
/// The API that used this type has been soft-deprecated in favor of
226280
/// ``ReducerProtocol/debug(_:state:action:actionFormat:to:)`` Read <doc:ReducerProtocols> for more
227281
/// information.

Sources/ComposableArchitecture/Reducer/Reducers/DebugReducer.swift

Lines changed: 41 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -3,116 +3,67 @@ extension ReducerProtocol {
33
///
44
/// > Note: Printing is only done in `DEBUG` configurations.
55
///
6-
/// - Parameters:
7-
/// - prefix: A string that will prefix all debug messages.
8-
/// - actionFormat: The format used to print actions.
9-
/// - logger: A function that is used to print debug messages.
106
/// - Returns: A reducer that prints debug messages for all received actions.
117
@inlinable
12-
public func debug(
13-
_ prefix: String = "",
14-
actionFormat: ActionFormat = .prettyPrint,
15-
to logger: @escaping @Sendable (String) async -> Void = { print($0) }
16-
) -> _DebugReducer<Self, State, Action> {
17-
self.debug(
18-
prefix,
19-
state: { $0 },
20-
action: { $0 },
21-
actionFormat: actionFormat,
22-
to: logger
23-
)
8+
public func debug() -> _DebugReducer<Self, CustomDumpStrategy> {
9+
.init(base: self, strategy: .customDump)
2410
}
2511

26-
/// Enhances a reducer with debug logging of received actions and state mutations.
12+
/// Enhances a reducer with debug logging of received actions and state mutations for the given
13+
/// ``DebugStrategy``.
2714
///
2815
/// > Note: Printing is only done in `DEBUG` configurations.
2916
///
30-
/// - Parameters:
31-
/// - prefix: A string with which to prefix all debug messages.
32-
/// - toChildState: A function that filters state to be printed.
33-
/// - toChildAction: A case path that filters actions to be printed.
34-
/// - actionFormat: The format used to print actions.
35-
/// - logger: A function that is used to print debug messages.
36-
/// - Returns: A reducer that prints debug messages for all received, filtered actions.
17+
/// - Parameter strategy: A strategy for printing debug messages.
18+
/// - Returns: A reducer that prints debug messages for all received actions.
3719
@inlinable
38-
public func debug<ChildState, ChildAction>(
39-
_ prefix: String = "",
40-
state toChildState: @escaping (State) -> ChildState,
41-
action toChildAction: @escaping (Action) -> ChildAction?,
42-
actionFormat: ActionFormat = .prettyPrint,
43-
to logger: @escaping @Sendable (String) async -> Void = { print($0) }
44-
) -> _DebugReducer<Self, ChildState, ChildAction> {
45-
.init(
46-
base: self,
47-
prefix: prefix,
48-
state: toChildState,
49-
action: toChildAction,
50-
actionFormat: actionFormat,
51-
logger: logger
52-
)
20+
public func debug<Strategy: DebugStrategy>(
21+
_ strategy: Strategy
22+
) -> _DebugReducer<Self, Strategy> {
23+
.init(base: self, strategy: strategy)
5324
}
5425
}
5526

56-
/// Determines how the string description of an action should be printed when using the
57-
/// ``ReducerProtocol/debug(_:state:action:actionFormat:to:)`` higher-order reducer.
58-
public enum ActionFormat: Sendable {
59-
/// Prints the action in a single line by only specifying the labels of the associated values:
60-
///
61-
/// ```swift
62-
/// Action.screenA(.row(index:, action: .textChanged(query:)))
63-
/// ```
64-
case labelsOnly
65-
66-
/// Prints the action in a multiline, pretty-printed format, including all the labels of
67-
/// any associated values, as well as the data held in the associated values:
68-
///
69-
/// ```swift
70-
/// Action.screenA(
71-
/// ScreenA.row(
72-
/// index: 1,
73-
/// action: RowAction.textChanged(
74-
/// query: "Hi"
75-
/// )
76-
/// )
77-
/// )
78-
/// ```
79-
case prettyPrint
27+
public protocol DebugStrategy {
28+
func debug<Action, State>(receivedAction: Action, oldState: State, newState: State)
8029
}
8130

82-
public struct _DebugReducer<Base: ReducerProtocol, DebugState, DebugAction>: ReducerProtocol {
83-
@usableFromInline
84-
let base: Base
31+
extension DebugStrategy where Self == CustomDumpStrategy {
32+
public static var customDump: Self { Self() }
33+
}
8534

86-
@usableFromInline
87-
let prefix: String
35+
public struct CustomDumpStrategy: DebugStrategy {
36+
public func debug<Action, State>(receivedAction: Action, oldState: State, newState: State) {
37+
var target = ""
38+
target.write("received action:\n")
39+
CustomDump.customDump(receivedAction, to: &target, indent: 2)
40+
target.write("\n")
41+
target.write(diff(oldState, newState).map { "\($0)\n" } ?? " (No state changes)\n")
42+
print(target)
43+
}
44+
}
8845

89-
@usableFromInline
90-
let toDebugState: (State) -> DebugState
46+
extension DebugStrategy where Self == ActionLabelsStrategy {
47+
public static var actionLabels: Self { Self() }
48+
}
9149

92-
@usableFromInline
93-
let toDebugAction: (Action) -> DebugAction?
50+
public struct ActionLabelsStrategy: DebugStrategy {
51+
public func debug<Action, State>(receivedAction: Action, oldState: State, newState: State) {
52+
print("received action: \(debugCaseOutput(receivedAction))")
53+
}
54+
}
9455

56+
public struct _DebugReducer<Base: ReducerProtocol, Strategy: DebugStrategy>: ReducerProtocol {
9557
@usableFromInline
96-
let actionFormat: ActionFormat
58+
let base: Base
9759

9860
@usableFromInline
99-
let logger: @Sendable (String) async -> Void
61+
let strategy: Strategy
10062

10163
@inlinable
102-
init(
103-
base: Base,
104-
prefix: String,
105-
state toDebugState: @escaping (State) -> DebugState,
106-
action toDebugAction: @escaping (Action) -> DebugAction?,
107-
actionFormat: ActionFormat,
108-
logger: @escaping @Sendable (String) async -> Void
109-
) {
64+
init(base: Base, strategy: Strategy) {
11065
self.base = base
111-
self.prefix = prefix
112-
self.toDebugState = toDebugState
113-
self.toDebugAction = toDebugAction
114-
self.actionFormat = actionFormat
115-
self.logger = logger
66+
self.strategy = strategy
11667
}
11768

11869
@usableFromInline
@@ -124,29 +75,11 @@ public struct _DebugReducer<Base: ReducerProtocol, DebugState, DebugAction>: Red
12475
) -> Effect<Base.Action, Never> {
12576
#if DEBUG
12677
if self.context != .test {
127-
let previousState = self.toDebugState(state)
78+
let oldState = state
12879
let effects = self.base.reduce(into: &state, action: action)
129-
guard let debugAction = self.toDebugAction(action) else { return effects }
130-
let nextState = self.toDebugState(state)
13180
return effects.merge(
132-
with: .fireAndForget { [actionFormat] in
133-
var actionOutput = ""
134-
if actionFormat == .prettyPrint {
135-
customDump(debugAction, to: &actionOutput, indent: 2)
136-
} else {
137-
actionOutput.write(debugCaseOutput(debugAction).indent(by: 2))
138-
}
139-
let stateOutput =
140-
DebugState.self == Void.self
141-
? ""
142-
: diff(previousState, nextState).map { "\($0)\n" } ?? " (No state changes)\n"
143-
await self.logger(
144-
"""
145-
\(self.prefix.isEmpty ? "" : "\(self.prefix): ")received action:
146-
\(actionOutput)
147-
\(stateOutput)
148-
"""
149-
)
81+
with: .fireAndForget { [newState = state] in
82+
self.strategy.debug(receivedAction: action, oldState: oldState, newState: newState)
15083
}
15184
)
15285
}

0 commit comments

Comments
 (0)