Tel Tel - 4 months ago 38
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

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")
}