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 ifs:
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()
}