String verification in textfield while typing in SwiftUI

Need to check the following pattern when a user type in textfield of SwiftUI : ABCDE1234F that is first five letters, followed by four numbers and last again a letter. I can check this after editing is complete by using func like given below:

func isValidPAN(_ pan:String)->Bool{

let panRegex = "^[A-Z]{5}[0-9]{4}[A-Z]{1}"
let panTest = NSPredicate(format: "SELF MATCHES %@", panRegex)
return panTest.evaluate(with: pan)
}

In view,

   TextField(proEdit.panNo,text: $editData.panNO)
        .disableAutocorrection(true)
        .autocapitalization(.allCharacters)
        .onChange(of: editData.panNO, perform: {
            
            editData.panNO = String($0.prefix(10))
            let valid = isValidPAN(editData.panNO)
            
            if valid{
                correct.toggle()
            }

This is working fine but what I want to do is user can only enter as per the required format that is first a letter, second letter and so on if first char is not letter it should not type like in the given below eg:

   TextField(proEdit.beneficiaryName, text:$editData.benefName)
        .onChange(of: editData.benefName, perform: {char in
           
            let filterChar = 
     editData.benefName.filter{$0.isLetter || $0.isWhitespace}
            if editData.benefName != filterChar{
                editData.benefName = filterChar
                                }
            })

In the above, user can only type letter or whitespace

If you do it onChange, the current typing will not match the Regex.

Note: The naming of var is a bit misleading. It is clearer with:

   TextField(proEdit.beneficiaryName, text:$editData.benefName)
        .onChange(of: editData.benefName, perform: { newName in
            let filterName =  editData.benefName.filter{$0.isLetter || $0.isWhitespace}
            if editData.benefName != filterName {
                editData.benefName = filterName
             }
          })

So, you could do this:

  • Add a State var with previousName
    @State var previousName = ""
  • Modify onChange closure
                .onChange(of: editData.benefName, perform: { newName in
                    if isValidPAN(newName) {
                        editData.benefName = newName
                        self.previousName = newName
                    } else {
                        editData.benefName = previousName
                    }
                })

Modify isValidPAN. Regex as defined cannot work.

  • You could have something more complex:
func isValidPAN(_ pan:String) -> Bool {

    let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    let upperLetters = Array(alphabet)

    for i in 0...4 where i <= pan.count {
        if let char = pan[i] {
            if !upperLetters.contains(char) { return false }
        }
    }
    if pan.count <= 5 { return true }
    
    // now pan has more than 5 chars
    let digits = "0123456789"
    let digitLetters = Array(digits)
    for i in 5...8 where i <= pan.count {
        if let char = pan[i] {
            if !digitLetters.contains(char) { return false }
        }
    }
    if pan.count <= 9 { return true }

    // now pan has more than 9 chars
        if let char = pan[9] {
            if !upperLetters.contains(char) { return false }
        }

    if pan.count > 10 { return false }
    
    return true
}
  • You need an extension to String:
extension String {
    subscript (i: Int) -> Character? {
        if i < 0 || i >= self.count {
            return nil
        }
        return self[self.index(self.startIndex, offsetBy: i)]
    }
}
String verification in textfield while typing in SwiftUI
 
 
Q