Develop a field validator for SwiftUI - episode 2
Recap
In the previous article the episode 1, we have started to implement a field validator trying to follow the philosophy and approach behind the SwiftUI the (no longer) new declarative framework proposed by Apple as a fast and homogeneous way to develop UI/UX on Apple Devices
Continuous Evolving & Learning
SwiftUI is in continuous evolution and then we have to continuous learning and during my deep dive in the SwiftUI articles and tutorials I came across this article “How to run some code when state changes using onChange()” where I’ve seen an example of Binding extension and precisely the follow one:
extension Binding {
func onChange(_ handler: @escaping (Value) -> Void) -> Binding<Value> {
Binding(
get: { self.wrappedValue },
set: { newValue in
self.wrappedValue = newValue
handler(newValue)
}
)
}
}
This code opened my mind to a new vision on “how to implement a field validation” in SwiftUI.
The Idea
The original FieldValidatorLibrary implementation relies on the FieldChecker
to hold validation status and FieldValidator
that, behind the scene, manages validation process. The original solution was also based on the custom Views TextFieldWithValidator
and SecureFieldWithValidator
needed to bind FieldChecker
with FieldValidator
.
The solution work well but the idea was to remove custom views in favour of the standard views removing as consequence also FieldValidator making the overall solution lean, more reusable and future proof
New Implementation
The new implementation is composed exclusively by class FieldChecker2
and a Binding
extension.
The FieldChecker2
is now an ObservableObject
and the Binding
extension contains just onValidate
method that accepts as arguments a FieldChecker2
object and a validation closure containing the validation logic.
This means that a classical field validation scenario become:
struct MyForm: View {
@State var username: String
// validation state of username
@StateObject var usernameCheck = FieldChecker2<String>()
TextField( "give me the username",
text: $username.onValidate(checker: usernameCheck) { v in
// validation closure where ‘v’ is the current value
if( v.isEmpty ) {
return "username cannot be empty"
}
return nil
})
.overlay( Text( usernameCheck.errorMessage ?? "") )
}
As you can see the solution is very easy and validation is directly applied on field (in the example username
) so it is completely decoupled by TextField
.
Conclusion
I’ve released version 1.5
of the FieldValidationLibrary containing this new implementation but keeping backward compatibility .
For more details and meaningful examples go to project on github
Happy coding and … enjoy SwiftUI