SwiftUI is an Framework introduced by Apple at WWDC2019 to handle the new style interface design to overcome the problems that people encounter with the old Swift + Interface Builder approach. An important idea of SwiftUI is that what shows on Views is the result of the corresponding State – We don’t directly change Views, instead, when the State is changed, it will make the View automatically update. updated. Many of you won’t understand what State is talking about here.
Apple defines that concept as follows:
It is a bit difficult to translate this concept, but can be understood simply, State includes data that we pour on the View as text set for Label, data displayed on Tableview or state variables like isVisible we created to set the state status for a view appears or hides. And when those values change, the UI will automatically update instead of having to manually call functions like tableView.reloadData (), titleLabel.isHidden == isVisible, …
This greatly aids in bindingData in the MVVM pattern. SwiftUI supports a number of ways to store State in an application. The new approach will be difficult to recognize the differences between them, but distinguishing concepts is very important to using the framework properly.
Before talking about State , there is one problem that you must grasp, that is in SwiftUI, Views are Structs, which means it cannot be changed. This is a little confusing, let’s try a simple example like this. Declare a detailText variable as String. On the view, there is 1 button and 1 Text (Like the Label when using Storyboards) showing the value of detailText, when pressing the button, the detailText is reassembled and the content on Text is also changed accordingly. We will try the following code:
We see the error “… ‘self’ is immutable”. The problem is that View cannot be changed, which I mentioned earlier (detailText is declared in View). So we have to handle how?
We will add the keyword State before the declaration. This means that we will authorize SwiftUI to store that variable value in memory as long as View exists. And when the State changes, SwiftUI will automatically understand to update the View corresponding to the latest state of the data. Back to the previous example
Add State and Self keyword (access variable in Closure) to see no more errors. Build and Rerun the project we get the desired results. Notice that when pressing Button, we change the detailText value and the Text will update itself without having to take another line to reset the text as when using UIKit.
State is a simple, effective way to use properties with a simple data type, belong to a specific view and never be accessed from outside that view. This feature is also different from State compared to other Wrappers.
Therefore the attributes declared State should be kept private. This is not mandatory but should be added to specify that it should never be accessed outside the view containing it.
As mentioned above, State is suitable for properties with simple data types. So what if we want to declare properties with complex data types that we define, or they are shared among views ??? -> ObservedObject is a solution of ObservedObject ‘s thought is quite similar to State , but there are a few differences:
- We will use “External Reference Type” instead of “Simple Local Property” like string or interger. Remember to refer to the Reference Type, ObservedObject cannot be used for Struct
- The view will still update itself when the data set for it changes, except that the data will be managed by us (from creating properties, initializing instances, …)
When you want to use ObservedObject , make sure the Class has implemented the ObservableObject protocol (Required). When declaring properties for ObservableObject, you will have to decide which properties will be “tracked”, ie when they change, the view must be updated. This is not a requirement, but using ObservableObject, you will need it.
There are several ways to notify the View that the data has been changed, but the easiest is to use the Published wrapper. You can also use other custom publishers of Combine Framework to increase control if desired. Since the data is bound to Views, make sure the notification of data changes and interface updates occurs on the Main thread.
Suppose we have a MainViewModel class, which is responsible for requesting API and returning Data
In ContentView, to display and update data, we call the following
Note: If multiple Views use data of a Observable Object, when the object changes, it will also automatically notify all of those views.
Once we have a model data that all views need, we can use EnvironmentObject .
All views can access the value of an EnvironmentObject model if they want, so this is a handy way if we want to move data around Views without having to do it manually.
When the model changes the value, all Views will also update immediately, thus eliminating the risk that Views display the same data but are not in sync.
Comparing Binding with the above 3 concepts may not be very true because they do not really have the same task. However, this is also an easy concept to learn about SwiftUI so I will include it in this article.
Binding is a Property Wrappers in SwiftUI, it allows us to declare a value that is actually declared elsewhere, but shared with each other. A little confusing, right? See the example below to understand the utility as well as distinguish it from the concepts outlined above.
Suppose we declare a Bool State property to determine whether child View is displayed or not.
We use showingAddUser as a parameter, which means that if this variable is set to True, then Add User View will appear.
So now, how to make Add User View self dismiss it when necessary (such as users press Done) What we need is how showAddUser is set to false, it will make ContentView automatically hide Add User View . This is when it comes to Binding . It allows creating an attribute in Add User View and telling it that the value of this variable will be provided from another place, and that value will be shared for Add User View and other Views.
Add the following code to AddUserView
Binding var isPresenter here can understand, I have a Boolean data type variable called isPresenter, its value will be provided elsewhere. And because the value of this variable is shared in many places, when changing the value, it will also update the value in other views.
Replace the following code into ContentView
Since the aforementioned isPresented is an Binding property, we need to provide it value so that it can be used.
We pass $ showingAddUser, which allows both ContentView and AddView to share the same Boolean value, and when there is a change in one place, it will also change in all other Views.
- State is used for simple data types, belongs to a single view and should be declared private
- ObservedObject is used for more complex data types like the classes we define and can be shared across multiple Views. It is also the most commonly used and most commonly used State storage
- EnviromentObject is used for properties that are initialized somewhere in the App but can be accessed anywhere
- Binding is used when wanting to mark the value of a variable in the View provided from elsewhere and share it with each other.