As asked
Explain the difference between @State, @StateObject, @ObservedObject, @EnvironmentObject, and @Bindable in SwiftUI. When do you reach for each?
Sample answer outline
@State: value-type local state owned by the view. @StateObject: reference-type owned by the view, lifecycle tied to the view. @ObservedObject: reference-type passed in, lifecycle owned elsewhere. @EnvironmentObject: injected through the environment, for app-wide singletons like a user session. @Bindable (iOS 17+, with the Observation framework): for two-way binding on @Observable classes, replacing the older pattern. The rule of thumb: pick the simplest one that owns the lifecycle correctly. State leaks happen when you mix @StateObject and @ObservedObject incorrectly and the model gets recreated on every render.
Reference implementation (swift)
import SwiftUI
@Observable
final class CartModel {
var items: [LineItem] = []
var total: Decimal { items.reduce(0) { $0 + $1.price } }
}
struct CartView: View {
// Owned here, lives as long as the view
@State private var cart = CartModel()
var body: some View {
NavigationStack {
List(cart.items) { item in
LineItemRow(item: item)
}
.navigationTitle("Cart")
CheckoutButton(cart: cart)
}
}
}
struct CheckoutButton: View {
// Passed in, two-way bindings via @Bindable when needed
@Bindable var cart: CartModel
var body: some View {
Button("Checkout (\(cart.total))") { /* ... */ }
}
}Expect these follow-ups
- Why is the @Observable macro a better default than ObservableObject?
- When does @EnvironmentObject hurt testability?
- What is the equivalent of useEffect in SwiftUI?