SwiftUI 101: How to use @State and @Binding in your first custom UI control
One of the biggest announcements from last WWDC was the new SwiftUI framework. The purpose of SwiftUI is to allow us to write declarative, state-base user interfaces for our applications. And as introduction of Swift changed the way we work, I believe SwiftUI will have significant impact on our daily work. If you haven’t heard about SwiftUI yet — I strongly recommend checking out the “Introducing SwiftUI” WWDC session.
Let’s create a Star-Picker
Today, I would like to guide you through creation of your very own first UI Control in SwiftUI. So let’s imagine that we have a recommendation app to create and we need a picker that will allow to select how many ⭐ we would like to award.
We will use the @State and @Binding in order to understand how to use newly introduced property wrappers in Swift 5.1. Following documentation:
A persistent value of a given type, through which a view reads and monitors the value.
A manager for a value that provides a way to mutate it.
They will monitor our
rating property and views will be automatically updated to reflect new value. Let’s see in practice how we can use those.
In order to run the experiments you will need at minimum Xcode 11. macOS Catalina comes in handy as well — it allows to use Preview on Xcode, which is super nice, hot-reloading from Apple. However, if you feel that installing beta version of MacOs is a bit too much — check out this article to see how to use Playground instead.
Ok, let’s create a simple component that will consist of 5 star icons and a label that describes current rating. To begin with, we will need a new Xcode project and lets make sure that “Use SwiftUI” checkbox is selected:
Afterwards you will see a
ContentView with simple Hello World label. Let’s create a new file
RatePicker (select SwiftUI View to get a new template) and add some code :)
Above you can see new ViewBuilder syntax for creating views in SwiftUI. First, we see
HStack, which tells that nested views should be aligned horizontally. Next, we wrap up
ForEach constructor. As View for button we provide here a simple star
Image(systemName: “star”) we have just used a new collection of icons — called SF Symbol. This extensive library contains hundreds of ready to use assets. You should definitely check out the SF Symbol app, which allows to browse the collection.
If your Xcode is able to run previews, you should see a similar screen:
Now we can add our new view directly to the original ContentView. We can accomplish this by wrapping both text label and the new view in VStack, which will lay them out vertically — simply by doing this:
Once our basic UI is there, let’s focus next on adding some interactions to our code. We would like to accomplish the following:
- Tap on star to select rating and highlight it;
- Once rating is selected, text label updates its value;
- Tap on selected rating removes the rate.
We will need a simple model that represents Ratings and their labels. Let’s use enum for this:
Now we can proceed to the ContentView and RatePicker view and introduce final changes:
There are a couple of important things to highlight here:
- We’ve introduced @State and @Binding operators — those are PropertyWrappers introduced with Swift 5.1;
- We’ve used $ prefix to pass binding to the child view;
- We have refactored RatePicker - now the star button is returned by function;
… and that’s it! It is all the code you need to have two views that react to the user interactions in a declarative fashion. Let’s spend a second to understand the new syntax:
State and Binding — what are those two about
@State keyword allows us to ask the SwiftUI to monitor the value of the property. Once the value will change, the View will be invalidated and rendered again in efficient manner.
Using this keyword creates “source of truth” and requires us to make a conscious decision which of the top-level views shall own this single source. The behaviour that invalidates the view allows to create Views that are a function of State, and not a function of Events. This means no more of spaghetti 🍝 KVO / notifications to update the UI to reflect user actions as long as we will keep the @State in single view hierarchy.
@Binding and $ prefix allows passing State property into the nested child.
In the above example Top level view creates a state (rate), which is later passed as binding (via $ prefix) to the nested view which expects binding (see declaration in line 4). With this structure, once user interacts with any button, both Rate Picker and Content View are updated to reflect new value of rate property. This new, two way communication drastically simplifies our code and reduces the need for the View Controllers 😢.
View builders and functions returning SwiftUI View
In the previously displayed sample we have also moved the code responsible for Star button into a separate function. The reason for this is because inside the View Builder block we cannot declare any properties (sorry if let). Yet, we can declare needed properties within the new function AND the return view builder block (mind that return keyword will be necessary in this case).
Power of Previews
Finally let’s have a deeper look into the already mentioned new Previews. The power they contain is not only to enable us the hot-reloading, but also to showcase how our View will look in different scenarios. We can modify our Preview to display all enum options at once (who would have time to click thru all of those!):
and here is the beautiful outcome:
Rating model to
CaseIterable protocol allows using
ForEach in elegant fashion. Also, it’s worth to remember that
ForEach requires its objects to conform to
Identifiable protocol. For Enums we can accomplish it simply by calling
In this short article we’ve learned how to create and preview simple views in SwiftUI that respond to user interaction and how we can use new property wrappers like State and Binding to our advantage. With the help of composition and declarative syntax, even more complex views can be created easily. This signifies great power of SwiftUI.
So, what are your first thoughts and impressions about this new framework?