UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

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:

@Observable
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.

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
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.