FREE TRIAL: Accelerate your app development career with Hacking with Swift+! >>

Observable objects, environment objects, and @Published

Paul Hudson    @twostraws   

Updated for Xcode 13.0

We want to let folks place an order for pick up by selecting items and adding them to a cart. I already gave you a dedicated Order class that holds an array of items, so we’re going to add items to that then show them in a dedicated order view.

But there’s a catch: if we’re adding things inside ItemDetail, how can we show them in an entirely separate OrderView? More importantly, how can we make sure both of these two update each other as things change?

Well, SwiftUI has a quite brilliant solution called environment objects. These are objects that our views can use freely, but don’t create or manage – they get created elsewhere, and carry on existing after the view has gone away.

In this app, we’re going to create an instance of our order when the app launches, then pass it into our content view. Any view that is inside that content view – anything that can call the content view its ancestor – will automatically gain access to that environment object. Even better, when any view changes it, all other places automatically update.

Let’s try it out now. Open your iDineApp.swift, which is where our initial instance of ContentView is created. Now give it this property:

@StateObject var order = Order()

Tip: Xcode will shown an error when you add that line, which is okay – we’ll fix it in a moment.

That creates a new order when the app starts, and keeps it alive regardless of what view we show. The @StateObject property wrapper is responsible for keeping the object alive throughout the life of our app.

Now we can pass that into our ContentView struct when it gets created – look for this:

WindowGroup {
    ContentView()
}

And replace it with this:

WindowGroup {
    ContentView()
        .environmentObject(order)
}

Now, I said that Xcode would throw up an error when we used the @StateObject property – something along the lines of “Argument type 'Order' does not conform to expected type ‘ObservableObject’”.

What it means is that SwiftUI doesn’t understand how its user interface is supposed to watch our Order class for changes – it doesn’t understand how it should send and receive notifications that the data changed.

Think about it: if we select some food from the menu and add it to our order, we want that to appear immediately on the order page – we don’t want to have hit refresh, or wait a few seconds, we want it immediately. And for that to work, SwiftUI needs a standard way for objects like Order to say “hey, if anyone is watching me, you should know my data just changed.”

This standard already exists, and it’s the ObservableObject protocol. Anything that conforms to ObservableObject can be used inside SwiftUI, and publish announcements when its values have changed so the user interface can be updated.

Apple provides a couple of different ways of publishing change announcements, but the easiest is to use the @Published property wrapper before any properties that should trigger change notifications. In this case, just placing @Published before a property is enough to have it update any SwiftUI views that are watching for changes – it’s really powerful!

So, open Order.swift and change the items property to this:

@Published var items = [MenuItem]()

And that’s it! Now that our class is configured correctly, we can make it conform to ObservableObject, like this:

class Order: ObservableObject {

…and our code is back to compiling again. In total, we have updated Order so it knows how to announce changes to any views that are watching, we have told the items array that whenever it changes it should send out such an announcement, we have created an instance of the Order object in our main app, and we have placed it into the SwiftUI environment for other views to use – nice!

Further reading

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for iOS devs who want to become complete senior developers — from October 18th to 24th. Learn how to apply iOS app architecture patterns through a series of lectures and practical coding sessions.

Learn more

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

Similar solutions…

BUY OUR BOOKS
Buy Pro Swift 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 (Vapor Edition) 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 Server-Side Swift (Kitura Edition) Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.0/5

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.