You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Access ViewStore state dynamically via closure (#36)
* Access ViewStore state dynamically via closure
This makes ViewStore's behavior more like that of Bindings.
This is in reponse to an unusual bug we experienced while working on
Subconscious.
Was experiencing a mysterious crasher when factoring NavigationStack
into a subview. What I discovered was that, at some point,
NavigationStack seems to be attempting to mutate the array that
manages that stack. I believe the fact that we pass the stack down
as a value type may be causing the problem. It's obscured because
the issue happens in Apple's proprietary SwiftUI code. The crash
had to do with an array manipulation, and seemed likely to be related
to the array that manages the view stack, or perhaps it with some
sort of race condition in view rendering on the SwiftUI side.
The workaround was giving NavigationStack a @State binding and
replaying changes onto it.
However, this made me wonder if the cause was the fact that we passed
the stack by value down the the view, before creating a Binding that
referenced it.
After stubbing in a ViewStore with a Binding-like closure get function,
the issue was resolved. So it seems that there are corner cases where
SwiftUI needs to dynamically read the value via closure, not have it
passed down by value.
* Deprecated CursorProtocol and KeyedCursorProtocol
Bring back these protocols for backwards-compat, but mark them
deprecated.
/// CursorProtocol.update offers a convenient way to call child
425
+
/// update functions from the parent domain, and get parent-domain
426
+
/// states and actions back from it.
427
+
///
428
+
/// - `state` the outer state
429
+
/// - `action` the inner action
430
+
/// - `environment` the environment for the update function
431
+
/// - Returns a new outer state
432
+
@available(
433
+
*,
434
+
deprecated,
435
+
message:"CursorProtocol is depreacated and will be removed in a future update. Use ModelProtocol.update(get:set:tag:state:action:environment:) instead."
436
+
)
437
+
publicstaticfunc update(
438
+
state:Model,
439
+
action viewAction:ViewModel.Action,
440
+
environment:ViewModel.Environment
441
+
)->Update<Model>{
442
+
letnext=ViewModel.update(
443
+
state:get(state: state),
444
+
action: viewAction,
445
+
environment: environment
446
+
)
447
+
returnUpdate(
448
+
state:set(state: state, inner: next.state),
449
+
fx: next.fx.map(tag).eraseToAnyPublisher(),
450
+
transaction: next.transaction
451
+
)
452
+
}
453
+
}
454
+
455
+
publicprotocolKeyedCursorProtocol{
456
+
associatedtypeKey
457
+
associatedtypeModel:ModelProtocol
458
+
associatedtypeViewModel:ModelProtocol
459
+
460
+
/// Get an inner state from an outer state
461
+
staticfunc get(state:Model, key:Key)->ViewModel?
462
+
463
+
/// Set an inner state on an outer state, returning an outer state
/// Update an inner state within an outer state through a keyed cursor.
472
+
/// This cursor type is useful when looking up children in dynamic lists
473
+
/// such as arrays or dictionaries.
474
+
///
475
+
/// - `state` the outer state
476
+
/// - `action` the inner action
477
+
/// - `environment` the environment for the update function
478
+
/// - `key` a key uniquely representing this model in the parent domain
479
+
/// - Returns an update for a new outer state or nil
480
+
@available(
481
+
*,
482
+
deprecated,
483
+
message:"KeyedCursorProtocol is depreacated and will be removed in a future update. Use ModelProtocol.update(get:set:tag:state:action:environment:) instead."
0 commit comments