Commit 332458e
authored
Introduce KeyedCursorProtocol, remove ViewStore in favor of forward (#19)
Fixes #18.
This PR sketches out one potential solution to #18. It refactors our approach to sub-components by decomplecting action sending from state getting.
- Removes `ViewStore`
- Introduces `Address.forward(send:tag:)` which gives us an easy way to create tagged `send` functions. This solves one part of what ViewStore was solving.
- Introduces `Binding(get:send:tag:)` which gives us the binding equivalent to `Address.forward`
- Introduces `KeyedCursorProtocol` which offers an alternative cursor for subcomponents that need to be looked up within dynamic lists.
This refactor is in response to the awkwardness of the `ViewStore/Cursor` paradigm for components that are part of a dynamic list. Even if we had created a keyed cursor initializer for ViewStore, it necessarily would have had to hold an optional (nillable) state. This is because ViewStore lookup was dynamic, and this trips up the lifetime typechecking around the model. In practice, a view would not exist if its model did not exist, but this is not a typesafe guarantee for dynamic list lookups.
Anyway, the whole paradigm of looking up child from parent dynamically is a bit odd for list items. In SwiftUI the typical approach is to ForEach, and then pass the model data down as a static property to the view. This guarantees type safety, since a view holds its own copy of the data. What if we could do something more like that?
The approach in this PR leans into this approach. State can be passed to sub-components as plain old properties. `Address.forward` can be used to create view-local send functions that you can pass down to sub-views. `Binding` gets a similar form. In both cases, we can use a closure to capture additional parent-scoped state, such as an ID for lookup within the parent model.
Cursor sticks around, but mostly as a convenient way to create update functions for sub-components. We also introduce `KeyedCursorProtocol` which offers a keyed equivalent for dynamic lookup.
## Usage
Sub-components become more "vanilla", just using bare properties and closures.
```swift
struct ParentView: View {
@StateObject = Store(
AppModel(),
AppEnvironment()
)
var body: some View {
ChildModel(
state: store.state.child,
send: Address.forward(
send: store.send,
tag: ParentChildCursor.tag
)
)
}
}
struct ChildView: View {
var state: ChildModel
var send: (ChildAction) -> Void
var body: some View {
Button(state.text) {
send(.activate)
}
}
}
```
## Prior art
This approach is inspired by Reflex:
- Forward https://github.com/mozilla/reflex/blob/c5e75e98bc601e2315b6d43e5e347263cf67359e/src/signal.js#L5
- Cursor https://github.com/browserhtml/browserhtml/blob/master/src/Common/Cursor.js1 parent e54ae1d commit 332458e
5 files changed
Lines changed: 350 additions & 221 deletions
File tree
- Sources/ObservableStore
- Tests/ObservableStoreTests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
191 | 191 | | |
192 | 192 | | |
193 | 193 | | |
194 | | - | |
| 194 | + | |
195 | 195 | | |
196 | 196 | | |
197 | 197 | | |
198 | 198 | | |
199 | 199 | | |
200 | | - | |
201 | | - | |
| 200 | + | |
| 201 | + | |
202 | 202 | | |
203 | 203 | | |
204 | 204 | | |
205 | 205 | | |
206 | 206 | | |
207 | | - | |
208 | | - | |
209 | | - | |
210 | | - | |
211 | | - | |
212 | | - | |
213 | | - | |
214 | | - | |
215 | | - | |
216 | | - | |
217 | | - | |
218 | | - | |
219 | | - | |
220 | 207 | | |
221 | 208 | | |
222 | 209 | | |
223 | | - | |
| 210 | + | |
224 | 211 | | |
225 | | - | |
| 212 | + | |
226 | 213 | | |
227 | | - | |
| 214 | + | |
228 | 215 | | |
229 | 216 | | |
230 | 217 | | |
| |||
249 | 236 | | |
250 | 237 | | |
251 | 238 | | |
252 | | - | |
| 239 | + | |
| 240 | + | |
253 | 241 | | |
254 | 242 | | |
255 | 243 | | |
256 | | - | |
| 244 | + | |
257 | 245 | | |
258 | 246 | | |
259 | 247 | | |
| |||
265 | 253 | | |
266 | 254 | | |
267 | 255 | | |
268 | | - | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
269 | 282 | | |
270 | 283 | | |
271 | 284 | | |
272 | 285 | | |
273 | 286 | | |
| 287 | + | |
| 288 | + | |
274 | 289 | | |
275 | 290 | | |
276 | 291 | | |
| |||
295 | 310 | | |
296 | 311 | | |
297 | 312 | | |
298 | | - | |
299 | | - | |
300 | | - | |
301 | | - | |
302 | | - | |
303 | | - | |
304 | | - | |
305 | | - | |
306 | | - | |
307 | | - | |
308 | | - | |
309 | | - | |
310 | | - | |
311 | | - | |
312 | | - | |
313 | | - | |
314 | | - | |
315 | | - | |
316 | | - | |
317 | | - | |
318 | | - | |
319 | 313 | | |
320 | 314 | | |
321 | 315 | | |
| |||
342 | 336 | | |
343 | 337 | | |
344 | 338 | | |
345 | | - | |
346 | | - | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
258 | 258 | | |
259 | 259 | | |
260 | 260 | | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
261 | 273 | | |
262 | 274 | | |
263 | 275 | | |
| |||
302 | 314 | | |
303 | 315 | | |
304 | 316 | | |
305 | | - | |
306 | | - | |
307 | | - | |
308 | | - | |
309 | | - | |
310 | | - | |
311 | | - | |
312 | | - | |
313 | | - | |
314 | | - | |
315 | | - | |
316 | | - | |
317 | | - | |
318 | | - | |
319 | | - | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
320 | 321 | | |
321 | | - | |
322 | | - | |
323 | | - | |
324 | | - | |
325 | | - | |
326 | | - | |
327 | | - | |
328 | | - | |
| 322 | + | |
| 323 | + | |
329 | 324 | | |
330 | | - | |
331 | | - | |
| 325 | + | |
| 326 | + | |
332 | 327 | | |
333 | | - | |
334 | | - | |
335 | | - | |
336 | | - | |
| 328 | + | |
| 329 | + | |
337 | 330 | | |
338 | 331 | | |
339 | | - | |
340 | | - | |
341 | | - | |
342 | | - | |
343 | | - | |
344 | | - | |
345 | | - | |
346 | | - | |
347 | | - | |
348 | | - | |
349 | | - | |
350 | | - | |
351 | | - | |
352 | | - | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
353 | 362 | | |
354 | 363 | | |
355 | | - | |
356 | 364 | | |
357 | | - | |
358 | | - | |
359 | | - | |
360 | | - | |
361 | | - | |
362 | | - | |
363 | | - | |
364 | | - | |
365 | | - | |
366 | | - | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
367 | 393 | | |
368 | 394 | | |
369 | 395 | | |
370 | 396 | | |
371 | 397 | | |
372 | | - | |
373 | | - | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
374 | 401 | | |
375 | | - | |
376 | | - | |
377 | | - | |
378 | | - | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
379 | 406 | | |
380 | 407 | | |
381 | | - | |
382 | | - | |
| 408 | + | |
| 409 | + | |
383 | 410 | | |
384 | 411 | | |
385 | 412 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
0 commit comments