Special Characters
*?+[(){}^$|\./
extension String {
func matchesPattern(pattern: String) -> Bool {
do {
let regex = try NSRegularExpression(pattern: pattern,
options: NSRegularExpressionOptions(rawValue: 0))
let range: NSRange = NSMakeRange(0, self.characters.count)
let matches = regex.matchesInString(self, options: NSMatchingOptions(), range: range)
return matches.count > 0
} catch _ {
return false
}
}
}
// very basic examples - check for specific strings
dump("Pinkman".matchesPattern("(White|Pinkman|Goodman|Schrader|Fring)"))
// using character groups to check for similar-sounding impressionist painters
dump("Monet".matchesPattern("(M[oa]net)"))
dump("Manet".matchesPattern("(M[oa]net)"))
dump("Money".matchesPattern("(M[oa]net)")) // false
// check surname is in list
dump("Skyler White".matchesPattern("\\w+ (White|Pinkman|Goodman|Schrader|Fring)"))
// check if string looks like a UK stock ticker
dump("VOD.L".matchesPattern("[A-Z]{2,3}\\.L"))
dump("BP.L".matchesPattern("[A-Z]{2,3}\\.L"))
// check entire string is printable ASCII characters
dump("tab\tformatted text".matchesPattern("^[\u{0020}-\u{007e}]*$"))
// Unicode example: check if string contains a playing card suit
dump("♠︎".matchesPattern("[\u{2660}-\u{2667}]"))
dump("♡".matchesPattern("[\u{2660}-\u{2667}]"))
dump("😂".matchesPattern("[\u{2660}-\u{2667}]")) // false
// NOTE: regex needs Unicode-escaped characters
dump("♣︎".matchesPattern("♣︎")) // does NOT work
Below is another example which builds on the above to do something useful, which can't easily be done by any other method and lends itself well to a regex solution.
// Pattern validation for a UK postcode.
// This simply checks that the format looks like a valid UK postcode and should not fail on false positives.
private func isPostcodeValid(postcode: String) -> Bool {
return postcode.matchesPattern("^[A-Z]{1,2}([0-9][A-Z]|[0-9]{1,2})\\s[0-9][A-Z]{2}")
}
// valid patterns (from https://en.wikipedia.org/wiki/Postcodes_in_the_United_Kingdom#Validation)
// will return true
dump(isPostcodeValid("EC1A 1BB"))
dump(isPostcodeValid("W1A 0AX"))
dump(isPostcodeValid("M1 1AE"))
dump(isPostcodeValid("B33 8TH"))
dump(isPostcodeValid("CR2 6XH"))
dump(isPostcodeValid("DN55 1PT"))
// some invalid patterns
// will return false
dump(isPostcodeValid("EC12A 1BB"))
dump(isPostcodeValid("CRB1 6XH"))
dump(isPostcodeValid("CR 6XH"))
There are several considerations when implementing Regular Expressions in Swift.
let letters = "abcdefg"
let pattern = "[a,b,c]"
let regEx = try NSRegularExpression(pattern: pattern, options: [])
let nsString = letters as NSString
let matches = regEx.matches(in: letters, options: [], range: NSMakeRange(0, nsString.length))
let output = matches.map {nsString.substring(with: $0.range)}
//output = ["a", "b", "c"]
In order to get an accurate range length that supports all character types the input string must be converted to a NSString.
For safety matching against a pattern should be enclosed in a do catch block to handle failure
let numbers = "121314"
let pattern = "1[2,3]"
do {
let regEx = try NSRegularExpression(pattern: pattern, options: [])
let nsString = numbers as NSString
let matches = regEx.matches(in: numbers, options: [], range: NSMakeRange(0, nsString.length))
let output = matches.map {nsString.substring(with: $0.range)}
output
} catch let error as NSError {
print("Matching failed")
}
//output = ["12", "13"]
Regular expression functionality is often put in an extension or helper to seperate concerns.
Patterns can be used to replace part of an input string.
The example below replaces the cent symbol with the dollar symbol.
var money = "¢¥€£$¥€£¢"
let pattern = "¢"
do {
let regEx = try NSRegularExpression (pattern: pattern, options: [])
let nsString = money as NSString
let range = NSMakeRange(0, nsString.length)
let correct$ = regEx.stringByReplacingMatches(in: money, options: .withTransparentBounds, range: range, withTemplate: "$")
} catch let error as NSError {
print("Matching failed")
}
//correct$ = "$¥€£$¥€£$"
To match special characters Double Backslash should be used
\. becomes \\.
Characters you'll have to escape include
(){}[]/\+*$>.|^?
The below example get three kinds of opening brackets
let specials = "(){}[]"
let pattern = "(\\(|\\{|\\[)"
do {
let regEx = try NSRegularExpression(pattern: pattern, options: [])
let nsString = specials as NSString
let matches = regEx.matches(in: specials, options: [], range: NSMakeRange(0, nsString.length))
let output = matches.map {nsString.substring(with: $0.range)}
} catch let error as NSError {
print("Matching failed")
}
//output = ["(", "{", "["]
Regular expressions can be used to validate inputs by counting the number of matches.
var validDate = false
let numbers = "35/12/2016"
let usPattern = "^(0[1-9]|1[012])[-/.](0[1-9]|[12][0-9]|3[01])[-/.](19|20)\\d\\d$"
let ukPattern = "^(0[1-9]|[12][0-9]|3[01])[-/](0[1-9]|1[012])[-/](19|20)\\d\\d$"
do {
let regEx = try NSRegularExpression(pattern: ukPattern, options: [])
let nsString = numbers as NSString
let matches = regEx.matches(in: numbers, options: [], range: NSMakeRange(0, nsString.length))
if matches.count > 0 {
validDate = true
}
validDate
} catch let error as NSError {
print("Matching failed")
}
//output = false
func isValidEmail(email: String) -> Bool {
let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"
let emailTest = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
return emailTest.evaluate(with: email)
}
or you could use String extension like this:
extension String
{
func isValidEmail() -> Bool {
let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"
let emailTest = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
return emailTest.evaluate(with: self)
}
}