10 Step Guide: SwiftUI ObservableObject to Observable Macro Transition

Experienced iOS and React Native Developer with 10+ Years of Expertise - Available for New Opportunities. Connect Now!
Search for a command to run...

Experienced iOS and React Native Developer with 10+ Years of Expertise - Available for New Opportunities. Connect Now!
No comments yet. Be the first to comment.
One of the critical elements of SwiftUI is its approach to managing data flow within applications. This comprehensive guide will provide you with a strong understanding of data flow in SwiftUI, how it operates, and why it's vital for your SwiftUI app...
This time, I'm exploring the fascinating world of AI, stepping away from my usual focus on iOS development. There's so much more to share about AI, and I can't wait to dive into it with you! If you'd like to follow along and receive regular updates, ...

The app is Time Tango, a Pomodoro timer app. I chose this concept after conducting keyword research for popular terms that face minimal competition on the app store. The app is free to use and received approval in its first review. The strategy focus...

One of the critical elements of SwiftUI is its approach to managing data flow within applications. This comprehensive guide will provide you with a strong understanding of data flow in SwiftUI, how it operates, and why it's vital for your SwiftUI app...

Arrays are one of the most common data structures used in programming. In Swift, arrays are used to store ordered lists of values of the same type. They are incredibly versatile and powerful, but using them efficiently is key to writing high-performi...

In my last article, I talked about how data flows in SwiftUI. Now, Apple has released something new in iOS 17 - the Observation Framework. This changes how SwiftUI works with data. In this article, we will look at how to use the new Observable Macro in your SwiftUI code.
Observable Macro generates code automatically for you to observe data changes. Compared to the old ObservableObject protocol, this feature is more powerful as it tracks changes in optional and collection data types and enhances your app's performance by avoiding unnecessary updates.
Start by importing the new Observation framework. You'll then need to replace the ObservableObject with the Observable macro. For example, the class "MovieLibrary" was an ObservableObject will become an Observable:
// BEFORE
import SwiftUI
class MovieLibrary: ObservableObject {
// ...
}
// AFTER
import SwiftUI
import Observation
@Observable class MovieLibrary {
// ...
}
Before, you had to wrap observable properties with Published. But the Observable macro takes care of that. So, you can remove the Published wrapper. For instance:
// BEFORE
@Observable class MovieLibrary {
@Published var movies: [Movie] = [Movie(), Movie(), Movie()]
}
// AFTER
@Observable class MovieLibrary {
var movies: [Movie] = [Movie(), Movie(), Movie()]
}
What if you don't want to track certain properties? Just use the ObservationIgnored macro:
@Observable class MovieLibrary {
@ObservationIgnored var somePropertyNotToTrack: Int = 0
var movies: [Movie] = [Movie(), Movie(), Movie()]
}
SwiftUI allows you to change your code bit by bit. You can start by converting one data model to use the Observable macro and then move to others. But, remember that the way SwiftUI tracks changes might be slightly different now.
Once you've converted all data models to use the Observable macro, replace StateObject with State:
// BEFORE
@main
struct MovieApp: App {
@StateObject private var movieLibrary = MovieLibrary()
var body: some Scene {
WindowGroup {
MovieLibraryView()
.environmentObject(movieLibrary)
}
}
}
// AFTER
@main
struct MovieApp: App {
@State private var movieLibrary = MovieLibrary()
var body: some Scene {
WindowGroup {
MovieLibraryView()
.environment(movieLibrary)
}
}
}
Next, you should replace the EnvironmentObject property wrapper with Environment:
// BEFORE
struct MovieLibraryView: View {
@EnvironmentObject var movieLibrary: MovieLibrary
var body: some View {
List(movieLibrary.movies) { movie in
MovieView(movie: movie)
}
}
}
// AFTER
struct MovieLibraryView: View {
@Environment(MovieLibrary.self) private var movieLibrary
var body: some View {
List(movieLibrary.movies) { movie in
MovieView(movie: movie)
}
}
}
Now it's time to remove the ObservedObject property wrapper. This property wrapper isn’t needed when adopting Observation. That’s because SwiftUI automatically tracks any observable properties that a view’s body reads directly. For example:
// BEFORE
struct MovieView: View {
@ObservedObject var movie: Movie
@State private var isEditorPresented = false
var body: some View {
HStack {
Text(movie.title)
Spacer()
Button("Edit") {
isEditorPresented = true
}
}
.sheet(isPresented: $isEditorPresented) {
MovieEditView(movie: movie)
}
}
}
// AFTER
struct MovieView: View {
var movie: Movie
@State private var isEditorPresented = false
var body: some View {
HStack {
Text(movie.title)
Spacer()
Button("Edit") {
isEditorPresented = true
}
}
.sheet(isPresented: $isEditorPresented) {
MovieEditView(movie: movie)
}
}
}
If your view needs a binding to an observable type, replace ObservedObject with the Bindable property wrapper:
// BEFORE
struct MovieEditView: View {
@ObservedObject var movie: Movie
@Environment(\.dismiss) private var dismiss
var body: some View {
VStack() {
TextField("Title", text: $movie.title)
.textFieldStyle(.roundedBorder)
.onSubmit {
dismiss()
}
Button("Close") {
dismiss()
}
.buttonStyle(.borderedProminent)
}
.padding()
}
}
// AFTER
struct MovieEditView: View {
@Bindable var movie: Movie
@Environment(\.dismiss) private var dismiss
var body: some View {
VStack() {
TextField("Title", text: $movie.title)
.textFieldStyle(.roundedBorder)
.onSubmit {
dismiss()
}
Button("Close") {
dismiss()
}
.buttonStyle(.borderedProminent)
}
.padding()
}
}
By following these steps, you've just migrated to the Observable macro in SwiftUI. Great job! You've enhanced your app's performance and can now track changes in your data more effectively.
You may also like: 7 Key Strategies for Reducing Code Duplication in SwiftUI
I hope you enjoyed this article, and if you have any questions, comments, or feedback, then feel free to comment here or reach out via Twitter.
Thanks for reading!