Complex Password Validation in SwiftUI

Hello, I have built a complex password validation in SwiftUI. This is just for learning and practicing but I thought it worth sharing here as you can modify this according to your needs by only changing the regex.

So for this validation tool, I considered these:

  • Password length must be 8 characters or more.
  • Must contain at least 1 lowercase letter.
  • Must contain at least 1 uppercase letter.
  • One numeric digit at least.
  • At least one special character.

So I will just make a simple password field (SecureField) and then I will add a button to validate the password.

When the user enters the password and clicks on validate it will show what kind of validation error occurred.

Password validator in SwiftUI

Let’s first create the validation system without showing the exact validation error(s). But it will make the button green if password is validated perfectly.

import SwiftUI

struct ContentView: View {
    @State private var password = ""
    @State private var passwordIsValid = false
    @State private var errorMessage = ""

    var body: some View {
        Form {
            Section(header: Text("Password")) {
                SecureField("Enter your password", text: $password)
                    .textContentType(.password)
            }

            Section {
                Button("Validate Password") {
                    validatePassword()
                }
                .foregroundColor(.white)
                .frame(maxWidth: .infinity)
                .padding()
                .background(passwordIsValid ? Color.green : Color.gray)
                .cornerRadius(10)
            }

            if !passwordIsValid && !errorMessage.isEmpty {
                Section(header: Text("Validation Error")) {
                    Text(errorMessage)
                        .foregroundColor(.red)
                        .multilineTextAlignment(.center)
                }
            }
        }
        .navigationBarTitle("Password Validation")
    }

    private func validatePassword() {
        let passwordPattern = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$"
        let passwordPredicate = NSPredicate(format: "SELF MATCHES %@", passwordPattern)

        if !passwordPredicate.evaluate(with: password) {
            errorMessage = "Password must be at least 8 characters long and include at least 1 lowercase letter, 1 uppercase letter, 1 numeric digit, and 1 special character."
        } else {
            errorMessage = ""
            passwordIsValid = true
        }
    }
}

The main regex part is ^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$

password validation in SwiftUI

 

Show specific reason: password validation error in SwiftUI

Now I am going to make some changes to my code. I will show the exact reasons for the validation error as Text.

import SwiftUI

struct ContentView: View {
    @State private var password = ""
    @State private var passwordIsValid = false
    @State private var errorMessages: [String] = []

    var body: some View {
        Form {
            Section(header: Text("Password")) {
                SecureField("Enter your password", text: $password)
                    .textContentType(.password)
            }

            Section {
                Button("Validate Password") {
                    validatePassword()
                }
                .foregroundColor(.white)
                .frame(maxWidth: .infinity)
                .padding()
                .background(passwordIsValid ? Color.green : Color.gray)
                .cornerRadius(10)
            }

            if !passwordIsValid && !errorMessages.isEmpty {
                Section(header: Text("Validation Errors")) {
                    ForEach(errorMessages, id: \.self) { errorMessage in
                        Text(errorMessage)
                            .foregroundColor(.red)
                            .multilineTextAlignment(.center)
                    }
                }
            }
        }
        .navigationBarTitle("Password Validation")
    }

    private func validatePassword() {
        var errors: [String] = []

        if password.count < 8 {
            errors.append("Password must be at least 8 characters long.")
        }

        if !password.contains(where: { $0.isLowercase }) {
            errors.append("Password must include at least 1 lowercase letter.")
        }

        if !password.contains(where: { $0.isUppercase }) {
            errors.append("Password must include at least 1 uppercase letter.")
        }

        if !password.contains(where: { $0.isNumber }) {
            errors.append("Password must include at least 1 numeric digit.")
        }

        if !password.contains(where: { "!@#$%^&*()_-+=[{]};:'\",<.>/?".contains($0) }) {
            errors.append("Password must include at least 1 special character.")
        }

        errorMessages = errors
        passwordIsValid = errors.isEmpty
    }
}

Output:

Pssword validation in SwiftUI

By seeing the output you must be thinking that you are actually unable to see what I am typing in the box. So I will modify my code again and show how to add the show password option to it.

I have also created a whole SwiftUI login form with show-hide password option. You can check it: Login Form in SwiftUI

Password validation with show – hide option in SwiftUI

I have added this button:

Button(action: {

                        showPassword.toggle()

                    }) {

                        Image(systemName: showPassword ? "eye.slash.fill" : "eye.fill")

                            .foregroundColor(.gray)

                    }

The final full code will be like this:

import SwiftUI

struct ContentView: View {
    @State private var password = ""
    @State private var passwordIsValid = false
    @State private var errorMessages: [String] = []
    @State private var showPassword = false

    var body: some View {
        Form {
            Section(header: Text("Password")) {
                HStack {
                    if showPassword {
                        TextField("Enter your password", text: $password)
                            .textContentType(.password)
                    } else {
                        SecureField("Enter your password", text: $password)
                            .textContentType(.password)
                    }
                    
                    Button(action: {
                        showPassword.toggle()
                    }) {
                        Image(systemName: showPassword ? "eye.slash.fill" : "eye.fill")
                            .foregroundColor(.gray)
                    }
                }
            }

            Section {
                Button("Validate Password") {
                    validatePassword()
                }
                .foregroundColor(.white)
                .frame(maxWidth: .infinity)
                .padding()
                .background(passwordIsValid ? Color.green : Color.gray)
                .cornerRadius(10)
            }

            if !passwordIsValid && !errorMessages.isEmpty {
                Section(header: Text("Validation Errors")) {
                    ForEach(errorMessages, id: \.self) { errorMessage in
                        Text(errorMessage)
                            .foregroundColor(.red)
                            .multilineTextAlignment(.center)
                    }
                }
            }
        }
        .navigationBarTitle("Password Validation")
    }

    private func validatePassword() {
        var errors: [String] = []

        if password.count < 8 {
            errors.append("Password must be at least 8 characters long.")
        }

        if !password.contains(where: { $0.isLowercase }) {
            errors.append("Password must include at least 1 lowercase letter.")
        }

        if !password.contains(where: { $0.isUppercase }) {
            errors.append("Password must include at least 1 uppercase letter.")
        }

        if !password.contains(where: { $0.isNumber }) {
            errors.append("Password must include at least 1 numeric digit.")
        }

        if !password.contains(where: { "!@#$%^&*()_-+=[{]};:'\",<.>/?".contains($0) }) {
            errors.append("Password must include at least 1 special character.")
        }

        errorMessages = errors
        passwordIsValid = errors.isEmpty
    }
}

Output:

SwiftUI final password validation

Leave a Reply

Your email address will not be published. Required fields are marked *