Regular Expressions Tutorial: Getting Started

In this tutorial, you’ll learn how to implement regular expressions in an iOS app using Swift 4.2. By Tom Elliott.

Leave a rating/review
Download materials
Save for later
Share
Update note: Tom Elliott updated this tutorial for Swift 4.2. James Frost wrote the original.

Regular Expression Basics

If you haven’t heard of regular expressions — also called regex — before, it’s probably worth wrapping your head around the basics before continuing with this tutorial. Fortunately, we have you covered! Check out this Introduction to Regular Expressions tutorial here.

Implementing Regex in iOS

Now that you know the basics, it’s time to use regular expressions in an app.

Use the Download Materials button at the top or bottom of this tutorial to download the starter project. Open the iRegex starter project in Xcode and run it.

You’re going to build a diary app for your boss — a Super Villain! Everyone knows that Super Villains need to keep track of all their diabolical plans for world domination, right? There’s lots of planning to do and you, as the minion, are part of these plans — your part being to build the app for the other plans!

The UI for the app is mostly complete, but the core functionality of the app relies on regular expressions, which it doesn’t have… yet!

Your job in this tutorial is to add the required regular expressions into this app to make it shine (and hopefully avoid being thrown into a vat of molten hot magma).

Here are a few sample screen shots demonstrating the final product:

iRegex App Screenshots

The final app will cover two common use cases with regular expressions:

  1. Performing text search: highlighting, as well as search and replace.
  2. Validating user input.

You’ll start by implementing the most straightforward use of regular expressions: text search.

Implementing Search and Replace

Here’s the basic overview of the search-and-replace functionality of the app:

  • The Search view controller, SearchViewController has a read-only UITextView that contains an excerpt from your boss’ private diary.
  • The Navigation bar contains a Search button that will present SearchOptionsViewController modally.
  • This will allow your evil boss to type information into the field and tap “Search.”
  • The app will then dismiss the Search view and highlight all matches from the diary in the Text view.
  • If your boss selected the “Replace” option in SearchOptionsViewController, the app will perform a search-and-replace function for all matches in the text, instead of highlighting the results.
Note: Your app uses the NSAttributedString property of UITextView to highlight the search results.
You could also implement the highlighting functionality using Text Kit. Be sure to check out the Text Kit Tutorial in Swift to find out more.

There’s also a Reading Mode button that will allow highlighting all the dates, times and splitters between each entry in the diary. For simplicity’s sake, you won’t cover every possible format of date and time strings that can appear in the text. You’ll implement this highlighting functionality at the very end of the tutorial.

Your first step to getting the search functionality to work is to turn standard strings representing regular expressions into NSRegularExpression objects.

Open SearchOptionsViewController.swift. SearchViewController presents this view controller modally and allows the user to enter his or her search (and optional replace) terms, as well as specifying whether the search should be case sensitive or match only whole words.

Take a look at the SearchOptions struct at the top of the file. SearchOptions is a simple struct that encapsulates the user’s search options. The code passes an instance of SearchOptions back to SearchViewController. It would be good to be able to use this directly to construct an appropriate NSRegularExpression. You can do this by adding a custom initializer to NSRegularExpression with an extension.

Choose File ▸ New ▸ File… and choose Swift File. Name your file RegexHelpers.swift. Open the new file and add the following code:

extension NSRegularExpression {
  
  convenience init?(options: SearchOptions) throws {
    let searchString = options.searchString
    let isCaseSensitive = options.matchCase
    let isWholeWords = options.wholeWords
    
    let regexOption: NSRegularExpression.Options = 
      isCaseSensitive ? [] : .caseInsensitive
    
    let pattern = isWholeWords ? "\\b\(searchString)\\b" : searchString
    
    try self.init(pattern: pattern, options: regexOption)
  }
}

This code adds a convenience initializer to NSRegularExpression. It uses the various settings within the passed-in SearchOptions instance to configure things correctly.

Things to note:

  • Whenever the user requests a case-insensitive search, the regular expression uses the .caseInsensitive NSRegularExpressionOptions value. The default behavior of NSRegularExpression is to perform case-sensitive searches, but, in this case, you’re using the more user-friendly default of case-insensitive searches.
  • If the user requests a whole word search, then the app wraps the regular expression pattern in the \b character class. Recall that \b is the word boundary character class, so putting \b before and after the search pattern will turn it into a whole word search (that is, the pattern “\bcat\b” will match only the word “cat,” but not “catch”).

If, for any reason, it’s not possible to create the NSRegularExpression, then the initializer will fail and return nil. Now that you have the NSRegularExpression object, you can use it for matching text.

Open SearchViewController.swift, find searchForText(_:replaceWith:inTextView:), and add the following implementation to the empty method stub:

if let beforeText = textView.text, let searchOptions = self.searchOptions {
  let range = NSRange(beforeText.startIndex..., in: beforeText)
      
  if let regex = try? NSRegularExpression(options: searchOptions) {
    let afterText = regex?.stringByReplacingMatches(
      in: beforeText,
      options: [], 
      range: range, 
      withTemplate: replacementText
    )
    textView.text = afterText
  }
}

First, this method captures the current text in the UITextView and calculates the range of the entire string. It’s possible to apply a regular expression to just a part of your text, which is why you need to specify the range. In this case, you’re using the the entire string, which will result in the regular expression being applied to all of your text.

The real magic happens in the call to stringByReplacingMatches(in:options:range:withTemplate:). This method returns a new string without mutating the old string. Then the method sets the new string on the UITextView so that the user can see the results.

Still in SearchViewController, find highlightText(_:inTextView:) and add the following:

// 1
let attributedText = textView.attributedText.mutableCopy() as! NSMutableAttributedString
// 2
let attributedTextRange = NSMakeRange(0, attributedText.length)
attributedText.removeAttribute(
  NSAttributedString.Key.backgroundColor, 
  range: attributedTextRange)
// 3
if let searchOptions = self.searchOptions, 
   let regex = try? NSRegularExpression(options: searchOptions) {
  let range = NSRange(textView.text.startIndex..., in: textView.text)
  if let matches = regex?.matches(in: textView.text, options: [], range: range) {
    // 4
    for match in matches {
      let matchRange = match.range
      attributedText.addAttribute(
        NSAttributedString.Key.backgroundColor, 
        value: UIColor.yellow, 
        range: matchRange
      )
    }
  }
}

// 5
textView.attributedText = (attributedText.copy() as! NSAttributedString)

Here’s a step-by-step explanation of the code above:

  1. First, get a mutable copy of the textview’s attributedText.
  2. Then, create an NSRange for the entire length of the text and remove any background color text attributes that already exist within it.
  3. As with find and replace, create a regular expression using your convenience initializer and fetch an array of all matches for the regular expression within the textview’s text.
  4. Loop through each match and add a yellow color background attribute for each one.
  5. Finally, update the UITextView with the highlighted results.

Build and run your app. Try searching for various words and groups of words! You’ll see the search terms highlighted throughout your text, as shown in the image below:

iRegex text highlighting

Try searching for the word “the” using various options and see the effects. Notice, for example, that, when using whole words, the ‘the’ in ‘then’ does not highlight.

Also, test out the search-and-replace functionality to see that your text strings are replaced as expected. Also try both the ‘match case’ and ‘whole words’ options.

Highlighting and replacing text are both great. But how else can you effectively use regular expressions in your apps?