SwiftUI lessons (part 6)

link to part 5

Understanding State and Binding

We've finally gotten to some of the most important topics related to data management, including data modification, tracking and transfer. We'll start with the simplest things like State and Binding

Let's create a new project and call it SUIStateBinding

While Xcode is generating files for us, let's look at the theoretical core issue related to State

State in SwiftUI is a property that stores the source of truth for a view. This means that State defines the data that is used in the view and controls changes to it. When the State value changes, SwiftUI automatically updates the view to reflect those changes. Thus, State is the key element to implement reactivity in SwiftUI.

We've figured this out, let's look at it in practice, imagine that we want to change the color of a button when you click on it, let's try to implement this in code, insert the following fragment into your ContentView file

struct ContentView: View {

	private var buttonColor = Color.blue
	
	var body: some View {
		VStack {
			Button("Change Color") {
				// Изменяем цвет кнопки при нажатии
				buttonColor = (buttonColor == Color.blue) ? Color.red : Color.blue
			}
			.padding()
			.background(buttonColor)
			.foregroundColor(.white)
			.cornerRadius(10)
			.animation(.easeInOut, value: buttonColor) // Добавляем анимацию при изменении цвета
		}
	}
}

And we immediately encounter the error, why is immutable if we specified our buttonColor as var?

In SwiftUI, every View is immutable, which means that once a View is created, it cannot be changed during its lifecycle. Even if you use var for variables inside body, they still cannot be changed after the view has been created. This can also be explained as follows – in Swift struct are value types, not reference types, and by default they are immutable. When you declare a variable of type structeven with a keyword varyou can change its properties, but you cannot change the value of the variable itself.

In the case of our example, buttonColor is an instance of the structure Colorand when you try to change it internally bodyyou are actually trying to change the value of the variable itself buttonColorwhich is impossible because struct immutable by default.

Usage @State solves this problem because the annotation @State creates a special storage (storage) that can be changed internally bodyand SwiftUI automatically handles changes to this store, rebuilding the view as needed, so let's add @State to our buttonColor

struct ContentView: View {

	@State private var buttonColor = Color.blue
	
	var body: some View {
		VStack {
			Button("Change Color") {
				// Изменяем цвет кнопки при нажатии
				buttonColor = (buttonColor == Color.blue) ? Color.red : Color.blue
			}
			.padding()
			.background(buttonColor)
			.foregroundColor(.white)
			.cornerRadius(10)
			.animation(.easeInOut, value: buttonColor) // Добавляем анимацию при изменении цвета
		}
	}
}

Try to press the button, as you can see it changes color animatedly, we defined the animation on line 24, animations are not the topic of this part of the articles about SUI, so we’ll look at this later, but in general, now you know how you can follow states of properties that can change during application operation.

Inside SwiftUI, the framework uses an observer mechanism to track changes in the state marked with an annotation. @State. When the value @State changes, SwiftUI automatically starts the process of rebuilding the view to display the new state value.

Let's look at Binding

A Binding in SwiftUI is a bidirectional relationship between two values, allowing one value to track and automatically update based on changes in the other value. Binding plays a key role in SwiftUI's reactive architecture, allowing views and subviews to interact with shared data.

Let's create a separate ButtonView that will contain our Binding property and, depending on its changes, change its display:

struct ButtonView: View {
	@Binding var counter: Int
	
	var body: some View {
		Button {
			counter += 1
		} label: {
			Text("\(counter)")
				.padding(30)
				.font(.system(size: 60))
				.background(.red)
				.clipShape(.circle)
		}
	}
}

So we created a counter property that will be somehow connected to another view, and when this counter itself changes, we want the text inside the label to change, let's add this ButtonView and another text object that will also monitor such a counter in our ContentView

struct ContentView: View {
	@State private var counter = 0
	
	var body: some View {
		VStack {
			Text("\(counter)")
				.padding(30)
				.font(.system(size: 60))
				.background(.green)
				.clipShape(.circle)
			ButtonView(counter: counter)
		}
	}
}

When we wanted to add our counter to the ButtonView, an error occurred, Xcode tells us that we cannot use a value of type Int since we are expected to have a Binding, this can be easily achieved by simply substituting the $ sign

struct ContentView: View {
	@State private var counter = 0
	
	var body: some View {
		VStack {
			Text("\(counter)")
				.padding(30)
				.font(.system(size: 60))
				.background(.green)
				.clipShape(.circle)
			ButtonView(counter: $counter)
		}
	}
}

But what does all this mean?

Sign $ before a variable in SwiftUI is used to create a Binding to the value of the variable. When you use $ before a variable, you get a Binding to its value, allowing you to read and change that value from other views or inside closures.

Concerning Binding under the hood, it's just a type that describes a Binding to a value of the type Int. There are many Binding types in SwiftUI, each corresponding to a specific data type, e.g. Binding, Binding, Binding etc. These types allow you to create Bindings to different data types and work with them in SwiftUI.

To make it a little more clear, I would still recommend diving into the description of Binding, looking at its initializer and reading the examples that Apple gives here

What is more important for us is that after adding the dollar sign, we received the Binding property passed to our ButtonView and now any change to the counter, which is a ContentView property, will occur in both the ButtonView and the ContentView

When to use State:

  • Local state within a view: State is great for storing local state within a single view. For example, you can use State to store text in a text field or the value of a counter.

  • User Experience Management: When you need to respond to user actions and display appropriate changes in the interface, State makes it easy to track those changes and update the view accordingly.

  • Simple applications or views: State is great for simple use cases where you don't need to pass data between different views or manage complex states.

When to use Binding:

  • Passing data between views: Binding is ideal for passing data between different views. For example, if you need to pass a value from one view to another for display or editing, Binding provides bidirectional communication between those values.

  • Shared state between views: If you need to ensure data consistency across multiple views, you can use Binding to link that data and make it consistent.

When to use both:

In some cases, you may need to use both State and Binding in the same application. For example, you can use State to store local state within a view, and Binding to pass that state to other views. This allows you to orchestrate efficient communication between different parts of your application, managing both local and shared state.

Results

In this part, we have dealt with State and Binding, both in theory and in practice, but I suggest you practice some more. Alternatively, try creating three different Views, display them in one ContenView and link some data that is considered in them with another View that will display this same data

For example: The top view will count the number of clicks in all views below it, the red view, green and blue will be different views with their own counters.

As before, subscribe to my telegram channel – https://t.me/swiftexplorer

The next parts of SwiftUI tutorials will be released soon.

Thanks for reading!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *