Regular expressions are powerful, but here is pure Swift solution. Exact answer to your question:
func isValidUsername(_ name: String, forbiddenChars: String = "@#$%&*()^<>!±", lengthRange: Range<Int> = 3..<19) -> Bool { guard lengthRange ~= name.count , name.first!.isLetter else { return false } return name.allSatisfy{ !forbiddenChars.contains($0) } } isValidUsername("tom3") // true isValidUsername("tom_c") // false (note disallowed underscope) isValidUsername("3tom") // false isValidUsername("tom#$%&") // false
A common thing to want is to have only alphanumerics in a user name:
func alphanumericsOnly(_ name: String, lengthRange: Range<Int> = 3..<19) -> Bool { guard lengthRange ~= name.count , name.first!.isLetter else { return false } return name.allSatisfy{ $0.isLetter || $0.isNumber } }
Important thing to note here is that some emoji's and all diacritics count as alphanumerics which may be not what you want.
alphanumericsOnly("tom3️⃣") // true alphanumericsOnly("Ḡ͓̟̟r̬e̱̬͔͑g̰ͮ̃͛T̆a̐̑͢ṫ̀ǔ̓͟m̮̩̠̟") // true alphanumericsOnly("tom_") // false
So the most strict way for latin username can be checked as follows:
func asciiOnly<R: RangeExpression>(_ name: String, lengthRange: R) -> Bool where R.Bound == Int { guard lengthRange ~= name.count , name.first!.isLetter else { return false } return name.allSatisfy{ $0.isASCII } } asciiOnly("tom_c", lengthRange: 3...) // true (infinite length) asciiOnly("tom3️⃣", lengthRange: 3...18) // false asciiOnly("tomё", lengthRange: 3..<19) // false