@Observable nested class mutation does not redraw the view, @Published worked fine swiftui-state
Migrated a screen from ObservableObject to the @Observable macro. Most of it works. One case does not and I cannot see the rule I am breaking.
@Observable final class AppStore {
var profile: Profile
var isLoading = false
}
final class Profile {
var displayName: String
var avatarURL: URL?
}
View reads store.profile.displayName. Setting store.isLoading = true redraws fine. Setting store.profile.displayName = "new" changes the value - I can print it and it is correct - but the view never updates.
With ObservableObject and @Published this worked, because I called objectWillChange.send() manually from the profile setter. There is no obvious equivalent now.
What is the actual rule for how far the tracking reaches?
@kerf_wander · 2w ago · 3 replies
The rule: the macro instruments the properties of the class it is applied to. Nothing else. It does not reach through a reference to another object.
Your view reads
store.profile(a tracked property - reading it registers a dependency on the reference) and then.displayNameon a plain class, which is not instrumented at all. MutatingdisplayNamechanges memory that nothing is watching. Theprofilereference itself never changed, so no invalidation fires.Three fixes, in order of how much I like them:
Profilea struct. Then mutatingdisplayNameis a mutation ofstore.profileitself, the tracked property changes, everything works. This is the right answer surprisingly often - most of these nested types are value-shaped and were only classes out of habit from the reference-type era.Profileas@Observabletoo. Tracking then continues through it and the view's read ofdisplayNameregisters properly. Correct when the object genuinely has identity and is shared.The part that makes this confusing coming from ObservableObject is that the old system was coarse - one publisher for the whole object, and you fired it manually so you controlled the granularity. The new one is fine-grained and automatic, which means it is precise about which property you read, and equally precise about not tracking things you never told it about.
Reply
Report
@secondshooter_v · 2w ago
Struct. It has four fields and no identity, it was a class purely because I wrote it that way in 2021 and never revisited. Fixed in about a minute once I understood the rule.
Reply
Report
@petra_lindqvist · last wk.
Worth noting the failure mode differs between the two systems in a way that matters. The old one over-invalidated - you got redraws you did not need, so the bug was performance. The new one under-invalidates if you get it wrong, so the bug is stale UI. Stale UI is much harder to notice in testing and much worse when a user finds it.
Reply
Report