NEW: Subscribe to Hacking with Swift+ and accelerate your learning! >>

Observable objects, environment objects, and @Published

Paul Hudson    @twostraws   

Updated for Xcode 12.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 SceneDelegate.swift, which is where our initial instance of ContentView is created. Now give it this property:

var order = Order()

That creates a new order when the app starts, and keeps it alive regardless of what view we show. Now we can pass that into our ContentView struct when it gets created inside the willConnectTo method – look for this line:

let contentView = ContentView()

And replace it with this:

let contentView = ContentView().environmentObject(order)

If you try compiling the code, you’ll find it doesn’t work – Swift will flag up an error 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. Property wrappers were introduced in Swift 5.1, and allow us to control the way properties are read and written. 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.

Further reading

Hacking with Swift is sponsored by NSSpain

SPONSORED Announcing NSSpain 2020: Remote Edition! An online, continuous conference for iOS developers. We’ll start on Thursday and finish on Friday, with talks, activities, and lots of fun for 36 hours, non-stop. Sound good? Join us!

Find out more

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

Similar solutions…

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: 3.8/5

Link copied to your pasteboard.