0

I made a test code for inverted bool binding as following, including almost binding ways I can think of(except @Environment...).

struct TestView: View {
    @State private var isOn: Bool = false
    @State private var parameter: Parameter = .init()
    @StateObject private var vm = ViewModel()
    
    var body: some View {
        VStack {
            Toggle("isOff = \(isOn.description)", isOn: !$isOn)
            Toggle("isOff = \(parameter.isOn.description)", isOn: !$parameter.isOn)
            Toggle("isOff = \(vm.isOn.description)", isOn: !$vm.isOn)
            Toggle("isOff = \(vm.parameter.isOn.description)", isOn: !$vm.parameter.isOn)
            InnerView { isOn in
                Toggle("isOff = \(isOn.wrappedValue.description)", isOn: !isOn)
            } t2: { parameter in
                Toggle("isOff = \(parameter.wrappedValue.isOn.description)", isOn: !parameter.isOn)
            }
        }
        .padding()
    }
    
    @MainActor
    class ViewModel: ObservableObject {
        @Published var isOn: Bool = false
        @Published var parameter: Parameter = .init()
    }
    
    struct Parameter {
        var isOn: Bool = false
    }
    
    struct InnerView<T>: View where T: View {
        @State private var isOn: Bool = false
        @State private var parameter: Parameter = .init()
        @StateObject private var vm: ViewModel = .init()
        
        let t1: (Binding<Bool>) -> T
        let t2: (Binding<Parameter>) -> T
        
        var body: some View {
            VStack {
                t1($isOn)
                t2($parameter)
                t1($vm.isOn)
                t1($vm.parameter.isOn)
            }
        }
    }
}

Q1: here define the prefix operator ! in 2 ways: global and extension binding. which works? They don't conflict if put together, and if comment one and the other works fine.

prefix operator !

prefix func !(value: Binding<Bool>) -> Binding<Bool> {
    Binding<Bool>(
        get: { !value.wrappedValue },
        set: { value.wrappedValue = !$0 }
    )
}

extension Binding where Value == Bool {
    static prefix func !(value: Binding<Bool>) -> Binding<Bool> {
        Binding<Bool>(
        get: { !value.wrappedValue },
        set: { value.wrappedValue = !$0 }
    )
    }
}

if I comment the global one, and change extension one like following, strange things happened, compile success and run run crash.

//prefix operator !
//
//prefix func !(value: Binding<Bool>) -> Binding<Bool> {
//    Binding<Bool>(
//        get: { !value.wrappedValue },
//        set: { value.wrappedValue = !$0 }
//    )
//}

extension Binding where Value == Bool {
    static prefix func !(value: Binding<Bool>) -> Binding<Bool> {
        !value
    }
}

Q2: if I put the prefix operator ! definition into a Swift Package framework, some tests will compile failed as 'Cannot convert value of type 'Binding' to expected argument type 'Bool'. Cannot convert value of type 'Bool' to expected argument type 'Binding''. Is it a wrong way to define or some problems about Swift Package?

Q3: in 'Toggle("isOff = (vm.isOn.description)", isOn: !$vm.isOn)', $vm is a binding< ViewModel >, isON is Bool, is $vm.isON a binding< Bool >? I think it is. But if not, what's it? if not, the operator cannot be used on it, right? I'm kind of confused.

foolbear
  • 726
  • 1
  • 7
  • 19
  • For me it makes more sense to define an operator in an extension to the type it operates on. In general I think it’s best to avoid global declarations if possible. And you don’t need to declare the `!` operator since it already exists. – Joakim Danielson Mar 23 '23 at 08:28
  • But this global(and extension) with parameter Binding. not the Bool which is already exist. – foolbear Mar 23 '23 at 08:33
  • I was referring to the declaration `prefix operator !` only which isn’t needed. – Joakim Danielson Mar 23 '23 at 08:43
  • we don't use view model objects in SwiftUI, that's not what StateObject is designed for – malhal Mar 23 '23 at 17:26

0 Answers0