Skip to content

Commit f5cc3fe

Browse files
committed
Fix value type State restoration, sync the box not the value (#101?)
This led to bindings pointing to stale state in some cases. Hopefully fixes #101 but I haven't reproduced the issue locally so not guaranteed.
1 parent f55186c commit f5cc3fe

File tree

1 file changed

+20
-11
lines changed

1 file changed

+20
-11
lines changed

Sources/SwiftCrossUI/State/State.swift

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@ import Foundation
33
@propertyWrapper
44
public struct State<Value>: DynamicProperty, StateProperty {
55
class Storage {
6-
var value: Value
6+
// This inner box is what stays constant between view updates. The
7+
// outer box (Storage) is used so that we can assign this box to
8+
// future state instances from the non-mutating
9+
// `update(with:previousValue:)` method. It's vital that the inner
10+
// box remains the same so that bindings can be stored across view
11+
// updates.
12+
var box: Box<Value>
713
var didChange = Publisher()
814

915
init(_ value: Value) {
10-
self.value = value
16+
self.box = Box(value: value)
1117
}
1218
}
1319

@@ -19,21 +25,24 @@ public struct State<Value>: DynamicProperty, StateProperty {
1925

2026
public var wrappedValue: Value {
2127
get {
22-
storage.value
28+
storage.box.value
2329
}
2430
nonmutating set {
25-
storage.value = newValue
31+
storage.box.value = newValue
2632
didChange.send()
2733
}
2834
}
2935

3036
public var projectedValue: Binding<Value> {
31-
Binding(
37+
// Specifically link the binding to the inner box instead of the outer
38+
// storage which changes with each view update.
39+
let box = storage.box
40+
return Binding(
3241
get: {
33-
storage.value
42+
box.value
3443
},
3544
set: { newValue in
36-
storage.value = newValue
45+
box.value = newValue
3746
didChange.send()
3847
}
3948
)
@@ -49,8 +58,8 @@ public struct State<Value>: DynamicProperty, StateProperty {
4958

5059
public func update(with environment: EnvironmentValues, previousValue: State<Value>?) {
5160
if let previousValue {
52-
storage.value = previousValue.storage.value
53-
storage.didChange = previousValue.didChange
61+
storage.box = previousValue.storage.box
62+
storage.didChange = previousValue.storage.didChange
5463
}
5564
}
5665

@@ -62,11 +71,11 @@ public struct State<Value>: DynamicProperty, StateProperty {
6271
return
6372
}
6473

65-
storage.value = state as! Value
74+
storage.box.value = state as! Value
6675
}
6776

6877
func snapshot() throws -> Data? {
69-
if let value = storage.value as? Codable {
78+
if let value = storage.box as? Codable {
7079
return try JSONEncoder().encode(value)
7180
} else {
7281
return nil

0 commit comments

Comments
 (0)