Skip to content

Commit 927208a

Browse files
authored
Merge pull request #46 from Ryu0118/reducer-macro
[WIP] Add Reducer macro
2 parents be62d4f + ef358d9 commit 927208a

34 files changed

+1192
-511
lines changed

.DS_Store

-6 KB
Binary file not shown.

Examples/Github-App/Github-App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Examples/Github-App/Github-App/View/RepositoryView.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import SwiftUI
22
import SimplexArchitecture
33

4-
struct RepositoryReducer: ReducerProtocol {
5-
enum Action {
4+
@Reducer
5+
struct RepositoryReducer {
6+
enum ViewAction {
67
case onOpenURLButtonTapped
78
}
89

Examples/Github-App/Github-App/View/RootView.swift

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import SwiftUI
22
import SimplexArchitecture
33

4-
struct RootReducer: ReducerProtocol {
5-
enum Action: Equatable {
4+
@Reducer
5+
struct RootReducer {
6+
enum ViewAction: Equatable {
67
case onSearchButtonTapped
78
case onTextChanged(String)
89
}
@@ -20,9 +21,19 @@ struct RootReducer: ReducerProtocol {
2021

2122
func reduce(
2223
into state: StateContainer<RootView>,
23-
action: ReducerAction
24-
) -> SideEffect<RootReducer> {
24+
action: Action
25+
) -> SideEffect<Self> {
2526
switch action {
27+
case .onSearchButtonTapped:
28+
state.isLoading = true
29+
return fetchRepositories(query: state.text)
30+
31+
case .onTextChanged(let text):
32+
if text.isEmpty {
33+
state.repositories = []
34+
}
35+
return .none
36+
2637
case let .fetchRepositoriesResponse(.success(repositories)):
2738
state.isLoading = false
2839
state.repositories = repositories
@@ -50,23 +61,6 @@ struct RootReducer: ReducerProtocol {
5061
}
5162
}
5263

53-
func reduce(
54-
into state: StateContainer<RootView>,
55-
action: Action
56-
) -> SideEffect<Self> {
57-
switch action {
58-
case .onSearchButtonTapped:
59-
state.isLoading = true
60-
return fetchRepositories(query: state.text)
61-
62-
case .onTextChanged(let text):
63-
if text.isEmpty {
64-
state.repositories = []
65-
}
66-
return .none
67-
}
68-
}
69-
7064
func fetchRepositories(query: String) -> SideEffect<Self> {
7165
.run { send in
7266
await send(

README.md

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ let package = Package(
3636
## Usage
3737
### Basic Usage
3838
```Swift
39-
struct MyReducer: ReducerProtocol {
40-
enum Action {
39+
@Reducer
40+
struct MyReducer {
41+
enum ViewAction {
4142
case increment
4243
case decrement
4344
}
@@ -80,8 +81,9 @@ ReducerState is also effective to improve performance because the View is not up
8081

8182
This is the example code
8283
```Swift
83-
struct MyReducer: ReducerProtocol {
84-
enum Action {
84+
@Reducer
85+
struct MyReducer {
86+
enum ViewAction {
8587
case increment
8688
case decrement
8789
}
@@ -135,8 +137,9 @@ If there are Actions that you do not want to expose to View, ReducerAction is ef
135137
This is the sample code:
136138

137139
```Swift
138-
struct MyReducer: ReducerProtocol {
139-
enum Action {
140+
@Reducer
141+
struct MyReducer {
142+
enum ViewAction {
140143
case login
141144
}
142145

@@ -156,11 +159,6 @@ struct MyReducer: ReducerProtocol {
156159
)
157160
)
158161
}
159-
}
160-
}
161-
162-
func reduce(into state: StateContainer<MyView>, action: ReducerAction) -> SideEffect<Self> {
163-
switch action {
164162
case let .loginResponse(result):
165163
...
166164
return .none
@@ -206,12 +204,13 @@ struct ParentView: View {
206204
}
207205
}
208206

209-
struct ParentReducer: ReducerProtocol {
210-
enum Action {
207+
@Reducer
208+
struct ParentReducer {
209+
enum ViewAction {
211210
case child(ChildReducer.Action)
212211
}
213212

214-
func reduce(into state: StateContainer<ParentView>, action: Action) -> SideEffect<ParentReducer> {
213+
func reduce(into state: StateContainer<ParentView>, action: Action) -> SideEffect<Self> {
215214
switch action {
216215
case .child(.onButtonTapped):
217216
// do something
@@ -231,12 +230,13 @@ struct ChildView: View, ActionSendable {
231230
}
232231
}
233232

234-
struct ChildReducer: ReducerProtocol {
235-
enum Action {
233+
@Reducer
234+
struct ChildReducer {
235+
enum ViewAction {
236236
case onButtonTapped
237237
}
238238

239-
func reduce(into state: StateContainer<ChildView>, action: Action) -> SideEffect<ChildReducer> {
239+
func reduce(into state: StateContainer<ChildView>, action: Action) -> SideEffect<Self> {
240240
switch action {
241241
case .onButtonTapped:
242242
return .none

Sources/SimplexArchitecture/ActionSendable.swift

Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ public protocol ActionSendable<Reducer> {
1313
public extension ActionSendable {
1414
/// Send an action to the store
1515
@discardableResult
16-
func send(_ action: consuming Reducer.Action) -> SendTask {
16+
func send(_ action: consuming Reducer.ViewAction) -> SendTask {
1717
threadCheck()
1818
return store.send(action, target: self)
1919
}
2020

2121
@discardableResult
22-
func send(_ action: consuming Reducer.Action, animation: Animation?) -> SendTask {
22+
func send(_ action: consuming Reducer.ViewAction, animation: Animation?) -> SendTask {
2323
threadCheck()
2424
return withAnimation(animation) {
2525
store.send(action, target: self)
@@ -28,7 +28,7 @@ public extension ActionSendable {
2828

2929
/// Send an action to the store with transaction
3030
@discardableResult
31-
func send(_ action: consuming Reducer.Action, transaction: Transaction) -> SendTask {
31+
func send(_ action: consuming Reducer.ViewAction, transaction: Transaction) -> SendTask {
3232
threadCheck()
3333
return withTransaction(transaction) {
3434
store.send(action, target: self)
@@ -88,41 +88,4 @@ public extension ActionSendable {
8888
store.pullback(to: casePath, parent: parent, id: id)
8989
return self
9090
}
91-
92-
/// Pullbacks the `Action` to the specified case path in the parent's reducer.
93-
///
94-
/// - Parameters:
95-
/// - casePath: The case path to which the action will be pulled back.
96-
/// - parent: The parent `ActionSendable` to which the action will be sent.
97-
/// - Returns: Self
98-
@_disfavoredOverload
99-
@inlinable
100-
@discardableResult
101-
func pullback<Parent: ActionSendable>(
102-
to casePath: CasePath<Parent.Reducer.Action, Reducer.ReducerAction>,
103-
parent: Parent
104-
) -> Self where Reducer.ReducerState == Never {
105-
store.pullback(to: casePath, parent: parent)
106-
return self
107-
}
108-
109-
/// Pullbacks the `ReducerAction` to the specified case path with an associated identifier in the parent's reducer.
110-
/// This specifies an id to identify which View sent the Action in the ForEach.
111-
///
112-
/// - Parameters:
113-
/// - casePath: The case path with an associated identifier to which the action will be pulled back.
114-
/// - parent: The parent `ActionSendable` to which the action will be sent.
115-
/// - id: The identifier associated with the action.
116-
/// - Returns: Self.
117-
@_disfavoredOverload
118-
@inlinable
119-
@discardableResult
120-
func pullback<Parent: ActionSendable, ID: Hashable>(
121-
to casePath: CasePath<Parent.Reducer.Action, (id: ID, action: Reducer.ReducerAction)>,
122-
parent: Parent,
123-
id: ID
124-
) -> Self where Reducer.ReducerState == Never {
125-
store.pullback(to: casePath, parent: parent, id: id)
126-
return self
127-
}
12891
}

Sources/SimplexArchitecture/Effect/CombineAction.swift

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

Sources/SimplexArchitecture/Internal/ActionTransition.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ struct ActionTransition<Reducer: ReducerProtocol> {
1717
/// The unique effect context that represents root effect.
1818
let effectContext: UUID
1919
/// The Action that cause a change of state
20-
let action: CombineAction<Reducer>
20+
let action: Reducer.Action
2121

2222
/// - Parameters:
2323
/// - previous: The previous state.
@@ -30,7 +30,7 @@ struct ActionTransition<Reducer: ReducerProtocol> {
3030
next: Self.State,
3131
effect: SideEffect<Reducer>,
3232
effectContext: UUID,
33-
for action: CombineAction<Reducer>
33+
for action: Reducer.Action
3434
) {
3535
self.previous = previous
3636
self.next = next

Sources/SimplexArchitecture/Macros.swift

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
/// Text("MyView")
2222
/// }
2323
/// }
24-
///
25-
/// struct MyReducer: ReducerProtocol {
26-
/// enum Action {
24+
/// @Reducer
25+
/// struct MyReducer {
26+
/// enum ViewAction {
2727
/// case someAction
2828
/// }
2929
///
@@ -55,9 +55,9 @@
5555
/// }
5656
/// }
5757
/// }
58-
///
59-
/// struct MyReducer: ReducerProtocol {
60-
/// enum Action {
58+
/// @Reducer
59+
/// struct MyReducer {
60+
/// enum ViewAction {
6161
/// case someAction
6262
/// }
6363
///
@@ -78,3 +78,9 @@
7878
@attached(extension, conformances: ActionSendable)
7979
public macro ViewState() =
8080
#externalMacro(module: "SimplexArchitectureMacrosPlugin", type: "ViewStateMacro")
81+
82+
/// Macro for creating Action from ViewAction and ReducerAction, and conforming Reducer to ReducerProtocol
83+
@attached(member, names: named(Action), named(ReducerAction))
84+
@attached(extension, conformances: ReducerProtocol)
85+
public macro Reducer() =
86+
#externalMacro(module: "SimplexArchitectureMacrosPlugin", type: "ReducerMacro")

Sources/SimplexArchitecture/Reducer/DependenciesOverrideModifier.swift

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,6 @@ public struct _DependenciesOverrideModifier<Base: ReducerProtocol>: ReducerModif
1717
}
1818
}
1919

20-
@inlinable
21-
public func reduce(into state: StateContainer<Base.Target>, action: Base.ReducerAction) -> SideEffect<Base> {
22-
withDependencies(override) {
23-
base.reduce(into: state, action: action)
24-
}
25-
}
26-
2720
@inlinable
2821
public func dependency<Value>(
2922
_ keyPath: WritableKeyPath<DependencyValues, Value>,

0 commit comments

Comments
 (0)