Categories
iOS Swift

@Observable macro in SwiftUI

@Observable macro was announced in WWDC23 with the aim of simplifying observation related code and improving the performance of the app. The performance boost comes from the fact that SwiftUI starts tracking which properties are used in SwiftUI view’s body and only then view rendering is triggered when tracked properties change. If any of the other properties change in the same model object, no new re-rendering happens – excellent.

It has always felt a bit odd to use the @Published property wrapper for each of the property, which should trigger view updates. I feel overwhelmingly happy that I can clean up all of that noise from the code now. Let’s compare two model objects: one which uses ObservableObject and the other one using the new @Observable macro. Note that we need to import the new Observation framework for being able to use @Observable.

struct ContentView: View {
@StateObject private var viewModel = ContentViewModel()
var body: some View {
VStack {
Text(viewModel.title)
TextField("Username", text: $viewModel.username)
.textFieldStyle(.roundedBorder)
}
.padding()
}
}
final class ContentViewModel: ObservableObject {
@Published var username: String = ""
var title: String {
if username.isEmpty {
return "Hello, world!"
}
else {
return "Hello \(username)!"
}
}
}
view raw Old.swift hosted with ❤ by GitHub
A simple view with a view model conforming to ObservableObject.
import Observation
import SwiftUI
struct ContentView2: View {
@Bindable private var viewModel = Content2ViewModel()
var body: some View {
VStack {
Text(viewModel.title)
TextField("Username", text: $viewModel.username)
.textFieldStyle(.roundedBorder)
}
.padding()
}
}
@Observable class Content2ViewModel {
var username: String = ""
var title: String {
if username.isEmpty {
return "Hello, world!"
}
else {
return "Hello \(username)!"
}
}
}
view raw New.swift hosted with ❤ by GitHub
A simple view with a view model using the new @Observable macro.

When we compare these two views then the main differences are that we can drop using @Published property wrappers, instead of conforming to ObservableObject protocol we instead add the @Observable macro in front of the view model’s definition. Instead of @StateObject we can just use @State since we still want the view to own and keep one instance of the view model.

Here we have a tiny example, but if we are dealing with larger models then having a chance to skip all the @Published property wrappers is a bliss.

Note: In Xcode 15 beta 1 the Swift compiler crashes if the Content2ViewModel is part of the ContentView2 extension.

Time to get back to consuming WWDC23 information, thank you for reading!

If this was helpful, please let me know on Mastodon@toomasvahter or Twitter @toomasvahter. Feel free to subscribe to RSS feed. Thank you for reading.