How to pass optional binding to a SwiftUI component
1 min readDec 21, 2022
When we have an optional field in a our model, the SwiftUI will give us the following exception:
Cannot convert value of type 'Binding<String?>' to expected
argument type 'Binding<String>'
Have a look at the following code. The description
is optional and TextField
does not accept Binding<String?>
but it expects Binding<String>
.
import SwiftUI
struct Book {
var name: String
var description: String?
}
class BookViewModel: ObservableObject {
@Published var book = Book(name: "")
}
struct ContentView: View {
@ObservedObject var vm: BookViewModel = BookViewModel()
var body: some View {
VStack {
TextField("", text: $vm.book.name)
TextField("", text: $vm.book.description)
}
.padding()
}
}
We can implement the following function that will create a new binding that makes sure there is always some value returned.
func ??<T>(lhs: Binding<Optional<T>>, rhs: T) -> Binding<T> {
Binding(
get: { lhs.wrappedValue ?? rhs },
set: { lhs.wrappedValue = $0 }
)
}
Then we can use ??
where we provide the optional binding on the left side and the default value on the right side.
struct ContentView: View {
@StateObject var vm: BookViewModel = BookViewModel()
var body: some View {
VStack {
TextField("", text: $vm.book.name)
TextField("", text: $vm.book.description ?? "")
}
.padding()
}
}