Understanding SwiftUI Property Wrappers

Kelly O’
CodeX
Published in
4 min readApr 1, 2021
Photo by Sam Moqadam on Unsplash

If you are new to SwiftUI your mind might be blown right now. It’s a whole new world! While setting up the UI is fairly straightforward and quite easy to implement, it’s when you get to trying to share data between views where you stop and scratch your head.

There are no IBActions or IBOutlets, no storyboard to control-drag a segue from one to another, and the prepareForSegue and performSegue methods are no longer a thing. So what to do? Fret not, Apple has come up with some handy property wrappers to take care of all your needs.

So what are these property wrappers and what do they do?

In this article we will cover the ones used for storing temporary data:

@State, @Binding, @StateObject, @ObservedObject, @Published, and @EnvironmentObject.

I have to say, when I was first introduced to these it left my head spinning a bit. I was following along with tutorials and it kind of made sense, but then when I went to build my own app I wasn’t quite sure what to use when. Hopefully this overview will help you distinguish when to use each property wrapper.

Let’s start with @State.

** Apple actually says it should be written as @State private since it’s meant to only be used within that view.

You will want to use @State to store value types that are used locally by your view. You will want to use it when that property in your view will be changing.

Hold on a minute…💡 You might be saying to yourself, views in SwiftUI are structs, which means they are immutable, you can’t change their properties. Well that’s exactly why Apple created the @State property wrapper, to allow us to modify values inside a struct.

The takes us to the @Binding property wrapper. It goes hand in hand with @State.

Here is Apple’s definition: @Binding is a property wrapper type that can read and write a value owned by a source of truth. Use a binding to create a two-way connection between a property that stores data, and a view that displays and changes the data. A binding connects a property to a source of truth elsewhere, instead of storing data directly.

For example, a button that toggles between play and pause can create a binding to a property of its parent view using the Binding property wrapper.

Whenever the user taps the play button, the PlayerView will update the isPlaying state.

Now when it comes to sharing data across many views, Apple has given us a handful of property wrappers to use.

Let’s start with @StateObject and @ObservedObject, both have a lot of similar functionality but have different uses.

Simply put, @StateObject should be used to initialize the object whereas @ObservedObject is used when you are passing the object in from a previous view. It is reliant on the data from the parent view.

A little tip to help you remember the difference, whenever you see @State or @StateObject, it means the current view owns this data. Never use @ObservedObject to create instances of your object, that’s what @StateObject is for. @ObservedObject is for when you are passing data from another view and you need the data from that view.

Next we have the @EnvironmentObject property wrapper which allows us to share model data anywhere it’s needed, while also ensuring that our views automatically stay updated when the data changes.

With @ObservedObject you would have to pass it from down the line from one view to the next in order to have access to it say, two views down the line. With @EnvironmentObject, all views automatically have access to it.

Also like @ObservedObject, you never assign a value to an @EnvironmentObject property. The value should be passed in from elsewhere, most likely it will have been created by @StateObject somewhere.

Very important thing to note: @StateObject, @ObservedObject, and @EnvironmentObject must refer to a class that conforms to the ObservableObject protocol.

The ObservableObject is a handy protocol, it turns the class instances into publishers that can emit values to anyone who subscribes to them. To do so the properties in the class must be declared using the the @Published property wrapper.

The @Published property wrapper broadcasts to SwiftUI that changes have happened and that it should trigger view reloads. Definitely an upgrade from the days of having to tell the tableview to reload.

Quick recap to try and keep it all straight: remember that anytime you see State whether it’s @State or @StateObject, it’s used to hold state in memory.

And you use @State for simple value type data that is created and managed locally. @Binding refers to value type data that is owned on a differnt view and changing the binding locally changes the remote data too.

@StateObject is for creating and managing reference type data locally and can be shared across other views using @ObservedObject. But don’t forget, in order for your views to be updated you need to set your class to conform to the ObservableObject protocol and you need to set your properties as @Published so that they will broadcast when there is a change so that the view can refresh.

This is the tip of the iceberg when it comes to property wrappers. We’ve only just touched the surface on six of them. Check out this great article by Paul Hudson that talks about all 17 property wrappers currently available in iOS 14.

Happy coding!

--

--