Hey I saw a code for email validation in swift that uses onEditingChanged protocol as follows:-
TextField("Email address", text: $formModel.textEmail, onEditingChanged: { (isChanged) in
if !isChanged {
if formModel.textFieldValidatorEmail(formModel.textEmail) {
formModel.isEmailValid = true
} else {
formModel.isEmailValid = false
formModel.textEmail = ""
}
}
})
if !formModel.isEmailValid {
InvalidEmailView()
}
I want to refactor this code in such a manner that the protocol onEditingChanged can be reused.
Can anyone please help me with it.
CodePudding user response:
If I understand your question correctly, you mean "closure" rather than "protocol". In any case, to reuse the logic, you can just create a function that takes the same parameters as the closure, plus parameters for things the closure captures. In your example, it captures formModel
.
Assuming formModel
is of type FormModel
, and that it is a value type rather than a reference type, it would look something like this:
func handleEditChanged(_ isChanged: Bool, formModel: inout FormModel)
{
if !isChanged {
if formModel.textFieldValidatorEmail(formModel.textEmail) {
formModel.isEmailValid = true
} else {
formModel.isEmailValid = false
formModel.textEmail = ""
}
}
}
Of course, readability is another reason to refactor, and to that end, you could clean up the nested if
s:
func handleEditChanged(_ isChanged: Bool, formModel: inout FormModel)
{
guard !isChanged else { return }
formModel.isEmailValid =
formModel.textFieldValidatorEmail(formModel.textEmail)
if !formModel.isEmailValid {
formModel.textEmail = ""
}
}
If FormModel
is code under your control, and textEmail
should be an empty string any time isEmailValid
is false
, you could move setting textEmail
to a property observer. In FormModel
var isEmailValid: Bool {
didSet { if !isEmailValid { textEmail = "" } }
}
Then handleEditChanged
is just
func handleEditChanged(_ isChanged: Bool, formModel: inout FormModel)
{
guard !isChanged else { return }
formModel.isEmailValid =
formModel.textFieldValidatorEmail(formModel.textEmail)
}
Obviously substitute your own real type for FormModel
. If it's actually a reference (ie. class
) then you don't need the inout
specification in the parameter list, and below where I show how to use it, you don't need the &
.
Then to use it, you still provide a closure to TextEdit
, but you call your function. It looks like this, passing it using trailing closure syntax rather than as an explicit parameter.
TextField("Email address", text: $formModel.textEmail) {
handleEditChanged($0, formModel: &formModel)
}
if !formModel.isEmailValid {
InvalidEmailView()
}