Tel Tel - 1 year ago 101
iOS Question

How to check in Swift if a Regular Expression is valid?

I'm using Regex to search in a textView. All works fine with simple strings but when I try to enter only a part of a regular expression operator I get a cash with the error:

Error Domain=NSCocoaErrorDomain Code=2048 "The value “\” is invalid." UserInfo={NSInvalidValue=\}


For example if I enter "\n" it works, but if I enter only "\" (without quotes), I get the crash. What I can't get to do is to find a way to check the expression before to use it.

I'm using this code partially based on this tutorial and this answer by Wiktor Stribiżew:

extension NSRegularExpression {
convenience init?(options: SearchOptions) {
let searchString = options.searchString
let isCaseSensitive = options.matchCase // set to true
let isWholeWords = options.wholeWords // set to false
let escapeMetacharacters = options.escapeMetacharacters // set to false

// handle case sensitive option
var regexOption: NSRegularExpressionOptions = .CaseInsensitive
if isCaseSensitive { // if it is match case remove case sensitive option
regexOption = []
}

// put the search string in the pattern
var pattern = searchString

if isWholeWords {
pattern = "(?<!\\w)" + NSRegularExpression.escapedPatternForString(searchString) + "(?!\\w)"
}

do {
try self.init(pattern: pattern, options: regexOption)
} catch {
print(error)
}
}
}

Answer Source

The first code below have shown unstable behaviour and you should not use it. (Please see the latter part of this answer.)

Add one line to your failable initializer:

        do {
            try self.init(pattern: pattern, options: regexOption)
        } catch {
            print(error)
            return nil //->you need to return nil to tell initialization failed
        }

(I think Swift compiler should warn about this missing return nil. May be a bug of Swift?)

After that you can safely check the result if it's nil or not:

let sOpts = SearchOptions(searchString: "\\", replacementString: "", matchCase: false, wholeWords: false)
if let regex = NSRegularExpression(options: sOpts) {
    //Use regex
    print(regex)
} else {
    //Process errors
    print("Something bad in SearchOptions")
}

(I omitted escapeMetacharacters, as it's not used yet.)


As far as I tested, using static method has never crashed.

extension NSRegularExpression {
    static func expresssionWith(options: SearchOptions) -> NSRegularExpression? {
        let searchString = options.searchString
        let isCaseSensitive = options.matchCase // set to true
        let isWholeWords = options.wholeWords // set to false

        // handle case sensitive option
        var regexOption: NSRegularExpressionOptions = .CaseInsensitive
        if isCaseSensitive { // if it is match case remove case sensitive option
            regexOption = []
        }

        // put the search string in the pattern
        var pattern = searchString

        if isWholeWords {
            pattern = "(?<!\\w)" + NSRegularExpression.escapedPatternForString(searchString) + "(?!\\w)"
        }

        do {
            return try NSRegularExpression(pattern: pattern, options: regexOption)
        } catch  {
            print(error)
            return nil
        }
    }
}

let sOpts = SearchOptions(searchString: "\\", replacementString: "", matchCase: false, wholeWords: false)

if let regex = NSRegularExpression.expresssionWith(sOpts) {
    //Use regex
    print(regex)
} else {
    //Process errors
    print("Something bad in SearchOptions")
}