I have a SwiftUI Form() that I use to enter inital data using TextField()s. I use the data to record into Core Data. I want to be able to re-edit the data using a Form() but cannot figure out how to preset the TextField()s.
How can I preset the TextField()s?
Ah, that little problem. Yeah, I've been bitten by that one. Happily, I worked out a nice little suite of Binding initializers to work around the issue in different ways.
I posted an article about it with a full description of the mechanics, but there are a few different problems, each with their own solution. Frequently these build on the built-in Binding initializer that unwraps an Optional value to return a binding to a non-nil value, or a nil binding:
/// Creates an instance by projecting the base optional value to its
/// unwrapped value, or returns `nil` if the base value is `nil`.
public init?(_ base: Binding<Value?>)
For when your value might be nil, and you want to assign a non-nil 'empty' value automatically (e.g. for a non-optional property on a newly-created CoreData object), here's a Binding initializer that initializes that for you:
extension Binding {
init(_ source: Binding<Value?>, _ defaultValue: Value) {
// Ensure a non-nil value in `source`.
if source.wrappedValue == nil {
source.wrappedValue = defaultValue
}
// Unsafe unwrap because *we* know it's non-nil now.
self.init(source)!
}
}
If you have a CoreData property that is optional, you might want to map nil to and from a given 'none' value, for instance an empty string. This will do that for you:
init(_ source: Binding<Value?>, replacingNilWith nilValue: Value) {
self.init(
get: { source.wrappedValue ?? nilValue },
set: { newValue in
if newValue == nilValue {
source.wrappedValue = nil
}
else {
source.wrappedValue = newValue
}
})
}
Using these two is fairly straightforward. Assuming we have an @ObservedObject referring to a CoreData object with non-optional "title: String?" and optional "notes: String?" attributes, we can now do this:
Form {
// Items should have titles, so use a 'default value' binding
TextField("Title", text: Binding($item.title, "New Item"))
// Notes are entirely optional, so use a 'replace nil' binding
TextField("Notes", text: Binding($item.notes, replacingNilWith: ""))
}