|
25 | 25 | > Note: We conform `State` and `Action` to the `Equatable` protocol in order to test this |
26 | 26 | > feature later. |
27 | 27 |
|
28 | | - @Code(name: "ContactsFeature.swift", file: "02-01-01-code-0000") |
| 28 | + @Code(name: "ContactsFeature.swift", file: 02-01-01-code-0000.swift) |
29 | 29 | } |
30 | 30 |
|
31 | 31 | @Step { |
32 | 32 | Add a view that holds onto a ``ComposableArchitecture/Store`` of the `ContactsFeature` |
33 | 33 | and observes the store in order to show a list of contacts and send actions. |
34 | 34 |
|
35 | | - @Code(name: "ContactsFeature.swift", file: "02-01-01-code-0001", reset: true) |
| 35 | + @Code(name: "ContactsFeature.swift", file: 02-01-01-code-0001.swift, reset: true) |
36 | 36 | } |
37 | 37 |
|
38 | 38 | @Step { |
39 | 39 | Add a preview with a few stubbed contacts already in the state so that we can see what |
40 | 40 | the feature looks like. |
41 | 41 |
|
42 | | - @Code(name: "ContactsFeature.swift", file: "02-01-01-code-0002", reset: true) { |
| 42 | + @Code(name: "ContactsFeature.swift", file: 02-01-01-code-0002.swift, reset: true) { |
43 | 43 | @Image(source: "ch02-sub01-sec01-image-0001") |
44 | 44 | } |
45 | 45 | } |
|
50 | 50 | button for dismissing, and a "Save" button that when tapped should dismiss the feature |
51 | 51 | _and_ add the contact to the list of contacts in the parent. |
52 | 52 |
|
53 | | - @Code(name: "AddContactFeature.swift", file: "02-01-01-code-0003", reset: true) |
| 53 | + @Code(name: "AddContactFeature.swift", file: 02-01-01-code-0003.swift, reset: true) |
54 | 54 | } |
55 | 55 |
|
56 | 56 | @Step { |
57 | 57 | Add a view that holds onto a ``ComposableArchitecture/Store`` of the `AddContactFeature` |
58 | 58 | and observes the state in order to show a text field for the contact name and send actions. |
59 | 59 |
|
60 | | - @Code(name: "AddContactFeature.swift", file: "02-01-01-code-0004", reset: true) |
| 60 | + @Code(name: "AddContactFeature.swift", file: 02-01-01-code-0004.swift, reset: true) |
61 | 61 | } |
62 | 62 |
|
63 | 63 | @Step { |
64 | 64 | Add a preview so that we can see what the feature looks like. |
65 | 65 |
|
66 | | - @Code(name: "AddContactFeature.swift", file: "02-01-01-code-0005", reset: true) { |
| 66 | + @Code(name: "AddContactFeature.swift", file: 02-01-01-code-0005.swift, reset: true) { |
67 | 67 | @Image(source: "ch02-sub01-sec01-image-0002") |
68 | 68 | } |
69 | 69 | } |
|
87 | 87 | few steps. Go back to the ContactsFeature.swift file where we built the `ContactsFeature` |
88 | 88 | reducer for handling the logic and behavior of the list of contacts. |
89 | 89 |
|
90 | | - @Code(name: "ContactsFeature.swift", file: "02-01-02-code-0000") |
| 90 | + @Code(name: "ContactsFeature.swift", file: 02-01-02-code-0000.swift) |
91 | 91 | } |
92 | 92 |
|
93 | 93 | @Step { |
|
98 | 98 | A `nil` value represents that the "Add Contacts" feature is not presented, and a non-`nil` |
99 | 99 | value represents that it is presented. |
100 | 100 |
|
101 | | - @Code(name: "ContactsFeature.swift", file: "02-01-02-code-0001") |
| 101 | + @Code(name: "ContactsFeature.swift", file: 02-01-02-code-0001.swift) |
102 | 102 | } |
103 | 103 |
|
104 | 104 | @Step { |
|
107 | 107 |
|
108 | 108 | This allows the parent to observe every action sent from the child feature. |
109 | 109 |
|
110 | | - @Code(name: "ContactsFeature.swift", file: "02-01-02-code-0002") |
| 110 | + @Code(name: "ContactsFeature.swift", file: 02-01-02-code-0002.swift) |
111 | 111 | } |
112 | 112 |
|
113 | 113 | @Step { |
114 | 114 | Since a new case has been added to the action enum we must now handle it in the main |
115 | 115 | reducer. For now we will do nothing for this case and return `.none`, but soon we will do |
116 | 116 | more here. |
117 | 117 |
|
118 | | - @Code(name: "ContactsFeature.swift", file: "02-01-02-code-0003") |
| 118 | + @Code(name: "ContactsFeature.swift", file: 02-01-02-code-0003.swift) |
119 | 119 | } |
120 | 120 |
|
121 | 121 | @Step { |
|
128 | 128 | automatically handles effect cancellation when the child feature is dismissed, and a lot |
129 | 129 | more. See the documentation for more information. |
130 | 130 |
|
131 | | - @Code(name: "ContactsFeature.swift", file: "02-01-02-code-0004") |
| 131 | + @Code(name: "ContactsFeature.swift", file: 02-01-02-code-0004.swift) |
132 | 132 | } |
133 | 133 |
|
134 | 134 | That is all it takes to integrate the two features' domains together. Before moving onto the |
|
140 | 140 | When the "+" button is tapped in the contacts list feature we can now populate the |
141 | 141 | `addContact` state to represent that the feature should be presented. |
142 | 142 |
|
143 | | - @Code(name: "ContactsFeature.swift", file: "02-01-02-code-0005") |
| 143 | + @Code(name: "ContactsFeature.swift", file: 02-01-02-code-0005.swift) |
144 | 144 | } |
145 | 145 |
|
146 | 146 | @Step { |
|
152 | 152 | > ``ComposableArchitecture/PresentationAction/presented(_:)`` case in order to listen for |
153 | 153 | > actions inside the "Add Contact" feature. |
154 | 154 |
|
155 | | - @Code(name: "ContactsFeature.swift", file: "02-01-02-code-0006") |
| 155 | + @Code(name: "ContactsFeature.swift", file: 02-01-02-code-0006.swift) |
156 | 156 | } |
157 | 157 |
|
158 | 158 | @Step { |
159 | 159 | When the "Save" button is tapped _inside_ the "Add Contacts" feature we want to not only |
160 | 160 | dismiss the feature, but we also want to add the new contact to the collection of contacts |
161 | 161 | held in `ContactsFeature.State`. |
162 | 162 |
|
163 | | - @Code(name: "ContactsFeature.swift", file: "02-01-02-code-0007") |
| 163 | + @Code(name: "ContactsFeature.swift", file: 02-01-02-code-0007.swift) |
164 | 164 | } |
165 | 165 |
|
166 | 166 | That is all it takes to implement communication between parent and child features. The parent |
|
182 | 182 | and we have a navigation title and toolbar. We need to figure out how to present a sheet |
183 | 183 | in this view whenever the `addContact` state flips to non-`nil`. |
184 | 184 |
|
185 | | - @Code(name: "ContactsFeature.swift", file: "02-01-02-code-0008", reset: true) |
| 185 | + @Code(name: "ContactsFeature.swift", file: 02-01-02-code-0008.swift, reset: true) |
186 | 186 | } |
187 | 187 |
|
188 | 188 | The library comes with a variety of tools that mimic SwiftUI's native navigation tools (such |
|
195 | 195 | store will be derived focused only on the `AddContactFeature` domain, which is what you can |
196 | 196 | pass to the `AddContactView`. |
197 | 197 |
|
198 | | - @Code(name: "ContactsFeature.swift", file: "02-01-02-code-0009") |
| 198 | + @Code(name: "ContactsFeature.swift", file: 02-01-02-code-0009.swift) |
199 | 199 | } |
200 | 200 |
|
201 | 201 | @Step { |
|
225 | 225 | `AddContactFeature`. This enum will describe all the actions that the parent can listen for |
226 | 226 | and interpret. It allows the child feature to directly tell the parent what it wants done. |
227 | 227 |
|
228 | | - @Code(name: "AddContactFeature.swift", file: 02-01-04-code-0000, previousFile: 02-01-04-code-0000-previous) |
| 228 | + @Code(name: "AddContactFeature.swift", file: 02-01-04-code-0000.swift, previousFile: 02-01-04-code-0000-previous.swift) |
229 | 229 | } |
230 | 230 |
|
231 | 231 | @Step { |
232 | 232 | Handle the new case in the reducer, but we should never actually perform any logic in this |
233 | 233 | case. Only the parent should listen for `delegate` actions and respond accordingly. |
234 | 234 |
|
235 | | - @Code(name: "AddContactFeature.swift", file: 02-01-04-code-0001) |
| 235 | + @Code(name: "AddContactFeature.swift", file: 02-01-04-code-0001.swift) |
236 | 236 | } |
237 | 237 |
|
238 | 238 | @Step { |
239 | 239 | Anytime we want the child feature to communicate to the parent we will return an effect that |
240 | 240 | immediately and synchronously sends a delegate action. For example, when the "Save" button |
241 | 241 | is tapped, we will send the `saveContact` action. |
242 | 242 |
|
243 | | - @Code(name: "AddContactFeature.swift", file: 02-01-04-code-0002) |
| 243 | + @Code(name: "AddContactFeature.swift", file: 02-01-04-code-0002.swift) |
244 | 244 | } |
245 | 245 |
|
246 | 246 | @Step { |
247 | 247 | Go back to ContactsFeature.swift and update the reducer to listen for delegate actions to |
248 | 248 | figure out when it is time to dismiss or save the contact. |
249 | 249 |
|
250 | | - @Code(name: "ContactsFeature.swift", file: 02-01-04-code-0003, previousFile: 02-01-04-code-0003-previous) |
| 250 | + @Code(name: "ContactsFeature.swift", file: 02-01-04-code-0003.swift, previousFile: 02-01-04-code-0003-previous.swift) |
251 | 251 | } |
252 | 252 |
|
253 | 253 | The application should work exactly as it did before the "delegate action" refactor, but now |
|
262 | 262 | ``ComposableArchitecture/DismissEffect``. This is a value that allows child features to |
263 | 263 | dismiss themselves without any direct contact with the parent feature. |
264 | 264 |
|
265 | | - @Code(name: "AddContactFeature.swift", file: 02-01-04-code-0004, previousFile: 02-01-04-code-0004-previous) |
| 265 | + @Code(name: "AddContactFeature.swift", file: 02-01-04-code-0004.swift, previousFile: 02-01-04-code-0004-previous.swift) |
266 | 266 | } |
267 | 267 |
|
268 | 268 | @Step { |
|
273 | 273 | > Note: The `dismiss` dependency is asynchronous which means it is only appropriate to |
274 | 274 | > invoke from an effect. |
275 | 275 |
|
276 | | - @Code(name: "AddContactFeature.swift", file: 02-01-04-code-0005) |
| 276 | + @Code(name: "AddContactFeature.swift", file: 02-01-04-code-0005.swift) |
277 | 277 | } |
278 | 278 |
|
279 | 279 | @Step { |
280 | 280 | Remove the `cancel` action from the `Delegate` enum because it is no longer needed. We do |
281 | 281 | not need to explicitly communicate to the parent that it should dismiss the child. That is |
282 | 282 | all handled by the ``ComposableArchitecture/DismissEffect``. |
283 | 283 |
|
284 | | - @Code(name: "AddContactFeature.swift", file: 02-01-04-code-0006) |
| 284 | + @Code(name: "AddContactFeature.swift", file: 02-01-04-code-0006.swift) |
285 | 285 | } |
286 | 286 |
|
287 | 287 | @Step { |
288 | 288 | Go back to ContactsFeature.swift. We can also remove the `cancel` logic from the |
289 | 289 | `ContactsFeature` reducer and it is no longer necessary to explicitly `nil` out the |
290 | 290 | `addContact` state. That is already taken care of. |
291 | 291 |
|
292 | | - @Code(name: "ContactsFeature.swift", file: 02-01-04-code-0007, previousFile: 02-01-04-code-0007-previous) |
| 292 | + @Code(name: "ContactsFeature.swift", file: 02-01-04-code-0007.swift, previousFile: 02-01-04-code-0007-previous.swift) |
293 | 293 | } |
294 | 294 | } |
295 | 295 | } |
|
0 commit comments