Teo Sartori
Teo Sartori

Reputation: 1112

Init of SwiftUI View with @Binding optional property seems ambiguous

Given my assumptions, from the code below, that:

then why does B(a: Binding<Bool?>) reject (correctly) the first with "Cannot convert value of type 'Binding<Bool>' to expected argument type 'Binding<Bool?>'" but accept the second?

import SwiftUI

struct ContentView: View {
    @State var a: Bool = false

    var body: some View {
        A(a: $a)
    }
}

struct A : View {
    @Binding var a: Bool

    var body: some View {
            //B(a: $a)
            // ^~~~ 
            // Fails with "Cannot convert value of type 'Binding<Bool>' to expected argument type 'Binding<Bool?>'"       
            B(a: Binding.constant(self.a))
    }
}

struct B : View {
    @Binding var a: Bool?

    var body: some View {

        if a == nil {
            return Text("a is nil")
        } else {
            return Text("a is \(a! ? "true" : "false")")
        }
    }
}

Upvotes: 1

Views: 3298

Answers (1)

marcprux
marcprux

Reputation: 10355

What would you expect to happen to the contents of the non-optional Bool held by A.a if someone sets the derived optional Bool binding in B.b to nil? Your workaround of wrapping the Bool in a .constant works because it prevents that possibility (since a constant binding cannot be changed).

You could alternatively create a derived binding that simply ignores setting nil values, like so (note the under-documented "_" prefix for assigning an underlying property wrapper value):

extension B {
    init(reqA: Binding<Bool>) {
        self._a = Binding<Bool?>(get: { reqA.wrappedValue },
          set: { if let newValue = $0 { reqA.wrappedValue = newValue } })
    }
}

Upvotes: 1

Related Questions