I recently came across this extention to Binding and I am having trouble understanding how it works.
extension Binding where Value == Bool {
init<T>(value: Binding<T?>) {
self.init {
value.wrappedValue != nil
}
set: { newValue in
if !newValue {
value.wrappedValue = nil
}
}
}
}
Its purpose I understand. It’s designed to enable you to use a Binding for an Optional value as the trigger for .alert(...) or .sheet(...).
For example:
struct ExampleView : View {
@State var message: String? = nil
@State var showIt: Bool = false
var body: some View {
Button("Show Message") {
message = "Some kind of message"
}.alert("Alert!", isPresented: Binding(value: $message)) { // <---- used here
Text(message!)
}
}
}
I’ve never seen the use of self.init { ... } inside of an initializer. I’m also confused by the use of set: { ... } in the initializer. Please help me understand these language semantics.
>Solution :
The initialiser in the extension must call an existing initialiser, or assign something to self. How else is it going to initialiser a Binding, after all?
The initialiser declared in the extension is calling this existing initialiser of Binding, which takes closure parameters called get and set. Note that set isn’t part of some special syntax – it’s just a parameter name.
The call to self.init is using the trailing closure syntax, described here in the language reference. This syntax allows you to omit the parameter label of the first parameter. I have an answer here that goes into more detail.
You see this syntax everywhere in SwiftUI, whenever you initialise views, like:
Button {
// do something
} label: {
Text("Foo")
}
You are calling the Button.init that takes an action and a label. Notice that the action: parameter label is omitted.
Finally, while this extension is useful for alerts, sheet has a version that takes in an item: Binding<Item?>, so you don’t need to use this extension for sheets.