Property Wrappers in SwiftUI

Ozkan Erdem
Orion Innovation techClub
7 min readJul 6, 2022

Let’s take a look at the property wrappers that are used extensively in SwiftUI to make changes to your view’s data values and objects.

Managing UI Values and Model Objects

Even though property wrappers are a feature of the Swift language introduced with Swift 5.1, these are also widely used in SwiftUI.

SwiftUI trusts heavily on the property wrappers to make out a code easier to read, write & maintain.

This article is all about how to use SwiftUI property wrappers to manage changes to its data values and objects with the @State, @Binding, @Published, @Environment, @ObservedObject, @StateObject and @EnvironmentObject property wrappers.

@State

The @State is used inside of View objects and allows your view to responding to any changes made to @State. You use @State for properties that are owned by the view that it’s contained in.

@State should be used with simple struct types such as string, integer, and arrays and generally shouldn’t be shared with other views. If you want to share values across views, you should use @ObservedObject or @EnvironmentObject instead, both of those will ensure that all views will be refreshed when the data changes. (You can find more details about these property wrappers below 🤓.)

Note: External sources shouldn’t modify your @State properties, so you should mark your @State properties private as follows:

<@State private var name = “ ”

Let’s glance at the following sample I prepared for the @State property wrapper.

If we do not use the @State property wrapper for the property whose value we want to change at run time, we will get the error that appears on lines 29 and 30 in the image below. Xcode compiler would not compile this, because structs are immutable. It is saying you can not assign a value to the property of View because it’s in a struct, hence it is immutable.

We need to solve this by using @State as a property wrapper. By adding @State lines 12 and 13, the Xcode now compiles the code and works fine 😊.

@Binding

The @Binding property wrapper is used to synchronize the states between two views. In other words, the @Binding property wrapper is used for properties passed by another view.

Let’s glance at the following sample I prepared to better understand the @Binding property wrapper.

This is a basic sample of how to share data between views in SwiftUI. As you can see, this uses the showingName for the view’s isPresented parameter, so when this boolean is true, the NameView will be displayed. However, it can close the NameView itself by setting the showingName of the NameView back to false. This is exactly what @Binding property wrapper is for: it lets us create a property in the add user view that says this value will be provided from elsewhere, and will be shared between us and that other place.

As seen in the sample, we should use @Binding a property wrapper when need to read and write access to a property that’s owned by a parent view.

@ObservedObject

How can we notify the UI that when data is updated or downloaded, the UI must be refreshed to display new data? In SwiftUI, we can easily do this using the @ObservedObject property wrappers.

NOTE: The view does not create the instance of the @ObservedObject itself. (if it does, you need a @StateObject)

Let’s glance at the following sample I prepared to better understand the @ObservedObject property wrapper.

It has drawn your attention that I also use the @Published property wrapper in this sample. After reading the @Published property wrapper section, you will understand why I use it. 👇

@Published

@Published is the most useful property wrapper in SwiftUI. When the property or value of an object marked @Published is changed, all views using that object will be reloaded to reflect those changes.

Let’s glance again at the sample for the @ObservedObject property wrapper to better understand the @Published property wrapper.

class User : ObservableObject {   internal init (name: String, age: Int){   self.name = name   self.age = age   }   var name : String   @Published var age : Int }

That User class uses @Published so it will automatically send change announcements when theage variable changes, and DetailView uses @ObservedObject to watch for those announcements.

NOTE: Without @ObservedObject the change, announcements would be sent but ignored!!!

@StateObject

Introduced with iOS 14 and SwiftUI 2.0, @StateObject is a property wrapper type that instantiates an Observable Object.

@StateObject allows us to keep a reference to an object within a view
With the Observable Object protocol, we define in our class.

It uses @Publishedproperties to maintain values, which can be used to
write and read to cause views to be re-rendered.

It’s important to use @StateObject whenever you need a reference type for the object to stay alive and not get released/destroyed when views are recreated. Because views are structs (value types).

NOTE: You should only use @StateObject once per object, which should be in the view responsible for creating the object. All other views that share your object must use @ObservedObject.

Let’s glance at the following sample I prepared to better understand the @StateObject property wrapper.

Where/When would you want to use @StateObject;

  • If you have an object with @Published properties
    that needs to be alive as long as the view is.
  • You want to avoid using @ObservedObject and use this for the view that
    need to maintain a reference to this object for as long as the view is
    Alive
  • If you need to manage and maintain multiple properties and objects
    within a class using an Observable Object with @StateObject would be
    cleaner.

@EnvironmentObject

You may want to use a piece of data in the home view and also deep in a settings menu, but if we don’t want every view in between to know about that data, in other words, we don’t need it, we need to use the @EnvironmentObject property wrapper.

@EnvironmentObject has a lot in common with @ObservedObject, they both must reference a class compatible with Observable Object, both can be shared across many views, and both update all monitored views whenever significant changes occur.

NOTE: When a view using @EnvironmentObject is shown, SwiftUI will immediately search the environment for an object of the correct type. If no such object is found, i.e. you forgot to place it in the environment, your app will crash immediately. When you use @EnvironmentObject you are effectively promising that the object will be present in the environment when it is needed, which is a bit like using an implicitly unwrapped optional.

Notice how the random property isn’t given a default value, by using @EnvironmentObject we’re saying that value will be provided by the SwiftUI environment rather than explicitly created by this view.

CONCLUSION

  • @State should be used with simple struct types such as Int, Bool, or String and generally shouldn’t be shared with other views.
  • @Binding should be used to synchronize the states between two views. In other words, it should be used for properties passed by another view/
  • @ObservedObject should be used to notify the UI that the UI needs to be refreshed to display new data when data is updated or downloaded. Don’t forget to use @Published property wrappers on the properties you would like to cause the view to re-render, or you’d like to update from a view!!!
  • @StateObject should be used to create any type that is more complex than what @State can handle. As with @ObservedObject, don’t forget to use @Published property wrappers on properties you want to cause the View to be re-rendered or updated from a view!!! (Hint: Always use @StateObject when you are instantiating a model, and ensure that the type conforms to Observable Object.😉)
  • @EnvironmentObject should be used to use up an Observable Object that has already been created in a parent view and then attached via the view’s environmentObject() view modifier.

REFERENCES

That’s all for now. Thanks for reading. I hope it was useful.

Happy Coding !! 🤓

--

--