Skip to content

Commit 6d1bdd3

Browse files
author
Mohammed Rokon Uddin
authored
Merge pull request #19 from Arman-Morshed/feature/observation
feat: introduce observation
2 parents 5c54c9e + e8f6a47 commit 6d1bdd3

File tree

12 files changed

+280
-294
lines changed

12 files changed

+280
-294
lines changed

{{cookiecutter.app_name}}/Common/Package.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ let package = Package(
1414
dependencies: [
1515
.package(
1616
url: "https://github.com/pointfreeco/swift-composable-architecture",
17-
exact: "1.5.1"
17+
exact: "1.8.0"
1818
)
1919
],
2020
targets: [

{{cookiecutter.app_name}}/Common/Sources/Common/FeatureReducer.swift

+6-34
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ import SwiftUI
1212
// MARK: FeatureReducer
1313
public protocol FeatureReducer: Reducer
1414
where State: Sendable & Hashable, Action == FeatureAction<Self> {
15-
associatedtype ViewAction: Sendable & Equatable = Never
16-
associatedtype InternalAction: Sendable & Equatable = Never
17-
associatedtype ChildAction: Sendable & Equatable = Never
18-
associatedtype DelegateAction: Sendable & Equatable = Never
15+
associatedtype ViewAction: Sendable = Never
16+
associatedtype InternalAction: Sendable = Never
17+
associatedtype ChildAction: Sendable = Never
18+
associatedtype DelegateAction: Sendable = Never
1919

2020
func reduce(into state: inout State, viewAction: ViewAction) -> Effect<Action>
2121
func reduce(into state: inout State, internalAction: InternalAction)
@@ -93,7 +93,7 @@ public typealias PresentationStoreOf<R: Reducer> = Store<
9393

9494
// MARK: FeatureAction
9595
@CasePathable
96-
public enum FeatureAction<Feature: FeatureReducer>: Sendable, Equatable {
96+
public enum FeatureAction<Feature: FeatureReducer>: Sendable {
9797
case destination(PresentationAction<Feature.Destination.Action>)
9898
case view(Feature.ViewAction)
9999
case `internal`(Feature.InternalAction)
@@ -103,7 +103,7 @@ public enum FeatureAction<Feature: FeatureReducer>: Sendable, Equatable {
103103

104104
// MARK: DestinationReducer
105105
public protocol DestinationReducer: Reducer
106-
where State: Sendable & Hashable, Action: Sendable & Equatable & CasePathable {}
106+
where State: Sendable & Hashable, Action: Sendable & CasePathable {}
107107

108108
// MARK: EmptyDestination
109109

@@ -116,31 +116,3 @@ public enum EmptyDestination: DestinationReducer {
116116
Action
117117
> { .none }
118118
}
119-
120-
//MARK: FeatureAction + Hashable
121-
extension FeatureAction: Hashable
122-
where
123-
Feature.Destination.Action: Hashable,
124-
Feature.ViewAction: Hashable,
125-
Feature.ChildAction: Hashable,
126-
Feature.InternalAction: Hashable,
127-
Feature.DelegateAction: Hashable
128-
{
129-
public func hash(into hasher: inout Hasher) {
130-
switch self {
131-
case let .destination(action):
132-
hasher.combine(action)
133-
case let .view(action):
134-
hasher.combine(action)
135-
case let .internal(action):
136-
hasher.combine(action)
137-
case let .child(action):
138-
hasher.combine(action)
139-
case let .delegate(action):
140-
hasher.combine(action)
141-
}
142-
}
143-
}
144-
145-
/// For scoping to an actionless childstore
146-
public func actionless<T>(never: Never) -> T {}

{{cookiecutter.app_name}}/Features/Package.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ let package = Package(
2323
.package(path: "../PersistentPlatform"),
2424
.package(
2525
url: "https://github.com/pointfreeco/swift-composable-architecture",
26-
exact: "1.5.1"
26+
exact: "1.8.0"
2727
),
2828
],
2929
targets: [

{{cookiecutter.app_name}}/Features/Sources/App/AppFeature.swift

+103-101
Original file line numberDiff line numberDiff line change
@@ -13,123 +13,125 @@ import Domain
1313

1414
public struct AppFeature: FeatureReducer {
1515

16-
@Dependency(\.appClient) var appClient
16+
@Dependency(\.appClient) var appClient
1717

18-
public init() {}
19-
20-
public struct State: Equatable, Hashable {
2118
public init() {}
2219

23-
@PresentationState var destination: Destination.State?
24-
var product: Product?
25-
}
26-
27-
public enum ViewAction: Equatable {
28-
case onAppear
29-
case showSheet
30-
case showFullScreenCover
31-
case save
32-
}
33-
34-
public enum InternalAction: Equatable {
35-
case dismissDestination
36-
case productResponse(TaskResult<Product?>)
37-
}
38-
39-
public var body: some ReducerOf<Self> {
40-
Reduce(core)
41-
.ifLet(\.$destination, action: \.destination) {
42-
Destination()
43-
}
44-
}
45-
46-
public func reduce(into state: inout State, viewAction: ViewAction) -> Effect<
47-
Action
48-
> {
49-
switch viewAction {
50-
case .onAppear:
51-
return .run { send in
52-
await appClient.prepare(Void())
53-
await send(
54-
.internal(
55-
.productResponse(
56-
TaskResult {
57-
try await appClient.product(1)
58-
})))
59-
}
60-
case .showSheet:
61-
state.destination = .sheet(.init())
62-
return .none
63-
64-
case .showFullScreenCover:
65-
state.destination = .fullScreenCover(.init())
66-
return .none
67-
68-
case .save:
69-
return .run { [product = state.product] send in
70-
do {
71-
try await appClient.save(product!)
72-
} catch {
20+
@ObservableState
21+
public struct State: Equatable, Hashable {
22+
public init() {}
7323

74-
}
75-
}
24+
@Presents var destination: Destination.State?
25+
var product: Product?
7626
}
77-
}
7827

79-
public func reduce(
80-
into state: inout State, presentedAction: Destination.Action
81-
) -> Effect<Action> {
82-
switch presentedAction {
83-
case .sheet(.delegate(.close)):
84-
return .send(.internal(.dismissDestination))
28+
public enum ViewAction {
29+
case onAppear
30+
case showSheet
31+
case showFullScreenCover
32+
case save
33+
}
8534

86-
case .fullScreenCover(.delegate(.close)):
87-
return .send(.internal(.dismissDestination))
35+
public enum InternalAction {
36+
case dismissDestination
37+
case productResponse(Result<Product?, Error>)
38+
}
8839

89-
default:
90-
return .none
40+
public var body: some ReducerOf<Self> {
41+
Reduce(core)
42+
.ifLet(\.$destination, action: \.destination) {
43+
Destination()
44+
}
9145
}
92-
}
93-
94-
public func reduce(into state: inout State, internalAction: InternalAction)
95-
-> Effect<Action>
96-
{
97-
switch internalAction {
98-
case let .productResponse(.success(product)):
99-
state.product = product
100-
return .none
101-
case let .productResponse(.failure(error)):
102-
print(error)
103-
return .none
104-
case .dismissDestination:
105-
state.destination = nil
106-
return .none
46+
47+
public func reduce(into state: inout State, viewAction: ViewAction) -> Effect<
48+
Action
49+
> {
50+
switch viewAction {
51+
case .onAppear:
52+
return .run { send in
53+
await appClient.prepare(Void())
54+
await send(
55+
.internal(
56+
.productResponse(
57+
Result {
58+
try await appClient.product(1)
59+
})))
60+
}
61+
case .showSheet:
62+
state.destination = .sheet(.init())
63+
return .none
64+
65+
case .showFullScreenCover:
66+
state.destination = .fullScreenCover(.init())
67+
return .none
68+
69+
case .save:
70+
return .run { [product = state.product] send in
71+
do {
72+
try await appClient.save(product!)
73+
} catch {
74+
75+
}
76+
}
77+
}
10778
}
108-
}
10979

110-
public struct Destination: DestinationReducer {
80+
public func reduce(
81+
into state: inout State, presentedAction: Destination.Action
82+
) -> Effect<Action> {
83+
switch presentedAction {
84+
case .sheet(.delegate(.close)):
85+
return .send(.internal(.dismissDestination))
11186

112-
public init() {}
87+
case .fullScreenCover(.delegate(.close)):
88+
return .send(.internal(.dismissDestination))
11389

114-
@CasePathable
115-
public enum State: Hashable {
116-
case sheet(Counter.State)
117-
case fullScreenCover(Counter.State)
90+
default:
91+
return .none
92+
}
11893
}
11994

120-
@CasePathable
121-
public enum Action: Equatable {
122-
case sheet(Counter.Action)
123-
case fullScreenCover(Counter.Action)
95+
public func reduce(into state: inout State, internalAction: InternalAction)
96+
-> Effect<Action>
97+
{
98+
switch internalAction {
99+
case let .productResponse(.success(product)):
100+
state.product = product
101+
return .none
102+
case let .productResponse(.failure(error)):
103+
print(error)
104+
return .none
105+
case .dismissDestination:
106+
state.destination = nil
107+
return .none
108+
}
124109
}
125110

126-
public var body: some ReducerOf<Self> {
127-
Scope(state: \.sheet, action: \.sheet) {
128-
Counter()
129-
}
130-
Scope(state: \.fullScreenCover, action: \.fullScreenCover) {
131-
Counter()
132-
}
111+
public struct Destination: DestinationReducer {
112+
113+
public init() {}
114+
115+
@dynamicMemberLookup
116+
@CasePathable
117+
public enum State: Hashable {
118+
case sheet(Counter.State)
119+
case fullScreenCover(Counter.State)
120+
}
121+
122+
@CasePathable
123+
public enum Action {
124+
case sheet(Counter.Action)
125+
case fullScreenCover(Counter.Action)
126+
}
127+
128+
public var body: some ReducerOf<Self> {
129+
Scope(state: \.sheet, action: \.sheet) {
130+
Counter()
131+
}
132+
Scope(state: \.fullScreenCover, action: \.fullScreenCover) {
133+
Counter()
134+
}
135+
}
133136
}
134-
}
135137
}

0 commit comments

Comments
 (0)