Greetings, traveler!
In SwiftUI, there is a common way of presenting modals. Check out this example:
struct ContentView: View {
@State private var text: String?
@State private var isPresenting = false
var body: some View {
Button("Press me!") {
text = .greeting
isPresenting = true
}
.alert(isPresented: $isPresenting) {
Alert(title: Text(text ?? ""))
}
}
}
extension String? {
static var greeting: Self {
[
"Good morning!",
"Good afternoon!",
"Good evening!",
"Good night!"
].randomElement()
}
}While it seems a convenient way, we need to handle two variables. We must toggle the isPresented value and set the new text to the alert. Moreover, we must assign a nil value to the text after the alert presentation. Something like that:
.onChange(of: isPresenting) { _, newValue in
guard !newValue else { return }
text = nil
}Looks a bit weird, isn’t it? To fix this issue, we can create an extension to the optional Binding, which will accept nil as false. We must take care of setting a new value only if it is false because we can’t use true to set a new value.
extension Binding where Value == Bool {
init<Wrapped: Sendable>(mapped: Binding<Wrapped?>) {
self.init(
get: {
mapped.wrappedValue != nil
},
set: { newValue in
guard !newValue else { return }
mapped.wrappedValue = nil
}
)
}
}
extension Binding {
func boolValue<Wrapped: Sendable>() -> Binding<Bool> where Value == Wrapped? {
Binding<Bool>(mapped: self)
}
}Now, we can use it.
struct ContentView: View {
@State private var text: String?
var body: some View {
Button("Press me!") {
text = .greeting
}
.alert(isPresented: $text.boolValue()) {
Alert(title: Text(text ?? ""))
}
}
}Nice!
Hidden redraws
SwiftUI distinguishes between bindings created through key paths and bindings constructed manually with Binding(get:set:). The difference matters for view diffing. A key-path binding such as $state keeps a stable reference to the underlying value, which allows SwiftUI to compare view inputs efficiently. A manual binding stores closures instead. Every time the parent view recomputes its body, new closures are created, and SwiftUI has no reliable way to compare them. The framework may therefore assume the binding changed and trigger additional body evaluations in child views.
When predictable diffing and rendering efficiency matter more, a simpler structure usually works better: keep a dedicated Bool that controls presentation and observe it directly. The code becomes slightly more explicit, but the binding remains key-path based and SwiftUI can reason about it without guessing.
Conclusion
This does not mean manual bindings should never be used. In small presentation helpers the trade-off may be acceptable, and this simple workaround can make our lives a bit easier.
