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!
Conclusion
This simple workaround can make our lives a bit easier.
If you enjoyed this article, please feel free to follow me on my social media: