Home > Net >  how to refactor the onEditingChanged code so that the protocol can be used as a reusable code in Swi
how to refactor the onEditingChanged code so that the protocol can be used as a reusable code in Swi

Time:09-23

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 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()
}
  • Related