WWDC24 SALE: Save 50% on all my Swift books and bundles! >>

Sharing SwiftUI state with @Observable

Paul Hudson    @twostraws   

If you use @State with a struct, your SwiftUI view will update automatically when a value changes, but if you use @State with a class then you must mark that class with @Observable if you want SwiftUI to watch its contents for changes.

To understand what's going on, let's take a look at this code in more detail:

class User {
    var firstName = "Bilbo"
    var lastName = "Baggins"

This is a class with two string variables, but it starts with @Observable. That tells SwiftUI to watch each individual property inside the class for changes, and reload any view that relies on a property when it changes. I know that might sound a bit like magic, but it's not – it's just a whole lot of work carefully hidden.

I want to dig in a little here so you can see what's really happening, and to do that I'd like you to add another import line to the top, next to import SwiftUI:

import Observation

That @Observable line is a macro, which is Swift's way of quietly rewriting our code to add extra functionality. Now that we've imported the framework it comes from, Xcode can do something really neat: if you right-click on @Observable in your code, you can choose Expand Macro to see exactly what rewriting is happening – Xcode will show you all the hidden code that's being generated.

I'm not going to write out the whole macro expansion here because it's a lot, but I do want to point out three things:

  1. Our two properties are marked @ObservationTracked, which means Swift and SwiftUI are watching them for changes.
  2. If you right-click on @ObservationTracked you can expand that macro too – yes, it's a macro inside a macro. This macro has the job of tracking whenever any property is read or written, so that SwiftUI can update only views that absolutely need to be refreshed.
  3. Our class is made to conform to the Observable protocol. This is important, because some parts of SwiftUI look for this to mean "this class can be watched for changes."

All three of those are important, but it's the middle one that is doing the heavy lifting: iOS keeps track of every SwiftUI view that reads properties from an @Observed object, so that when a property changes it can intelligently update all the views that depend on it while leaving the others unchanged.

When working with structs, the @State property wrapper keeps a value alive and also watches it for changes. On the other hand, when working with classes, @State is just there for keeping object alive – all the watching for changes and updating the view is taken care of by @Observable.

Save 50% in my WWDC sale.

SAVE 50% To celebrate WWDC24, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.9/5

Unknown user

You are not logged in

Log in or create account

Link copied to your pasteboard.