---
title: Regular Expressions in Swift
author: Guy Lapalme
description: Tutorial for regular expressions in Swift
exportdir: "http://www.iro.umontreal.ca/~lapalme/RegexInSwift"
---
Regular Expressions in Swift
Guy Lapalme
RALI-DIRO
Université de Montréal
October 2024
The Swift programming language’s regular expression notation is unique enough to warrant an explanation. While it adheres to most of the conventions found in languages such as PERL, Java, JavaScript, PHP and Python, it also has some significant differences that I found challenging to understand and had to figure out through trial and error. This document is the product of my efforts to understand and I am sharing it in the hope that it will help others.
I will first review what regular expressions are, and then demonstrate how they can be represented and used in Swift. I will use three examples to highlight some unique aspects of Swift’s regular expression implementation: parsing Roman numerals, a tokenizer, and an Eliza-like chatbot. The appendix includes links to additional resources and a handy cheat sheet.
This document serves as a personal reference for me, as I couldn’t find a comprehensive guide on Swift regular expressions. I have searched the internet for tutorials and videos on Swift regexes, but I have not found a clear definition of what they can be used for and why. Most examples I have seen are repetitive, so I have decided to develop my own that each illustrate a specific point.
[[HTML version](http://www.iro.umontreal.ca/~lapalme/RegexInSwift/index.html)] [[Markdown version](http://www.iro.umontreal.ca/~lapalme/RegexInSwift/RegexInSwift.md)] [[PDF version](http://www.iro.umontreal.ca/~lapalme/RegexInSwift//RegexInSwift.pdf)]
# What is a *regex* ?
In 1951, S. C. Kleene introduced the concept of a *regular expression*, also known as *regex*, as a formal language construct that describes the collection of strings formed through the operations of concatenation, alternation, and repetition, or quantification. This concept was later applied in the 1970s to define patterns for matching lines in a file. For instance, the Unix `grep` command, whose name derives from “global regular expression print”, searches for and displays lines that match a specified regular expression. This notation has proven to be extremely practical and effective for extracting data from extensive texts, as well as for defining modifications in text editors.
Most modern programming languages offer a regular expression syntax for extracting the string parts that match a regex, or for indicating failure if no match is found.
Swift is a strongly typed programming language, and its creators have taken great care to ensure that regular expressions and their outputs comply with type constraints. Swift’s regular expression syntax was introduced in 2022, along with Swift 5.7. Previous Swift versions relied on a port of the [NSRegularExpression](https://developer.apple.com/documentation/foundation/nsregularexpression) Objective-C library, which was less than ideal due to the complexity of bridging Objective-C’s `NSString` and Swift’s `String`. As a result, many people, including me, created custom functions for common tasks. However, these *new* regular expressions only work on iOS 16.0+ and MacOS 13.0+.
Swift’s syntax may be new, but it shares similarities with well-known languages like Perl, Java, JavaScript, PHP, and Python. Despite some difficulties, I have managed to understand the main aspects of the Swift regex API. This document summarizes my understanding, which I believe can benefit others.
We first briefly recall what is a regular expression to define the terms used in the rest of the document. In this section, we focus on the features that are common to all regex notations. Swift peculiarities will be detailed later.
## Components of Regex Definitions
A regular expression defines a *pattern* whose occurrences must be *matched* within a string, the *subject*. We follow the time-honored terminology of [SNOBOL4](http://www.regressive.org/snobol4/docs/burks/manual/contents.htm) which, in the sixties, was the first programming language to allow the definition of patterns as first-class objects for matching and operating on strings.
A *pattern* (a `RegexComponent` in the Swift parlance) is a combination of :
- `Character`: a letter (e.g., `a` or `w` ) or a period (`.`) which stands for any letter. It can also be specified by a set of characters within square brackets, such as `[abc]` or a range of characters, such as `[a-z]`for any English lowercase letter. A character set can be complemented by starting it with `^`. There are also predefined patterns, such as `\d` for matching a digit, `\w` for matching a character that can appear in a word (letter, digit or underline), `\W` for matching a *non-word* character (the set complement of `\w`) or `\s` for matching any space character (e.g., newline, tab, space, carriage return).
A character in a Swift string can be a *Unicode extended grapheme cluster* that can span more than one byte; so it must be retrieved using `String.Index` values and not by integer values on the byte representation. The [rules of character equality in Swift](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/stringsandcharacters/#String-and-Character-Equality) based on canonical equivalence also apply to regexes. For example, a character with a diacritic may be represented by a single Unicode character or by a base character followed by a *combining accent*. Therefore, the strings `"è"` and `"e\u{300}"` (*e* followed by a *combining grave accent*) will match `.` corresponding to any single character .
- *sequence* of patterns that the subject must match consecutively in the subject
- *alternation* between patterns denoted by a vertical bar (`|`) between patterns, alternation matches when one of the choices matches the subject.
- *position* : check if the subject matches on certain conditions: e.g.`^` matches only at the start of the string, `$` matches at the end of the string, `\b` matches at a word boundary, i.e. between a `\w` and a `\W`.
Sequence has priority over alternation, but parentheses can be used to change this ordering. For example, the pattern `abc|def|ghi` matches occurrences of `abc`, `def` or `ghi` , while `a(bc|de)f|ghi` matches occurrences of `abcf`, `adef` or `ghi` . But we will see later that parentheses are also used for delimiting parts of patterns that matches that are called *capture groups*.
A pattern can be repeated a certain number of times by adding a *quantifier* after it:
- `?` : [0,1]
- `*` : [0,+∞)
- `+` : [1,+∞)
- `{m,n}` : [m,n] `m` defaults to 0 and `n` to +∞
- `{m}` : [m,m]
By default, repetition of an indeterminate number of times is `eager` to find the longest match which is most often what is needed. In some cases, this strategy can lead to unexpected results. For example, given the subject string `"hello" my "friends"`, the pattern `".*"` will match once the whole subject, matching the quotes at the start and at the end of the string because the dot matches any character including a quote.
Repetition can be specified as either:
- `reluctant` by adding `?` after it. So `".*?"` will instead match the two quoted words, each match stopping as soon as a quote is encountered.
- `possessive` by adding `+` after it. *Possessive* matching finds the longest match without ever backtracking which is more efficient in some cases, but it can lead to surprising results. For example, `".*+"` fails on our subject string, because `.*` matches all following characters without ever reconsidering its choices, so it does not find the trailing quote, because it was already matched by the dot `.` . In this case, instead of the dot, we should match any character except a quote using `"[^"]*+"`. This pattern will now match the two quoted words.
When a regex must match a special character used for alternation or quantifier, this character must be escaped by a backslash, e.g., so matching `a` and `b` separated by `+` must be defined as `a\+b` otherwise it would match one or more `a` followed by a `b`.
Matching a character looks simple, but there can be variations. Should matching be case insensitive? Should diacritics be taken into account? For a multiline string, should position matches apply to each line ? Each language has its own way (usually *flags*) of specifying changes in matching behavior.
This very brief refresher on regex syntax is very far from complete, but it is adequate for explaining how regexes work in Swift. For a [more complete list of metacharacters and operators](#frequently-used-metacharacters).
## Result of the matching process
When applying a *pattern* to a *subject*, one of two things may happen:
- *Failure* : the pattern was not found on the subject. Failure returns `nil` in Swift (corresponding to `None` in Python or to `null` in Java or JavaScript). For old-timers: in SNOBOL4, control went to the instruction in the `:F(..)` *GOTO field*.
- *Success* : At least one occurrence of the pattern was found in the subject, so a *non-failure* value is an object with properties that provide information about the match. These include the start and end positions of the occurrence (`range` in Swift) or the substring itself (`output` in Swift). When a pattern contains *subpatterns* (called capture groups), the object provides access to them.
When a pattern appears more than once in a subject, a collection of successes can be returned, either as a list or as a generator that yields a match each time.
Given that regular expressions in most programming languages are defined using strings, errors or exceptions can occur at run-time if the regex string is not syntactically correct (e.g., unbalanced parentheses). Some compilers check the syntax of string literals in some special cases, but not in all of them. Swift goes to great lengths to limit the risk of such failures by applying static type checking to regexes as well. In other type-checked languages such as Java, regexes are plain strings whose peculiar structure is checked by a *compile* method, which is called at run-time.
# Regex in Swift
The matching process can be relatively slow because the pattern must be interpreted while scanning the subject. Most programming languages allow the *compilation* of a regex from the string to create an automaton for faster matching. A Swift regex is compiled by *default* to ensure that it is well formed and type-checked. Swift defines a `Regex` type used for type checking any expression involving regexes.
## Swift Regex Definition
Swift provides three notations for defining a regular expression:
- `Regex` literal enclosing the expression between two slashes, e.g. `/a(bc|de)f|ghi/` or `/".*?"/`. In order to avoid ambiguity with the single slash used for division, a regex cannot start with a space. A regex literal cannot be empty because `//` is used for line-ending comments; an empty regex would not be very useful anyway.
- `RegexBuilder` expression, a more verbose but more expressive for complex patterns. The first two columns of the following table show `RegexBuilder` expressions corresponding to our two previous examples. We will present this notation [in the next section](#RegexBuilder).
- *extended regex literal*, `#/.../#` which avoids the need to escape forward slashes within the regex. When the opening delimiter is followed by a new line, it defines a *multi-line literal* in which whitespace and line-ending comments starting with `#` are ignored. The third column of the next table shows how our second example can be written. This can be useful for documenting complex regular expressions.
| RegexBuilder | RegexBuilder | Extended literal |
| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| Regex {
ChoiceOf {
Regex {
"a"
ChoiceOf {
"bc" ; "de"
}
"f"
}
"ghi"
}
| Regex {
/"/
ZeroOrMore(.reluctant) {
/./
}
/"/
| #/
" # opening quote
.*? # up to next quote
" # ending quote
/#
|
We will later show how to define [run-time regular expressions](#dynamic-regex) using strings. In the remainder of this section, we use regex literals as the other notations are equivalent. We want to focus on the matching process, not on the syntax of the regular expressions.
### Regex Modifications
Changing some aspect of the matching behavior, obtained using *flags* in other programming languages, is performed by calling a `Regex` method that returns a new `Regex`:
- `.ignoresCase()` for case-insensitive matching, e.g. `/a(bc|de)f|ghi/.ignoresCase()`
- `.dotMatchesNewlines()` the any character (`.`) also matches an end of line.
## Swift Regex Operations
Regular expressions are used for identifying which parts of a subject correspond to the pattern. In some cases, it is enough to only check whether a pattern occurs in the subject, but more often it is necessary to get more information about the match.
Here are the definitions of a pattern and a subject used in the following examples of calls.
```swift
let identifier = /[A-Za-z]\w*/ // a letter, possibly followed by word characters (letter, digit or underline)
let subject = "Here are 10 tokens to be (matched) !"
```
### Checking for an Occurrence of a Pattern
The `String` methods `.contains(..)` and `.starts(with:..)` return a `Bool` (the value of each expression is shown here after `// => `)
```swift
subject.contains(identifier) // => true
"123 + 456".contains(identifier) // => false
subject.starts(with:pattern) // => true
"123 + 456".starts(with:identifier) // => false
```
### Looking in a Subject for Matches of a Pattern
#### Finding a single match
In Swift, the result of applying a pattern to a subject results in *match object* of *optional* type `Regex