SwiftUI Concepts

What is @state?

SwiftUI manages the storage of any property you declare as a state. When the state value changes, the view invalidates its appearance and recomputes the body. Use the state as the single source of truth for a given view.

State instance isn’t the value itself; it’s a means of reading and mutating the value. To access a state’s underlying value, use its value property.

@state will work only with struct not with class

Let’s put this into practice with a button, which in SwiftUI can be created with a title string and an action closure that gets run when the button is tapped:

That code looks reasonable enough: create a button that says “Tap Count” plus the number of times the button has been tapped, then add 1 to tapCount whenever the button is tapped.

However, it won’t build; that’s not valid Swift code. You see, ContentView is a struct, which might be created as a constant. If you think back to when you learned about structs, that means it’s immutable – we can’t change its values freely.

When creating struct methods that want to change properties, we need to add the mutating keyword: mutating func doSomeWork(), for example. However, Swift doesn’t let us make mutating computed properties, which means we can’t write mutating var body: some View – it just isn’t allowed.

This might seem like we’re stuck at an impasse: we want to be able to change values while our program runs, but Swift won’t let us because our views are structs.

Fortunately, Swift gives us a special solution called a property wrapper: a special attribute we can place before our properties that effectively gives them super-powers. In the case of storing simple program state like the number of times a button was tapped, we can use a property wrapper from SwiftUI called @State, like this:

That small change is enough to make our program work, so you can now build it and try it out.

@State allows us to work around the limitation of structs: we know we can’t change their properties because structs are fixed, but @State allows that value to be stored separately by SwiftUI in a place that can be modified.

One of the great things about the @State property wrapper is that it automatically watches for changes, and when something happens it will automatically re-invoke the body property. That’s a fancy way of saying it will reload your UI to reflect the changed state, and it’s a fundamental feature of the way SwiftUI works.

Whats is @Binding?

Passing data in the initializer is great if you want to pass data to the view’s children. If you want the reverse, however, you need a binding.

Bindings enable two-way communication between your views. They’re usually used with interactive views like text fields, toggles, checkboxes and similar. They allow two views to share a single piece of state.

For instance, when you created ErrorTextField, you used a binding to a String instead of a state variable.

struct ErrorTextField: View {
  ...
  let text: Binding<String>
  ...
}

Later, you initialized this view in the login screen by passing it the binding:

struct LoginView: View {
  
  @State private var email = ""
  
  var body: some View {
    ErrorTextField(
      ...
      text: $email,
      ...)
  }
}

In the above code, the $ operator converted a state to a binding. A binding is a kind of pointer to a piece of state. LoginView owns the state, but by giving ErrorTextField a binding to that state, it lets the text field change the value of the state.

This means that both LoginView and ErrorTextField can change the value of the email, and both will always update to match the latest value.

What is @propertyname in SwiftUI

That adds a name property, then uses it to create the text field. However, that code still won’t work because Swift needs to be able to update the name property to match whatever the user types into the text field, so you might use @State like this:

But that still isn’t enough, and our code still won’t compile.

The problem is that Swift differentiates between “show the value of this property here” and “show the value of this property here, but write any changes back to the property.”

In the case of our text field, Swift needs to make sure whatever is in the text is also in the name property, so that it can fulfill its promise that our views are a function of their state – that everything the user can see is just the visible representation of the structs and properties in our code.

This is what’s called a two-way binding: we bind the text field so that it shows the value of our property, but we also bind it so that any changes to the text field also update the property.

In Swift, we mark these two-way bindings with a special symbol so they stand out: we write a dollar sign before them. This tells Swift that it should read the value of the property but also write it back as any changes happen.

So, the correct version of our struct is this:

Try running that code now – you should find you can tap on the text field and enter your name, as expected.

Before we move on, let’s modify the text view so that it shows the user’s name directly below their text field:

Notice how that uses name rather than $name? That’s because we don’t want a two-way binding here – we want to read the value, yes, but we don’t want to write it back somehow, because that text view won’t change.

So, when you see a dollar sign before a property name, remember that it creates a two-way binding: the value of the property is read, but also written.

What is foreach?

SwiftUI gives us a dedicated view type for this purpose, called ForEach. This can loop over arrays and ranges, creating as many views as needed. Even better, ForEach doesn’t get hit by the 10-view limit that would affect us if we had typed the views by hand.

ForEach will run a closure once for every item it loops over, passing in the current loop item. For example, if we looped from 0 to 100 it would pass in 0, then 1, then 2, and so on.

For example, this creates a form with 100 rows:

Because ForEach passes in a closure, we can use shorthand syntax for the parameter name, like this:

What is some in SwiftUI?

https://www.hackingwithswift.com/books/ios-swiftui/why-does-swiftui-use-some-view-for-its-view-type

What is environmental modifier?

For example, if we have four text views in a VStack and want to give them all the same font modifier, we could apply the modifier to the VStack directly and have that change apply to all four text views:

This is called an environment modifier, and is different from a regular modifier that is applied to a view.

Views as properties

https://www.hackingwithswift.com/books/ios-swiftui/views-as-properties

Custom view

https://www.hackingwithswift.com/books/ios-swiftui/view-composition

Custom modifiers

https://www.hackingwithswift.com/books/ios-swiftui/custom-modifiers

What is @ObservedObject and @EnvironmentObject

If you want to use a class with your SwiftUI data – which you will want to do if that data should be shared across more than one view – then SwiftUI gives us two property wrappers that are useful: @ObservedObject and @EnvironmentObject

What is identifiable?

https://www.hackingwithswift.com/books/ios-swiftui/working-with-identifiable-items-in-swiftui

@Published

As an example, here’s an observable object that stores user settings:

Yes, it only stores one value, but that’s OK – what matters is that when the value changes the @Published property wrapper will ensure all views using it are refreshed.

User settings are a sensible piece of data that we might want to share everywhere in our app, so that we no longer need to handle synchronizing it by hand.

So, when our app first launches we’re going to create an instance of UserSettings so that shared instance is accessible everywhere in our app.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s