14

How is email validation done with swiftUI?

TextField("Please enter your e-mail", text: self.$email)
    .modifier(ClearButton(text: $email))
    .font(.headline)
    .padding(10)
    .foregroundColor(.black)
    .background(Color.white)
    .frame(width: 300, height: 40, alignment: .center)
    .cornerRadius(20)

4 Answers 4

24

Validation can be done simply using onEditingChanged of any TextField.

Below code is for email validation, but it can be used for any other validation.

When user is done and exists the textfield, I validate the text and if it is not validated I remove the text with an error below it.

import SwiftUI
struct ContentView: View {

@State private var emailString  : String = ""
@State private var textEmail    : String = ""
@State private var isEmailValid : Bool   = true

var body: some View {
    VStack {
        TextField("email...", text: $textEmail, onEditingChanged: { (isChanged) in
            if !isChanged {
                if self.textFieldValidatorEmail(self.textEmail) {
                    self.isEmailValid = true
                } else {
                    self.isEmailValid = false
                    self.textEmail = ""
                }
            }
        })
            //.modifier(ClearButton(text: $email))
            .font(.headline)
            .padding(10)
            .foregroundColor(.black)
            .background(Color.white)
            .frame(width: 300, height: 40, alignment: .center)
            .cornerRadius(20)
            .autocapitalization(.none)

        if !self.isEmailValid {
            Text("Email is Not Valid")
                .font(.callout)
                .foregroundColor(Color.red)
        }
    }
}

func textFieldValidatorEmail(_ string: String) -> Bool {
    if string.count > 100 {
        return false
    }
    let emailFormat = "(?:[\\p{L}0-9!#$%\\&'*+/=?\\^_`{|}~-]+(?:\\.[\\p{L}0-9!#$%\\&'*+/=?\\^_`{|}" + "~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\" + "x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[\\p{L}0-9](?:[a-" + "z0-9-]*[\\p{L}0-9])?\\.)+[\\p{L}0-9](?:[\\p{L}0-9-]*[\\p{L}0-9])?|\\[(?:(?:25[0-5" + "]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-" + "9][0-9]?|[\\p{L}0-9-]*[\\p{L}0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21" + "-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])"
    //let emailFormat = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
    let emailPredicate = NSPredicate(format:"SELF MATCHES %@", emailFormat)
    return emailPredicate.evaluate(with: string)
}}

onEditingChanged: When user tap on this, isChanged = true, and when user exit and goes somewhere else, isChange = false.

Sign up to request clarification or add additional context in comments.

Comments

5

You need to use combine with SwiftUI for validation

Use TextFieldWithValidator to validate textField

import SwiftUI
import Combine


// MARK:  FIELD VALIDATION

@available(iOS 13, *)
public struct FieldChecker {

    public var errorMessage:String?

    public var valid:Bool {
         self.errorMessage == nil
     }
    public init( errorMessage:String? = nil ) {
        self.errorMessage = errorMessage
    }
}

@available(iOS 13, *)
public class FieldValidator<T> : ObservableObject where T : Hashable {
    public typealias Validator = (T) -> String?

    @Binding private var bindValue:T
    @Binding private var checker:FieldChecker

    @Published public var value:T
    {
        willSet {
            self.doValidate(newValue)
        }
        didSet {
            self.bindValue = self.value
        }
    }
    private let validator:Validator

    public var isValid:Bool {
        self.checker.valid
    }

    public var errorMessage:String? {
        self.checker.errorMessage
    }

    public init( _ value:Binding<T>, checker:Binding<FieldChecker>, validator:@escaping Validator  ) {
        self.validator = validator
        self._bindValue = value
        self.value = value.wrappedValue
        self._checker = checker
    }

    public func doValidate( _ newValue:T? = nil ) -> Void {

        self.checker.errorMessage =
                        (newValue != nil) ?
                            self.validator( newValue! ) :
                            self.validator( self.value )
    }
}


// MARK:  FORM FIELD

@available(iOS 13, *)
public struct TextFieldWithValidator : View {
    // specialize validator for TestField ( T = String )
    public typealias Validator = (String) -> String?

    var title:String?
    var onCommit:() -> Void

    @ObservedObject var field:FieldValidator<String>

    public init( title:String = "",
              value:Binding<String>,
              checker:Binding<FieldChecker>,
              onCommit: @escaping () -> Void,
              validator:@escaping Validator ) {
        self.title = title;
        self.field = FieldValidator(value, checker:checker, validator:validator )
        self.onCommit = onCommit
    }

    public init( title:String = "", value:Binding<String>, checker:Binding<FieldChecker>, validator:@escaping Validator ) {
        self.init( title:title, value:value, checker:checker, onCommit:{}, validator:validator)
    }

    public var body: some View {
        VStack {
            TextField( title ?? "", text: $field.value, onCommit: self.onCommit )
                .onAppear { // run validation on appear
                    self.field.doValidate()
                }
        }
    }
}

@available(iOS 13, *)
public struct SecureFieldWithValidator : View {
    // specialize validator for TestField ( T = String )
    public typealias Validator = (String) -> String?

    var title:String?
    var onCommit:() -> Void

    @ObservedObject var field:FieldValidator<String>

    public init( title:String = "",
              value:Binding<String>,
              checker:Binding<FieldChecker>,
              onCommit: @escaping () -> Void,
              validator:@escaping Validator ) {
        self.title = title;
        self.field = FieldValidator(value, checker:checker, validator:validator )
        self.onCommit = onCommit
    }

    public init( title:String = "", value:Binding<String>, checker:Binding<FieldChecker>, validator:@escaping Validator ) {
        self.init( title:title, value:value, checker:checker, onCommit:{}, validator:validator)
    }

    public var body: some View {
        VStack {
            SecureField( title ?? "", text: $field.value, onCommit: self.onCommit )
                .onAppear { // run validation on appear
                    self.field.doValidate()
                }
        }
    }
}

in your View

import SwiftUI
import Combine



class DataItem: ObservableObject { // observable object

    @Published var username:String = "" // observable property
}


struct FormWithValidator : View {

    @EnvironmentObject var item:DataItem // data model reference

    @State var usernameValid = FieldChecker() // validation state of username field

    func username() -> some View {
        VStack {
            TextFieldWithValidator( title: "username",
                                value: $item.username,
                                checker: $usernameValid,
                                onCommit: submit) { v in
                         // validation closure where ‘v’ is the current value

                            if( v.isEmpty ) {
                                return "username cannot be empty"
                            }

                            return nil
                    }
                    .padding(.all)
                    .border( usernameValid.valid ? Color.clear : Color.red )
                    .background(Color(red: 239.0/255.0, green: 243.0/255.0, blue: 244.0/255.0, opacity: 1.0))
                    .autocapitalization(.none)
            if( !usernameValid.valid  ) {
                Text( usernameValid.errorMessage ?? "" )
                    .fontWeight(.light)
                    .font(.footnote)
                    .foregroundColor(Color.red)
            }

        }

    }

    var isValid:Bool {
         usernameValid.valid
    }

    func submit() {
        if( isValid ) {
            print( "submit:\nusername:\(self.item.username)")
        }
    }

    var body: some View {

        NavigationView {
        Form {

            Section {
                username()

            }

            Section {

                Button( "Submit" ) {

                    self.submit()
                }
                    .disabled( !self.isValid )
            } // end of section

        } // end of form
           .navigationBarTitle( Text( "Sample Form" ), displayMode: .inline  )

        } // NavigationView
    }
}

#if DEBUG
struct FormVithValidator_Previews: PreviewProvider {
    static var previews: some View {
        FormWithValidator()
            .environmentObject( DataItem() )
    }
}
#endif

enter image description here

Credits and inspiration

Comments

4

In SwiftUI IOS 15.0 just add an extension which test the email using Regular Expression and use it on your code.

extension String {
    var isValidEmail: Bool {
        NSPredicate(format: "SELF MATCHES %@", "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}").evaluate(with: self)
    }
}

In your code:

TextField("Please enter your e-mail", text: $email)
    .textContentType(.emailAddress)       // !IMPORTANT FOR EMAILS
    .disableAutocorrection(true)          // !IMPORTANT FOR EMAILS
    .textInputAutocapitalization(.never)  // !IMPORTANT FOR EMAILS
    .foregroundColor(email.isValidEmail ? .white : .red) // <- Example of a validation here
    .background(email.isValidEmail ? .green : .black)    // <- Example of a validation here

Comments

0

Try this extension

    import Foundation

    extension String{
        var isValidEmail: Bool{
            let emailFormat = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
            let emailPredicate = NSPredicate(format: "SELF MATCHES %@", emailFormat)
            return emailPredicate.evaluate(with: self)
        }
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.