Replies: 5 comments 6 replies
-
One thought is that property wrappers compose, so you should be able to introduce a new property wrapper for validation: @BindableState @Validate(.required) var oldPassword = "" Navigating through each layer may be a bit cumbersome, though, and/or require some more dynamic member lookup overloads. |
Beta Was this translation helpful? Give feedback.
-
Any examples how @BindableState can work together with @validated? How to access validated properties in swift body, via _ or $ .. |
Beta Was this translation helpful? Give feedback.
-
First of all, lots of great ideas here. A property wrapper like
|
Beta Was this translation helpful? Give feedback.
-
Hi can you please share ValidatableBinding protocol implementation |
Beta Was this translation helpful? Give feedback.
-
I know this discussion is older, but I realized I never shared what I came up with when I was thinking about this same thing. It has a caveat, though. I'm pretty sure I'm doing something we are told explicitly not to do--using an action to share some behavior with other actions. With a bit of extra thought and work, I think this could become a higher-order reducer, and with a bit more work maybe even be extrapolated out into a dependency to run the actual validation logic. Suppose I have a simple registration form for a booking. I have shortened it for brevity's sake: public struct BookingFeature: Reducer {
public struct State: Equatable {
@BindableState public var firstName: String = ""
@BindableState public var lastName: String = ""
@BindableState public var phoneNumber: String = ""
@BindableState public var email: String = ""
}
public struct FieldError: Equatable {
public var firstName: [ValidationError] = []
public var lastName: [ValidationError] = []
public var phoneNumber: [ValidationError] = []
public var email: [ValidationError] = []
var isEmpty: Bool {
[firstName, lastName, phoneNumber, email].allSatisfy { $0.isEmpty }
}
}
public enum Action: BindableAction, Equatable {
case binding(BindingAction<BookingFeature.State>)
case validate(PartialKeyPath<BookingFeature.State>)
case validateForm
case continueTapped
}
public var body: some Reducer<State, Action> {
BindingReducer()
Reduce { state, action in
switch action {
case let .binding(path):
return Effect(value: BookingFeature.Action.validate(path.keyPath))
.debounce(id: path.keyPath, for: 0.2, scheduler: mainQueue)
.animation()
case .validate(\.$firstName):
state.errors.firstName = state.firstName.validName()
return .none
//... pattern match on other keypaths we want to validate
case .continueTapped:
return .concatenate(
Effect.merge(
Effect(value: BookingFeature.Action.validate(\.$firstName),
// ... other fields
),
Effect(value: .validateForm)
)
}
}
}
} What this does is take advantage of our binding action to run additional logic, in this case a debounced effect that sends back a validation action on the keypath. We can then create an action that leverages a partial keypath, allowing us to run individual validation logic on the fields we are interested in. Finally, if a user attempts to continue along in the form, we can merge these actions and run validators against the entire form, then run logic to make sure the form is in a good state before continuing. There are some problems, though. Maybe the most prominent is that we're sending a The other primary issue is that we could be blocking the main thread with long-running effects, as some validation logic might not be trivial. This is where I think creating a dependency could be beneficial -- call out to the dependency sending the value and/or keypath you want validated, along with the ruleset it must pass. Then the dependency can run the validation logic and return an asynchronous effect that returns feedback to the user or system. |
Beta Was this translation helpful? Give feedback.
-
Hello, I'm currently toying around with some different ways to enhance the
@BindableState
property wrapper to accept a list of validation rules that can be run automatically any time the value changes. I have my idea working to some extent in a proof-of-concept and am hoping to get some ideas on how to handle validation rules that span multiple properties.My definition of
@BindableState
currently looks like this:This allows me to define properties and rules like this:
However, I'm having a bit of trouble trying to model how to attach a rule saying that the
newPassword
andconfirmNewPassword
properties need to match. Perhaps attaching this to the property wrapper is too limiting? It has been pretty nice in my testing though, as I can access the violated rules from the binding in the view which allows me to give feedback to the user:Thoughts?
Beta Was this translation helpful? Give feedback.
All reactions