SwiftUI.Binding: 7 ways to create, three of them I don't know how to use, help me figure them out!

There are 7 ways to create a SwiftUI.Binding:

Each one is useful for very specific purposes. I find it very interesting they have so many different ways to create binding. It's good to know how to apply each.

I don't know how to use #2, 3, 5:

import SwiftUI

struct BindingInitStudy: View {
    @State private var optionalNumber: Int? = 0

    @State private var randomNumber = Int.random(in: 0...100)

    @State private var isBig = true

    var body: some View {
        VStack {
            Text("Demonstrate use cases of all the Binding.init's")
            if let n = optionalNumber {
                Text("Number: \(n)")
            } else {
                Text("Number: is nil")
            }
            // #1 `init?(_ base: Binding<Value?>)`
            // from binding to optional value to binding of value:
            if let bindingToInt = Binding($optionalNumber) {
                // number is not nil
                Stepper("Enter number", value: bindingToInt)
            }

            // #2: `init<V>(_ base: Binding<V>) where Value == V?`
            // Creates a binding by projecting the base value to an optional value
            // how to use?

            // #3: `init<V>(_ base: Binding<V>) where Value == AnyHashable, V : Hashable`
            // Creates a binding by projecting the base value to a hashable value.
            // how to use?

            // #4: `init(projectedValue: Binding<Value>)`
            // Creates a binding from the value of another binding
            // this is SE-0293 in action: it's using this Binding.init to create a @Binding property wrapper
            // to pass in function parameter that's a property wrapper
            Button("Random number: \(randomNumber)") {
                randomizeMe($binding: $randomNumber)
            }

            // #5 `init(get: @escaping () -> Value, set: @escaping (Value, Transaction) -> Void)`
            // how to use? what's Transaction?

            // #6: `init(get: @escaping () -> Value, set: @escaping (Value) -> Void)`
            // Manually create a Binding
            let binding = Binding(get: { randomNumber }, set: { randomNumber = $0; isBig = $0 > 50 })
            Button("Random number: isBig: \(isBig ? "Yes" : "No")") {
                randomizeMe($binding: binding)
            }

            // #7 static func constant(_ value: Value) -> Binding<Value>
            // Make a Binding to a constant value
            Toggle("You cannot change this", isOn: .constant(true))
        }
    }

    func randomizeMe(@Binding binding: Int) {
        binding = Int.random(in: 0...100)
    }
}

struct BindingInitStudy_Previews: PreviewProvider {
    static var previews: some View {
        BindingInitStudy()
    }
}

When figuring out how to invoke a function, it may be useful to check the generic types used by the declaration, and work out the type of each argument/return value:


  1. init<V>(_ base: Binding<V>) where Value == V?

If you look closely, you'll see that if V == Int, then Value == Int?. So if you have

let a = Binding<Int>

Then you can do

let b: Binding<Int?> = Binding(a)

It is adding and extra optional to the resulting Binding.


  1. init<V>(_ base: Binding<V>) where Value == AnyHashable, V : Hashable

This one is taking Binding<V> for any hashable V, and creates Binding<AnyHashable> (from Value == AnyHashable). It simply type-erase Binding<V> to Binding<AnyHashable>.


  1. init(get: @escaping () -> Value, set: @escaping (Value, Transaction) -> Void)

This one is a straightforward getter-setter pattern, with the only notable thing being the extra setter argument, Transaction. Transaction normally dictates how the transition between values is rendered. Whether it is rendered continuously (with interpolation) or instantly, what animation interpolation would it be using (.linear, .easeIn), etc.


Now, how getter & setter work with these newly created Binding, you can test it out. What if you set nil to one from 2) or set an invalid type to one from 3)?

1 Like

#2

    func assign(@Binding binding: Int?, with value: Int?) {
        binding = value
    }
...
            // #2: `init<V>(_ base: Binding<V>) where Value == V?`
            // Creates a binding by projecting the base value to an optional value
            // from Binding<Int> to Binding<Int?>, the reverse of #1
            Button("Increment randomNumber by 1") {
                assign($binding: Binding($randomNumber), with: randomNumber + 1)
            }
            Button("Set randomNumber nil") {
                assign($binding: Binding($randomNumber), with: nil)   // assign with nil seems to do nothing
            }

assigning nil seems to be no-op.

#3 still no able to see how it's applied from Hashable. Can you show me an example of this in terns of Hashable and then why it needs to be type erase?

#5 Haven't used Transaction for animation yet. Will go read up on this subject.

ok, I read up on Transaction. Still don't see how to make use of this .init(). But there is .transaction(_:) I can understand how to use this method.

Thanks!