Binding Variables

Last updated on 20 Oct 2024.

Written by Jia Chen.

Binding Variables

Last updated on 20 Oct 2024.

Written by Jia Chen.

Binding Variables

Last updated on 20 Oct 2024.

Written by Jia Chen.

Scroll Down

On this page

On this page

What are Bindings?

Binding creates a two-way connection between a property that stores data, and a view that displays and changes the data.

This allows you to pass a State value down to another view and allow that view to update its contents.

Bindings are often used with controls as it allows a control to update the original value.

In the following example, a Binding counter value is passed into the Stepper. This allows the Stepper to update the counter's value by incrementing and decrementing it when the user taps on it.

struct ContentView: View {
    
    @State private var counter = 0
    
    var body: some View {
        VStack {
            Text("\(counter) cookies eaten!")
            
            Stepper("Cookies Eaten", value: $counter)
        }
        .padding()
    }
}

Another common example is when using a sheet to present a modal.

When presenting a sheet, the isSheetPresented value is set to true. Passing it in as a Binding allows the sheet to observe the change and present the sheet.

When the sheet is dismissed, the sheet will set isSheetPresented to false. Passing it in as a Binding allows the sheet to update the value of the State property.

.sheet(isPresented: $isSheetPresented) {
    ...
}

Declaring a Binding variable

To declare a Binding variable, the @Binding property wrapper used to prefix the variable's declaration.

Importantly, a Binding variable should not be initialized with an initial value and should simply contain the type annotation as seen below.

Unlike State variables, Binding variables should not be declared as private. This allows Binding variables to appear in the view's initializer by default.

@Binding var myValue:

In the following example, a MoodPickerView is created. The control allows a user to select an emoji as their mood and assigns it to the mood Binding variable.

struct MoodPickerView: View {
    
    @Binding var mood: String
    
    var body: some View {
        HStack {
            Button("😊") {
                mood = "😊"
            }
          
            Button("😢") {
                mood = "😢"
            }
          
            Button("😡") {
                mood = "😡"
            }
          
            Button("😱") {
                mood = "😱"
            }
          
            Button("🤢") {
                mood = "🤢"
            }
        }
        .padding()
        .font(.largeTitle)
    }
}

To initialize the MoodPickerView, you can call it as such.

MoodPickerView(mood: $mood)

This allows you to pass in a mood State variable as a Binding variable, allowing MoodPickerView to update its value.

struct ContentView: View {
    
    @State private var mood = "😊"
    
    var body: some View {
        VStack {
            Text("How are you feeling?")
            MoodPickerView(mood: $mood)
        }
        .padding()
    }
}

You can also choose to have different names for the State and Binding variables. In this example, the mood is called currentMood and passed into the mood parameter of the MoodPickerView.

struct ContentView: View {
    
    @State private var currentMood = "😊"
    
    var body: some View {
        VStack {
            Text("How are you feeling?")
            MoodPickerView(mood: $currentMood)
        }
        .padding()
    }
}

Working with Bindings

  • State variables: This is where the data is originally stored, representing the single source of truth. For example, you could have a State variable to display the user's name.

    @State private var username = ""


  • Binding: A binding is created when you access the @State property with a dollar-sign prefix ($) (e.g. $variable). The binding allows other views or controls to directly modify this state.

    TextField("Username", text: $username)


  • Two-way Communication: When a control (like Text Fields, Sliders, or Pickers) is bound to a State value through a binding, it creates a two-way relationship

    • If the user interacts with the control (e.g. changes a Text Field's content, slides a Slider, a Picker option is selected), the underlying State value updates.

    • The user interface will then update to reflect this change.

SwiftUI Previews & Bindings

The preview is often used in SwftUI to quickly iterate, make changes, and see how they look like in the app. Using a Binding poses a challenge as you will need to supply the previewed instance of the view with the Binding value.

You can use the @Previewable Swift Macro to create State variables within your previews. This allows you to use the familiar dollar-sign prefix ($) to pass in a Binding value.

#Preview {
    @Previewable @State var mood = "😊"
    MoodPickerView(mood: $mood)
}

Using a Constant Value

You may also see something like this, especially when finding information online.

#Preview {
    MoodPickerView(mood: .constant("😊"))
}

The .constant() method allows you to provide a constant Binding value, however, as the value is constant, this may lead to issues with the Live Preview where certain elements might not be interactive.

The @Previewable Swift Macro is intended to help replace the use of .constant() in these contexts as it allows the preview to remain interactive.

Custom Bindings

Custom Bindings are especially useful if you need to manipulate the values before passing it as a Binding. This is similar to Computed Properties where you can define custom getter and setter methods.

Some reasons you might need to use custom Bindings might be to convert between data types or unwrapping an Optional value.

In the example below, the mood State variable is set to nil until a selection is made. However, the MoodPickerView only accepts a non-Optional String.

struct ContentView: View {
    
    @State private var mood: String?
    
    var body: some View {
        VStack {
            Text("How are you feeling?")

            let bindingMood = Binding {
                mood ?? ""
            } set: { newValue in
                mood = newValue
            }
            MoodPickerView(mood: bindingMood)
        }
        .padding()
    }
}

Taking a closer look at the custom Binding initializer, it accepts 2 closures

  • The Getter: This allows you to write a block of code to retrieve the value.

    • In this example, the getter is used to unwrap the Optional value using the nil-coalescing operator (??) to provide a default value. This means if mood is nil, the bindingMood will return an empty String.

  • The Setter: This allows you to write a block of code to update the necessary State values with the newValue.

    • In this example, the setter is used to update the mood State variable with the newValue.

let bindingMood = Binding {
    mood ?? ""
} set: { newValue in
    mood = newValue
}

MoodPickerView(mood: bindingMood)

© 2024 Tinkertanker Pte Ltd / Swift Accelerator. All rights reserved.

© 2024 Tinkertanker Pte Ltd / Swift Accelerator. All rights reserved.

© 2024 Tinkertanker Pte Ltd / Swift Accelerator. All rights reserved.