Custom Binding with SwiftUI

A couple of days ago I was looking at some SwiftUI questions and saw one from a fellow trying to store the value of a picker into some Core Data. I thought I would share my solution here. Two caveats:

  1. This is for SwiftUI 1.0 - the person asking the question was using SwiftUI 2.0 (in beta as of this writing), but I think my solution will still work.

  2. This solution is not something uniquely mine - you will see other people mention it in their posts and writing. I thought I would highlight it specifically.

Here’s the problem: you want to use a component (DatePicker in this case) which transfers the value selected by the user via a Binding. If you are coming from UIKit thinking, your instinct is to use a delegate. Very understandable but that isn’t going to work for SwiftUI. The way to use the picker is:

private @State var selectedDate = Date()

DatePicker(
selection: self.$selectedDate,
displayedComponents: [.date]) {

EmptyView()
}.labelsHidden()

When the user changes any of the wheels of the DatePicker the value of selectedDate is immediately changed.

The question asked was: How can the value (selectedDate) be stored into Core Data? More generically, how do you react to this choice made by the user?

If you wanted the UI to change when the user picked a date, you can set that up easily using an IF statement and SwiftUI will detect that selectedDate changed, run the body code again, execute your IF statement, and produce a new View which it with then use to change the screen. That’s pretty straightforward. But what if you want to do something non-UI with the value when its changed?

The solution is to use a custom binding. Let’s keep selectedDate as the backing store variable so it can be used elsewhere and add a new var which reacts to the DatePicker changing its selection in a custom way:

private var handleBinding: Binding<Date> {
Binding(
get: { self.selectedDate },
set: { (newValue) in
self.selectedDate = newValue
// do something with newValue, like call another function
// or store it with Core Data or fetch some remote data

})}

and then put this to use:

DatePicker(
selection: self.handleBinding,
displayedComponents: [.date]) {

EmptyView()
}.labelsHidden()

The @State variable that was used in the binding with the DatePicker is replaced with the custom binding, handleBinding. It works exactly the same, except when the DatePicker’s selected date changes, the handleBinding set function is called, setting the @State variable and then doing whatever else you want it to do.

You can of course, use custom binding with your own components. If you component has a @Binding var in it, you can pass a custom binding. This will give you more control over the flow of data in your SwiftUI app, just don’t abuse it and write sloppy code. Use it only when you need to and try to keep your code clean and readable.

Previous
Previous

Adventures with React Native

Next
Next

GIT Merge & Squash