← All daysDay 13 of 49 · Part 4: State and data flow
Day 13

@Observable models

These are the lesson notes for this day. Its build-along tutorial and flash cards have not been written yet.

Lesson 15

@Observable models

For data that lives outside a single view — a model shared across several screens — write a class and mark it @Observable. SwiftUI records which properties each view's body actually reads, and re-renders a view only when one of those properties changes. A view that creates and owns the model keeps it in @State. Views that are handed the model simply declare an ordinary property. To get bindings to a model's properties, wrap it with @Bindable.

@Observable arrived in iOS 17. It replaces the older system of ObservableObject, @Published, @StateObject and @ObservedObject, which was built on Combine and re-rendered every observing view whenever anything changed. You will still meet the old form in existing code, so you need to be able to read it and to migrate it. Since iOS 26, Observations gives you an AsyncSequence of changes, for watching a model from outside a view.

Example
@Observable
final class TripStore {
    var trips: [String] = []
    var isSyncing = false
}

struct TripsScreen: View {
    @State private var store = TripStore()     // the owner keeps the model in @State

    var body: some View {
        TripCount(store: store)                // children simply receive it
    }
}

struct TripCount: View {
    let store: TripStore

    // This body reads `trips` only, so changing `isSyncing` does not re-render it.
    var body: some View {
        Text("\(store.trips.count) trips")
    }
}
Exercise

Create an @Observable model with two properties and two views that each display one of them. Add a print to each body and prove that changing one property re-renders only one view. Then convert a class that uses ObservableObject and @Published to @Observable.

After this lesson you should be able to answer
  1. How does SwiftUI decide which views to re-render when a property of an @Observable object changes?
  2. Where should a view keep an @Observable model that it owns, and how does a child view get a binding to one of its properties?
  3. What did @Observable replace, and what was the drawback of the older system?
Resources