Skip to content

Commit 761ab29

Browse files
authored
Improve reducer builder inference and prepare for Swift 5.8 (#1863)
* Update builders for changes coming in Swift 5.8 * wip * wip
1 parent e294b24 commit 761ab29

File tree

9 files changed

+108
-159
lines changed

9 files changed

+108
-159
lines changed

Sources/ComposableArchitecture/Internal/Deprecations.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ import XCTestDynamicOverlay
55

66
// MARK: - Deprecated after 0.49.2
77

8+
@available(
9+
*,
10+
deprecated,
11+
message: "Use 'ReducerBuilder<_, _>' with explicit 'State' and 'Action' generics, instead."
12+
)
13+
public typealias ReducerBuilderOf<R: ReducerProtocol> = ReducerBuilder<R.State, R.Action>
14+
815
// NB: As of Swift 5.7, property wrapper deprecations are not diagnosed, so we may want to keep this
916
// deprecation around for now:
1017
// https://github.com/apple/swift/issues/63139

Sources/ComposableArchitecture/Reducer/AnyReducer/AnyReducerCompatibility.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,9 @@ extension Reduce {
9090
"""
9191
)
9292
extension AnyReducer {
93-
public init<R: ReducerProtocol>(@ReducerBuilderOf<R> _ build: @escaping (Environment) -> R)
94-
where R.State == State, R.Action == Action {
93+
public init<R: ReducerProtocol>(
94+
@ReducerBuilder<State, Action> _ build: @escaping (Environment) -> R
95+
) where R.State == State, R.Action == Action {
9596
self.init { state, action, environment in
9697
build(environment).reduce(into: &state, action: action)
9798
}

Sources/ComposableArchitecture/Reducer/ReducerBuilder.swift

Lines changed: 65 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -7,101 +7,82 @@
77
/// See ``CombineReducers`` for an entry point into a reducer builder context.
88
@resultBuilder
99
public enum ReducerBuilder<State, Action> {
10-
#if swift(>=5.7)
11-
@inlinable
12-
public static func buildArray(
13-
_ reducers: [some ReducerProtocol<State, Action>]
14-
) -> some ReducerProtocol<State, Action> {
15-
_SequenceMany(reducers: reducers)
16-
}
17-
18-
@inlinable
19-
public static func buildBlock() -> some ReducerProtocol<State, Action> {
20-
EmptyReducer()
21-
}
22-
23-
@inlinable
24-
public static func buildBlock(
25-
_ reducer: some ReducerProtocol<State, Action>
26-
) -> some ReducerProtocol<State, Action> {
27-
reducer
28-
}
10+
@inlinable
11+
public static func buildArray<R: ReducerProtocol>(_ reducers: [R]) -> _SequenceMany<R>
12+
where R.State == State, R.Action == Action {
13+
_SequenceMany(reducers: reducers)
14+
}
2915

30-
@inlinable
31-
public static func buildEither<R0: ReducerProtocol, R1: ReducerProtocol>(
32-
first reducer: R0
33-
) -> _Conditional<R0, R1>
34-
where R0.State == State, R0.Action == Action, R1.State == State, R1.Action == Action {
35-
.first(reducer)
36-
}
16+
@inlinable
17+
public static func buildBlock() -> EmptyReducer<State, Action> {
18+
EmptyReducer()
19+
}
3720

38-
@inlinable
39-
public static func buildEither<R0: ReducerProtocol, R1: ReducerProtocol>(
40-
second reducer: R1
41-
) -> _Conditional<R0, R1>
42-
where R0.State == State, R0.Action == Action, R1.State == State, R1.Action == Action {
43-
.second(reducer)
44-
}
21+
@inlinable
22+
public static func buildBlock<R: ReducerProtocol>(_ reducer: R) -> R
23+
where R.State == State, R.Action == Action {
24+
reducer
25+
}
4526

46-
@inlinable
47-
public static func buildExpression(
48-
_ expression: some ReducerProtocol<State, Action>
49-
) -> some ReducerProtocol<State, Action> {
50-
expression
51-
}
27+
@inlinable
28+
public static func buildEither<R0: ReducerProtocol, R1: ReducerProtocol>(
29+
first reducer: R0
30+
) -> _Conditional<R0, R1>
31+
where R0.State == State, R0.Action == Action, R1.State == State, R1.Action == Action {
32+
.first(reducer)
33+
}
5234

53-
@inlinable
54-
public static func buildFinalResult(
55-
_ reducer: some ReducerProtocol<State, Action>
56-
) -> some ReducerProtocol<State, Action> {
57-
reducer
58-
}
35+
@inlinable
36+
public static func buildEither<R0: ReducerProtocol, R1: ReducerProtocol>(
37+
second reducer: R1
38+
) -> _Conditional<R0, R1>
39+
where R0.State == State, R0.Action == Action, R1.State == State, R1.Action == Action {
40+
.second(reducer)
41+
}
5942

60-
@inlinable
61-
public static func buildLimitedAvailability(
62-
_ wrapped: some ReducerProtocol<State, Action>
63-
) -> Reduce<State, Action> {
64-
Reduce(wrapped)
65-
}
43+
@inlinable
44+
public static func buildExpression<R: ReducerProtocol>(_ expression: R) -> R
45+
where R.State == State, R.Action == Action {
46+
expression
47+
}
6648

67-
@inlinable
68-
public static func buildOptional(
69-
_ wrapped: (some ReducerProtocol<State, Action>)?
70-
) -> some ReducerProtocol<State, Action> {
71-
wrapped
72-
}
49+
@inlinable
50+
public static func buildFinalResult<R: ReducerProtocol>(_ reducer: R) -> R
51+
where R.State == State, R.Action == Action {
52+
reducer
53+
}
7354

74-
@inlinable
75-
public static func buildPartialBlock(
76-
first: some ReducerProtocol<State, Action>
77-
) -> some ReducerProtocol<State, Action> {
78-
first
79-
}
55+
@inlinable
56+
public static func buildLimitedAvailability<R: ReducerProtocol>(
57+
_ wrapped: R
58+
) -> Reduce<State, Action>
59+
where R.State == State, R.Action == Action {
60+
Reduce(wrapped)
61+
}
8062

81-
@inlinable
82-
public static func buildPartialBlock(
83-
accumulated: some ReducerProtocol<State, Action>, next: some ReducerProtocol<State, Action>
84-
) -> some ReducerProtocol<State, Action> {
85-
_Sequence(accumulated, next)
86-
}
87-
#else
88-
@inlinable
89-
public static func buildArray<R: ReducerProtocol>(_ reducers: [R]) -> _SequenceMany<R>
90-
where R.State == State, R.Action == Action {
91-
_SequenceMany(reducers: reducers)
92-
}
63+
@inlinable
64+
public static func buildOptional<R: ReducerProtocol>(_ wrapped: R?) -> R?
65+
where R.State == State, R.Action == Action {
66+
wrapped
67+
}
9368

94-
@inlinable
95-
public static func buildBlock() -> EmptyReducer<State, Action> {
96-
EmptyReducer()
97-
}
69+
@inlinable
70+
public static func buildPartialBlock<R: ReducerProtocol>(
71+
first: R
72+
) -> R
73+
where R.State == State, R.Action == Action {
74+
first
75+
}
9876

99-
@inlinable
100-
public static func buildBlock<R: ReducerProtocol>(_ reducer: R) -> R
101-
where R.State == State, R.Action == Action {
102-
reducer
103-
}
77+
@inlinable
78+
public static func buildPartialBlock<R0: ReducerProtocol, R1: ReducerProtocol>(
79+
accumulated: R0, next: R1
80+
) -> _Sequence<R0, R1>
81+
where R0.State == State, R0.Action == Action, R1.State == State, R1.Action == Action {
82+
_Sequence(accumulated, next)
83+
}
10484

85+
#if swift(<5.7)
10586
@inlinable
10687
public static func buildBlock<
10788
R0: ReducerProtocol,
@@ -330,54 +311,12 @@ public enum ReducerBuilder<State, Action> {
330311
)
331312
}
332313

333-
@inlinable
334-
public static func buildEither<R0: ReducerProtocol, R1: ReducerProtocol>(
335-
first reducer: R0
336-
) -> _Conditional<R0, R1>
337-
where R0.State == State, R0.Action == Action {
338-
.first(reducer)
339-
}
340-
341-
@inlinable
342-
public static func buildEither<R0: ReducerProtocol, R1: ReducerProtocol>(
343-
second reducer: R1
344-
) -> _Conditional<R0, R1>
345-
where R1.State == State, R1.Action == Action {
346-
.second(reducer)
347-
}
348-
349-
@inlinable
350-
public static func buildExpression<R: ReducerProtocol>(_ expression: R) -> R
351-
where R.State == State, R.Action == Action {
352-
expression
353-
}
354-
355-
@inlinable
356-
public static func buildFinalResult<R: ReducerProtocol>(_ reducer: R) -> R
357-
where R.State == State, R.Action == Action {
358-
reducer
359-
}
360-
361314
@_disfavoredOverload
362315
@inlinable
363316
public static func buildFinalResult<R: ReducerProtocol>(_ reducer: R) -> Reduce<State, Action>
364317
where R.State == State, R.Action == Action {
365318
Reduce(reducer)
366319
}
367-
368-
@inlinable
369-
public static func buildLimitedAvailability<R: ReducerProtocol>(
370-
_ wrapped: R
371-
) -> Reduce<R.State, R.Action>
372-
where R.State == State, R.Action == Action {
373-
Reduce(wrapped)
374-
}
375-
376-
@inlinable
377-
public static func buildOptional<R: ReducerProtocol>(_ wrapped: R?) -> R?
378-
where R.State == State, R.Action == Action {
379-
wrapped
380-
}
381320
#endif
382321

383322
public enum _Conditional<First: ReducerProtocol, Second: ReducerProtocol>: ReducerProtocol
@@ -440,5 +379,3 @@ public enum ReducerBuilder<State, Action> {
440379
}
441380
}
442381
}
443-
444-
public typealias ReducerBuilderOf<R: ReducerProtocol> = ReducerBuilder<R.State, R.Action>

Sources/ComposableArchitecture/Reducer/Reducers/CombineReducers.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
/// .ifLet(\.child, action: /Action.child)
1616
/// }
1717
/// ```
18-
public struct CombineReducers<Reducers: ReducerProtocol>: ReducerProtocol {
18+
public struct CombineReducers<State, Action, Reducers: ReducerProtocol>: ReducerProtocol
19+
where State == Reducers.State, Action == Reducers.Action {
1920
@usableFromInline
2021
let reducers: Reducers
2122

@@ -24,7 +25,7 @@ public struct CombineReducers<Reducers: ReducerProtocol>: ReducerProtocol {
2425
/// - Parameter build: A reducer builder.
2526
@inlinable
2627
public init(
27-
@ReducerBuilderOf<Reducers> _ build: () -> Reducers
28+
@ReducerBuilder<State, Action> _ build: () -> Reducers
2829
) {
2930
self.init(internal: build())
3031
}

Sources/ComposableArchitecture/Reducer/Reducers/ForEachReducer.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,15 @@ extension ReducerProtocol {
5050
/// state.
5151
/// - Returns: A reducer that combines the child reducer with the parent reducer.
5252
@inlinable
53-
public func forEach<ID: Hashable, Element: ReducerProtocol>(
54-
_ toElementsState: WritableKeyPath<State, IdentifiedArray<ID, Element.State>>,
55-
action toElementAction: CasePath<Action, (ID, Element.Action)>,
56-
@ReducerBuilderOf<Element> _ element: () -> Element,
53+
public func forEach<ElementState, ElementAction, ID: Hashable, Element: ReducerProtocol>(
54+
_ toElementsState: WritableKeyPath<State, IdentifiedArray<ID, ElementState>>,
55+
action toElementAction: CasePath<Action, (ID, ElementAction)>,
56+
@ReducerBuilder<ElementState, ElementAction> _ element: () -> Element,
5757
file: StaticString = #file,
5858
fileID: StaticString = #fileID,
5959
line: UInt = #line
60-
) -> _ForEachReducer<Self, ID, Element> {
60+
) -> _ForEachReducer<Self, ID, Element>
61+
where ElementState == Element.State, ElementAction == Element.Action {
6162
_ForEachReducer(
6263
parent: self,
6364
toElementsState: toElementsState,

Sources/ComposableArchitecture/Reducer/Reducers/IfCaseLetReducer.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,15 @@ extension ReducerProtocol {
4646
/// present
4747
/// - Returns: A reducer that combines the child reducer with the parent reducer.
4848
@inlinable
49-
public func ifCaseLet<Case: ReducerProtocol>(
50-
_ toCaseState: CasePath<State, Case.State>,
51-
action toCaseAction: CasePath<Action, Case.Action>,
52-
@ReducerBuilderOf<Case> then case: () -> Case,
49+
public func ifCaseLet<CaseState, CaseAction, Case: ReducerProtocol>(
50+
_ toCaseState: CasePath<State, CaseState>,
51+
action toCaseAction: CasePath<Action, CaseAction>,
52+
@ReducerBuilder<CaseState, CaseAction> then case: () -> Case,
5353
file: StaticString = #file,
5454
fileID: StaticString = #fileID,
5555
line: UInt = #line
56-
) -> _IfCaseLetReducer<Self, Case> {
56+
) -> _IfCaseLetReducer<Self, Case>
57+
where CaseState == Case.State, CaseAction == Case.Action {
5758
.init(
5859
parent: self,
5960
child: `case`(),

Sources/ComposableArchitecture/Reducer/Reducers/IfLetReducer.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,15 @@ extension ReducerProtocol {
4343
/// state.
4444
/// - Returns: A reducer that combines the child reducer with the parent reducer.
4545
@inlinable
46-
public func ifLet<Wrapped: ReducerProtocol>(
47-
_ toWrappedState: WritableKeyPath<State, Wrapped.State?>,
48-
action toWrappedAction: CasePath<Action, Wrapped.Action>,
49-
@ReducerBuilderOf<Wrapped> then wrapped: () -> Wrapped,
46+
public func ifLet<WrappedState, WrappedAction, Wrapped: ReducerProtocol>(
47+
_ toWrappedState: WritableKeyPath<State, WrappedState?>,
48+
action toWrappedAction: CasePath<Action, WrappedAction>,
49+
@ReducerBuilder<WrappedState, WrappedAction> then wrapped: () -> Wrapped,
5050
file: StaticString = #file,
5151
fileID: StaticString = #fileID,
5252
line: UInt = #line
53-
) -> _IfLetReducer<Self, Wrapped> {
53+
) -> _IfLetReducer<Self, Wrapped>
54+
where WrappedState == Wrapped.State, WrappedAction == Wrapped.Action {
5455
.init(
5556
parent: self,
5657
child: wrapped(),

Sources/ComposableArchitecture/Reducer/Reducers/Scope.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,11 @@ public struct Scope<ParentState, ParentAction, Child: ReducerProtocol>: ReducerP
143143
/// - toChildAction: A case path from parent action to a case containing child actions.
144144
/// - child: A reducer that will be invoked with child actions against child state.
145145
@inlinable
146-
public init(
147-
state toChildState: WritableKeyPath<ParentState, Child.State>,
148-
action toChildAction: CasePath<ParentAction, Child.Action>,
149-
@ReducerBuilderOf<Child> _ child: () -> Child
150-
) {
146+
public init<ChildState, ChildAction>(
147+
state toChildState: WritableKeyPath<ParentState, ChildState>,
148+
action toChildAction: CasePath<ParentAction, ChildAction>,
149+
@ReducerBuilder<ChildState, ChildAction> _ child: () -> Child
150+
) where ChildState == Child.State, ChildAction == Child.Action {
151151
self.init(
152152
toChildState: .keyPath(toChildState),
153153
toChildAction: toChildAction,
@@ -215,14 +215,14 @@ public struct Scope<ParentState, ParentAction, Child: ReducerProtocol>: ReducerP
215215
/// - toChildAction: A case path from parent action to a case containing child actions.
216216
/// - child: A reducer that will be invoked with child actions against child state.
217217
@inlinable
218-
public init(
219-
state toChildState: CasePath<ParentState, Child.State>,
220-
action toChildAction: CasePath<ParentAction, Child.Action>,
221-
@ReducerBuilderOf<Child> _ child: () -> Child,
218+
public init<ChildState, ChildAction>(
219+
state toChildState: CasePath<ParentState, ChildState>,
220+
action toChildAction: CasePath<ParentAction, ChildAction>,
221+
@ReducerBuilder<ChildState, ChildAction> _ child: () -> Child,
222222
file: StaticString = #file,
223223
fileID: StaticString = #fileID,
224224
line: UInt = #line
225-
) {
225+
) where ChildState == Child.State, ChildAction == Child.Action {
226226
self.init(
227227
toChildState: .casePath(toChildState, file: file, fileID: fileID, line: line),
228228
toChildAction: toChildAction,

Tests/ComposableArchitectureTests/ForEachReducerTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ struct Elements: ReducerProtocol {
8585
}
8686
#if swift(>=5.7)
8787
var body: some ReducerProtocol<State, Action> {
88-
Reduce<State, Action> { state, action in
88+
Reduce { state, action in
8989
.none
9090
}
9191
.forEach(\.rows, action: /Action.row) {
@@ -99,7 +99,7 @@ struct Elements: ReducerProtocol {
9999
}
100100
#else
101101
var body: Reduce<State, Action> {
102-
Reduce<State, Action> { state, action in
102+
Reduce { state, action in
103103
.none
104104
}
105105
.forEach(\.rows, action: /Action.row) {

0 commit comments

Comments
 (0)