Property Wrappers in SwiftUI — 2
Greetings everyone, this post is the continuation of my post called “Property Wrappers in SwiftUI 1”. Hit this link to read my previous article. In my first article of property wrappers, I wrote about what property wrappers are about in general, why we use them. Now it’s the time for some of the most common ones.
How Do We Use Them?
Before we start using property wrappers, we must decide what type of data we are going to manipulate. So will our data be of a primitive type (Integer,String,Boolean) or an Object type? If our data is of primitive type we should use @State or @Binding , if our data is of Object type we should use @StateObject or @ObservedObject property wrappers. In this article, I will only describe the @State , @Binding , @ObservedObject and @StateObject wrappers. Apart from these, there are many more property wrappers. You can access all of them from the link I will put at the end of the article.
You can visually see which property wrapper suits in your app with this link : https://swiftuipropertywrappers.com/
@State
State property wrapper is used for LOCAL data only. In other words, @State is only used in the View it was created in and other views do not need to access this variable. One of the best features of property wrappers is that by placing an observer and subject on the data you are wrapping, so that it allows the screen to update itself when the data changes, and it can show the changed data immediately. That means, View is the observer and data is the subject that lets View know when data changes. The example below is one of the examples I showed in my previous article. If we remove the @State property wrapper at the beginning of the code on line 12, you will see that XCode gives an error when compiles this code. While the @State wrapper ensures that the value of this variable can be changed over time, it also provides this variable with an observer and subject pair, allowing the View to display the current data by renewing itself every time the value of this variable changes.
Since the @State property wrapper is only used for local data, I recommend defining this property as private. Thus, we can understand that this data belongs only to the View in which it is located.
On more example for showing the View renews itself when data changes.
The array of strings on line 7 is an array that stores several names, and the function on line 18 returns a random element from the array that comes as a parameter. When the “Change Text” button is clicked, this function is called and the function assigns a random name from nameArray array to nameString. In other words, each time the button is clicked, the value in the nameString changes and we can see these changes on the screen thanks to the @State at the beginning of initialization.
@Binding
You should use Binding when you need to use the same variable in more than one View or when you need to pass this variable to another View so that when the value of this variable changes, you want all the Views using this variable gets notified. Unlike @State, you should use @Binding for property that will come from another View. In other words, this property will be created (initialized) in another View and will pass to this View.
I have two Views named ContentView and ContentView2. On line 5 i have created a Boolean variable called “isOn” and assigned this value to ContentView2’s property named “isOnFromContentView” by passing this as a parameter. If you noticed, I did not initialize the “isOnFromContentView” in ContentView2 and told XCode that it is a binding, it will come from another view. In the code on line 9, while creating ContentView2, I put “$” sign in front of “isOn”. This dollar sign is called “two way binding” and it means connect this property to other View. Thus, when this value changes in ContentView, it will automatically change in ContentView2 as well.
I will give one more example to understand the dolar sign and @Binding property wrapper better.
Instead of giving a Boolean value as in the previous example, this time I created a String named “text”. Pay attention to the text parameter of the TextField. By placing a $ sign in front of my property “text”, I bind it to the TextField, that is, bind this text to TextField’s text. Thus, every value written in the TextField is assigned to mine text property. Lastly I bind this property to the textFromContentView property of ContentView2 with a $ sign again. With this usage, ContentView2 is updated every time the text changes and we can see it in the ContentView2’ s Text.
@StateObject & @ObservableObject
If the data we will use is of an object type, that is, not a primitive data type, the wrappers we need to use would be @StateObject and @ObservedObject. In fact, these two mean almost the same thing. The only difference between them is that if the object we want to use is created (initialized) in the used View, it must be created as @StateObject. But if this object will be created in another View and used in another View, we should use this with @ObservedObject annotation. The only difference between @State,@Binding pairs and @ObservedObject,@StateObject pairs is that the data type we are using this time is an object. The data type I will use in the example will be a custom model named “UserPreferences” that I wrote myself. This model contains 2 Boolean values.
- Did user choose dark mode?
- Did user choose receive notifications?
There are three main steps in order to use ObservedObject and StateObject.
1- You need to extend the model with ObservableObject, like the code in line 3.
2- You must create the variables that you want to be notified when there is a change, by prefixing them with @Published.
3- If the object you will use is being created (initialized) for the first time, you need to create it by using @StateObject. If this object has already been created by another View and you want to use this object, you have to define your object using @ObservedObject.
— Since my object named “userPreference” will be created in the ContentView for the first time, I have to use @StateObject.
— Since my “userPreference” object has already been created by ContentView, I have to use @ObservedObject in NotificationView.
The general distinction I want you to understand is that @State and @Binding are used for primitive types, while @StateObject and @ObservedObject are used for object types.
Please feel free to reach me if you have any question or suggestion.
Thank you!