SwiftUI
3 interview questions on swiftui.
When should a child view take a `Binding` rather than a plain value?
JuniorTap to think, then reveal
When should a child view take a `Binding` rather than a plain value?
JuniorTap to think, then reveal
When the child needs to write. A plain value is a one-way snapshot — the child can display it and nothing more. A Binding is a getter and setter pointing back at the owner's storage, so writes propagate.
The way I decide is by asking who owns the state. It should live with the view that's responsible for it — usually the highest view that cares — as @State. Anything below that either reads it, in which case a let is better because it documents that the child can't change anything, or writes it, in which case @Binding and pass $.
Getting this wrong produces a very characteristic bug: a toggle or text field that appears to work, animates correctly, and has no effect. There's no error, because passing a value where you meant a binding compiles fine — the child just ends up mutating its own copy.
The other reason to prefer let where you can is that it keeps the data flow readable. If every child takes a binding, you can't tell from a signature which ones actually mutate.
What an interviewer listens for
- Ties the choice to whether the child writes
- Places ownership at the highest view that cares
- Prefers let for read-only children, and says why
- Names the silent no-effect bug that results from getting it wrong
Follow-ups
How do you give a preview a binding without real state?
.constant(value) — a binding whose setter discards writes. It renders the view correctly for the static case. If the preview needs the control to actually respond, wrap it in a small container view holding real @State, or use @Previewable @State so the macro sets it up for you.
Why does SwiftUI use structs for views when they're recreated so often?
MidTap to think, then reveal
Why does SwiftUI use structs for views when they're recreated so often?
MidTap to think, then reveal
Because recreation is cheap and the alternative isn't. A struct is a value with no heap allocation and no reference counting — creating one is close to free, so SwiftUI can afford to build a fresh description of the UI on every change and diff it against the previous one. That's what makes the declarative model viable: body is a pure function from state to description, and the framework works out the minimal set of actual changes.
With classes you'd pay heap allocation and ARC traffic for every view on every update, and you'd have identity where you don't want it — two views describing the same thing would be different objects, so diffing would be harder rather than easier.
The consequence is that views can't hold their own memory, since the struct is thrown away. That's what @State is for: storage the framework keeps outside the struct, keyed by the view's identity in the hierarchy, with the wrapper holding a reference into it.
Identity then becomes the thing you have to be deliberate about. SwiftUI decides which storage belongs to which view by structural position or explicit .id(), so moving a view between branches of an if, or changing its id, gives it fresh state. Most of the "my state reset itself" bugs are identity changes nobody intended.
What an interviewer listens for
- Explains cheapness of value types as the enabler for diffing
- Frames body as a pure function from state to description
- Connects struct disposability to why @State exists
- Raises view identity as the thing that decides storage
Follow-ups
When would you use `@State` versus a reference type marked `@Observable`?
@State for state this one view owns and no one else needs — a toggle, a selected tab, whether a sheet is showing. An @Observable model when the state is shared across views, has behaviour attached, or outlives a single screen. The two combine: you hold the model in @State in the view that owns its lifetime, and pass it down either directly or through the environment.
How does SwiftUI know which views to re-render when an `@Observable` model changes?
SeniorTap to think, then reveal
How does SwiftUI know which views to re-render when an `@Observable` model changes?
SeniorTap to think, then reveal
It records dependencies by observing what each view actually reads. SwiftUI evaluates body inside withObservationTracking, and the macro has rewritten every stored property into a computed one that reports reads to an ObservationRegistrar. So by the end of evaluating a body, the framework has an exact set of properties that view depends on. Mutating one goes through withMutation, which notifies precisely the views that read it.
The contrast with ObservableObject is the granularity. There, any @Published write fires a single objectWillChange on the whole object, so every view observing it re-renders whether or not the change was relevant. In a screen with one model and twenty views, that's nineteen wasted body evaluations per keystroke. Observation makes that a non-issue without anyone hand-splitting models to work around it.
Two details I'd flag. Tracking is scoped to body evaluation, so a read in a button's action closure isn't a dependency — which is normally what you want. And granularity is per property, not per element: if a body reads an array, appending to it invalidates that view even if it only rendered one element. Fine-grained models don't help there; you need the view structure to match, typically by putting a ForEach row in its own view that reads only its element.
The practical upshot is that most gratuitous re-render problems go away, and the ones left are about view decomposition rather than about observation.
What an interviewer listens for
- Explains withObservationTracking and the registrar accurately
- Quantifies the improvement over objectWillChange
- Knows tracking is scoped to body evaluation, not closures
- Raises collection granularity as the remaining sharp edge
- Connects the fix to view decomposition
Follow-ups
How do you get a `Binding` out of an `@Observable` model?
@Bindable. It projects bindings for the model's properties, so $model.name works in a TextField. You can declare it as a property, or inline in a body with @Bindable var model = model when you only need it in one place. Plain @State on the model also projects bindings, but only in the view that owns it.
When would you still reach for `ObservableObject`?
Mainly for deployment targets below iOS 17, or when interoperating with existing Combine pipelines where you want objectWillChange as a publisher. For new code on current targets there's no reason — @Observable is less boilerplate and strictly better at invalidation.
Drill interview-level questions
Advanced and senior quiz questions only, across every category.