"The mic button does nothing until I type something into the composer. And it isn't even every time." — Dad, on a TestFlight build
Two Generations of "The View Should Update"
ObservableObject (Combine) is the older model. A class publishes changes through @Published properties and objectWillChange; a view subscribes to the object it holds with @ObservedObject or @StateObject, and any published change invalidates that whole view.
@Observable (the Observation framework, macOS 14 / iOS 17 / watchOS 10) is the newer one. The macro makes a class track which properties a view's body actually reads, and only changes to those properties re-render it. You hold the model as a plain property or in @State, and use @Bindable when you need bindings such as $model.draft.
The Bug That Looked Like Broken Hardware
The family's first native dictation shipped with a microphone button that seemed to do nothing. Sometimes it came alive after the user typed a character; sometimes it seemed to switch on by itself. It behaved exactly like flaky hardware. The microphone was recording the whole time.
The app model was an ObservableObject, and it held the dictation object — itself an ObservableObject — as a plain property. SwiftUI subscribes only to the object a view holds. The dictation object's @Published changes reached no view at all. Typing changed a @Published property on the model, the screen redrew, and the listening state appeared along with it. The fixes available under ObservableObject are to relay the child's objectWillChange into the parent's, or to hold the child as its own @ObservedObject in the view. Under Observation the trap does not exist: reading model.dictation.isListening in a body tracks that property through the nested object. The family's shared speech component is @Observable for exactly that reason.
The general lesson from Dad's report: a symptom that looks like broken hardware and correlates with unrelated UI activity is a redraw problem until proven otherwise.
The House Rule, and One Compiler Footnote
Every family deployment target already supports Observation, so the rule is @Observable for new models, with existing ObservableObject models migrated as they are touched. One footnote from the build Mac: Swift 6.3.3 crashed in code generation on two occasions when a SwiftUI Binding setter was written as a bound method reference, set: model.selectProvider. Writing the explicit closure set: { model.selectProvider($0) } compiled. The minimal trigger was never isolated, so treat it as a workaround to reach for when you see that crash, not a ban on method references.