Codementor Events

Validate form submission

Published May 06, 2019Last updated Nov 01, 2019

Introduction

I am sure you ever implemented login, register form at least once in your life. What did you do?

Below is some basic steps for register flow:

  • Validate email: empty, invalid
  • Validate password: empty, too short password, some situations, you need check it contains at least 1 uppercase character or other conditions.
  • Submit form

I did that many times and found validating in controller is not the good way. I found a better way to do that. Share you today.

This is definitely not the best practice to validate form submission, but much better than the method I told you above. Let's see how it work

Download the starter project at FormValidator.

Very simple UI with email, password, phone number.

How it works

Create new file RegisterValidator.swift and enter code below.

extension RegisterController {
    struct Validator { } 
}

We create Validator struct inside the RegisterController. We also can do that for LoginController. It's not conflict together. Same name in different namespace.

1. Define your properties

struct Validator {
    var email: String?
    var password: String?
    var phone: String?
    mutating func setValue(email: String?, password: String?, phone: String?) {
        self.email = email
        self.password = password
        self.phone = phone
    }
}

You can add more properties like your requirement, sometimes need to have first name, last name or address.

2. Validate your properties

func check() -> ValidatorResult {
    guard let email = email else {
        return ValidatorResult(isValid: false, message: "Email can't be empty")
    }

    if email.isEmpty == true {
        return ValidatorResult(isValid: false, message: "Email can't be empty")
    }

    if email.isValidEmail() == false {
        return ValidatorResult(isValid: false, message: "Invalid email")
    }

    guard let password = password else {
        return ValidatorResult(isValid: false, message: "Password can't be empty")
    }

    if password.isEmpty == true {
        return ValidatorResult(isValid: false, message: "Password can't be empty")
    }

    if password.count < 6 {
        return ValidatorResult(isValid: false, message: "Password must have at least 6 characters")
    }

    guard let phone = phone else {
        return ValidatorResult(isValid: false, message: "Phone can't be empty")
    }

    if phone.isEmpty == true {
        return ValidatorResult(isValid: false, message: "Phone can't be empty")
    }

    return ValidatorResult(isValid: true, message: nil)
}

We validate every factor of every property, and return correct error to user. For instance, you can tell user empty email error or invalid email or password length.

Clear enough? I think yes. You can check every requirement here, add or remove conditions quite easily with this way.

3. ValidatorResult

We need something to return to users, you can add more convenience things in ValidatorResult. I use the basic thing only.

struct ValidatorResult {
    var isValid = true
    var message: String?
}

4. Setup controller

@IBAction func registerAccount(_ sender: Any) {
    validator.setValue(email: emailTextField.text,
                       password: passwordTextField.text,
                       phone: phoneTextField.text)
    let result = validator.check()
    if result.isValid == false, let message = result.message {
        showMessage(message: message)
        return
    }

    callRegisterApi(email: validator.email!,
                    password: validator.password!,
                    phone: validator.phone!)
}

func showMessage(message: String) {
    let controller = UIAlertController(title: "", message: message, preferredStyle: .alert)
    controller.addAction(UIAlertAction(title: "OK", style: .default))
    present(controller, animated: true)
    return
}

We can put exclamation point - ! at validator.email, validator.password here, because they're not nil, sure.

Much better when we check result by switch...case. Your code is cleaner and good for complicated projects. (Thanks Bishal Ghimire for suggestion).

5. Run and see

We need an extension to check email validity.

extension String {
    func isValidEmail() -> Bool {
        let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"

        let emailTest = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
        return emailTest.evaluate(with: self)
    }
}

Ready to run.

You can download the completed project here.

Improvement

What do you think we can make better here? Is it better if focus on the error textfield? Or change the error textfield background color? Or shake it?

Yes, it will be much better. Add some code and make it done.

This post is too long now. You can check branch improvement to see how to improve it. Download at improvement.

Conclusion

The most convenience thing of this way is, you can reuse Validator everywhere you want, change properties and conditions a bit. Hope it can help you in coding.

There are lots of way to do validation. Please share everyone what you are doing below. Share to grow.

Enjoy coding.

Discover and read more posts from Ky Nguyen
get started
post comments3Replies
bishal ghimire
6 years ago

Nice & explained in a very simple manner.
I would like to suggest some small changes

if result.isValid == false, let message = result.message {
showMessage(message: message)
return
}

Rather than using the IF check, the switch would make this code cleaner. As in real production app, we have to show the error message on UILabels with colors property and hidden properties, etc, it would make code clean if we can refactor to Switch Case.

Ky Nguyen
6 years ago

Thanks for suggestion. I updated the post.

ronald ruck
6 years ago

Amazing job. Thanks for sharing.