A dot matched the whole e plus combining acute accent in Swift Regex, but NSRegularExpression returned two matches on the same Mac. That is the kind of difference I want to check before moving a pattern between APIs.

This atlas keeps 34 cases with their exact inputs, operations, options, captures, and UTF-16 ranges. Some cases agree. A shared result for one input does not establish engine parity.

Tested on one Mac

Dataset 1.0.0, schema 1, recorded October 16, 2026. The accepted run checked 68 engine observations across 34 cases.

Component Executed environment
macOS 27.0, build 26A428
Architecture arm64
Swift compiler Apple Swift 6.4, swiftlang-6.4.0.34.1, clang-2100.3.34.1
Xcode 27.0, build 27A266a
macOS SDK 27.0, build 26A425
Libraries Dynamically linked macOS system Swift and Foundation
Compilation arm64-apple-macosx14.2, default -Onone

Only this OS/build, architecture, and toolchain were executed. The macOS 14.2 compilation target is not a test on macOS 14.2. These cases do not measure speed or establish results on another macOS version, Intel, Linux, Wasm, JavaScript, or PCRE.

Read the results

match includes zero-length matches. noMatch means compilation succeeded and the operation returned no match. compileError means the pattern was rejected before matching. runtimeError keeps the error channel of a throwing operation separate; none of the 34 accepted string-pattern cases produced an engine runtime error.

Capture 0 is the full match. An absent optional capture has null text and range. A participating empty capture has "" text and a zero-length range. Ranges below use [offset, length] in UTF-16 code units. They do not count Swift Character values. Returned text preserves the input’s scalars.

matching is a source-reviewed mapping to Regex 2.5.0’s search inputs and i/s/m controls, not an app UI test. engineOnly marks standalone first-match, whole-input, scalar-mode, replacement, and empty-pattern probes. Counts are engine matches; the app’s selected-capture count follows participating ranges.

Compare the cases

Open a case for its copyable Swift string values, exact APIs, requested options, captures, and primary sources. The summary lists outcomes on the recorded environment only.

CaseSwiftFoundationApp scope
Inspect year and month captures1 match1 matchmatching
An optional capture can be absent2 matches2 matchesmatching
An empty capture still has a range1 match1 matchmatching
A repeated group retains its final capture1 match1 matchmatching
A noncapturing group adds no capture slot1 match1 matchmatching
Search accepts the first successful alternative1 match1 matchmatching
A backreference repeats captured text1 match1 matchmatching
Dot counts graphemes in Swift by default1 match2 matchesmatching
A flag emoji contains two regional indicators1 match2 matchesmatching
A composed pattern matches decomposed text in Swift1 matchnoMatchmatching
Swift scalar mode changes dot consumption2 matches2 matchesengineOnly
A CRLF pair can be one character-class member1 match2 matchesmatching
Backslash R preserves a CRLF pair2 matches2 matchesmatching
Backslash d accepts Arabic-Indic digits2 matches2 matchesmatching
An ASCII range excludes Arabic-Indic digits1 match1 matchmatching
Backslash w includes an accented letter1 match1 matchmatching
Case-insensitive matching includes accented letters1 match1 matchmatching
Dot excludes a line feed by defaultnoMatchnoMatchmatching
Dot-all permits a line feed1 match1 matchmatching
Multiline anchors can select an interior line1 match1 matchmatching
Finding digits does not validate the whole inputnoMatchnoMatchengineOnly
Whole-input matching can try a longer alternative1 match1 matchengineOnly
First-match search extracts a substring1 match1 matchengineOnly
Lookahead checks following context without consuming it1 match1 matchmatching
Test this lookbehind against the recorded runtimecompileError1 matchmatching
Capture the amount after a dollar sign1 match1 matchmatching
An unclosed group is a compilation errorcompileErrorcompileErrormatching
A nonexistent capture reference is rejectedcompileErrorcompileErrormatching
A possessive quantifier does not give a letter backnoMatchnoMatchmatching
Replace with a literal dollar expression1 match1 matchengineOnly
The same replacement string has different API meanings1 match1 matchengineOnly
Build a replacement from captured fields1 match1 matchengineOnly
A zero-width match has a participating empty range2 matches2 matchesmatching
An empty engine pattern can match boundaries3 matchescompileErrorengineOnly
Inspect year and month capturesnamed-date-captures

Group 0 is the full match; numbered groups retain the named year and month text.

let pattern = "(?<year>\\d{4})-(?<month>\\d{2})"
let input = "on 2026-10"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
swift-type, foundation

Swift: match (1 match).

  • Match 1 — group 0: "2026-10", [3,7]; group 1: "2026", [3,4]; group 2: "10", [8,2].

Foundation: match (1 match).

  • Match 1 — group 0: "2026-10", [3,7]; group 1: "2026", [3,4]; group 2: "10", [8,2].
An optional capture can be absentoptional-absent-capture

The first match has no group 1 range. Selecting group 1 in the app shows one participating range across these two matches.

let pattern = "(a)?b"
let input = "b ab"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
swift-type, foundation

Swift: match (2 matches).

  • Match 1 — group 0: "b", [0,1]; group 1: null, null.
  • Match 2 — group 0: "ab", [2,2]; group 1: "a", [2,1].

Foundation: match (2 matches).

  • Match 1 — group 0: "b", [0,1]; group 1: null, null.
  • Match 2 — group 0: "ab", [2,2]; group 1: "a", [2,1].
An empty capture still has a rangeempty-participating-capture

Group 1 participates with zero length, which differs from an absent optional capture.

let pattern = "(a*)b"
let input = "b"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
swift-type, foundation

Swift: match (1 match).

  • Match 1 — group 0: "b", [0,1]; group 1: "", [0,0].

Foundation: match (1 match).

  • Match 1 — group 0: "b", [0,1]; group 1: "", [0,0].
A repeated group retains its final capturerepeated-capture

The overall match contains three letters; group 1 refers to the final letter.

let pattern = "(a)+"
let input = "aaa"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
swift-type, foundation

Swift: match (1 match).

  • Match 1 — group 0: "aaa", [0,3]; group 1: "a", [2,1].

Foundation: match (1 match).

  • Match 1 — group 0: "aaa", [0,3]; group 1: "a", [2,1].
A noncapturing group adds no capture slotnoncapturing-prefix

Only the trailing c becomes group 1.

let pattern = "(?:ab)(c)"
let input = "abc"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
swift-type, foundation

Swift: match (1 match).

  • Match 1 — group 0: "abc", [0,3]; group 1: "c", [2,1].

Foundation: match (1 match).

  • Match 1 — group 0: "abc", [0,3]; group 1: "c", [2,1].
Search accepts the first successful alternativeordered-alternation

A search for cat or cater accepts cat here. Whole-input matching has a separate fixture.

let pattern = "(cat|cater)"
let input = "cater"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
swift-type, foundation

Swift: match (1 match).

  • Match 1 — group 0: "cat", [0,3]; group 1: "cat", [0,3].

Foundation: match (1 match).

  • Match 1 — group 0: "cat", [0,3]; group 1: "cat", [0,3].
A backreference repeats captured textnumeric-backreference

Group 1 supplies the text required after the hyphen.

let pattern = "(\\w+)-\\1"
let input = "go-go"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
swift-type, foundation

Swift: match (1 match).

  • Match 1 — group 0: "go-go", [0,5]; group 1: "go", [0,2].

Foundation: match (1 match).

  • Match 1 — group 0: "go-go", [0,5]; group 1: "go", [0,2].
Dot counts graphemes in Swift by defaultdot-decomposed-accent

Swift consumes e plus combining acute together. Foundation returns two scalar matches; both report UTF-16 ranges.

let pattern = "."
let input = "e\u{301}"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Version-sensitive fixture; observation limited to the recorded environment.
Primary source IDs
unicode, foundation

Swift: match (1 match).

  • Match 1 — group 0: "e\u{301}", [0,2].

Foundation: match (2 matches).

  • Match 1 — group 0: "e", [0,1].
  • Match 2 — group 0: "\u{301}", [1,1].
A flag emoji contains two regional indicatorsdot-flag

Swift returns one flag match. Foundation dot returns two regional-indicator matches, each spanning two UTF-16 units.

let pattern = "."
let input = "\u{1f1f8}\u{1f1ec}"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Version-sensitive fixture; observation limited to the recorded environment.
Primary source IDs
unicode, foundation

Swift: match (1 match).

  • Match 1 — group 0: "\u{1f1f8}\u{1f1ec}", [0,4].

Foundation: match (2 matches).

  • Match 1 — group 0: "\u{1f1f8}", [0,2].
  • Match 2 — group 0: "\u{1f1ec}", [2,2].
A composed pattern matches decomposed text in Swiftcanonical-equivalence

The returned text preserves the input scalars. Foundation does not match this composed pattern against decomposed input.

let pattern = "\u{e9}"
let input = "e\u{301}"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Version-sensitive fixture; observation limited to the recorded environment.
Primary source IDs
unicode, foundation

Swift: match (1 match).

  • Match 1 — group 0: "e\u{301}", [0,2].

Foundation: noMatch.

Swift scalar mode changes dot consumptionscalar-dot

Swift matchingSemantics(.unicodeScalar) returns separate e and accent matches, agreeing with Foundation for this input. The app has no scalar-mode control.

let pattern = "."
let input = "e\u{301}"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
unicodeScalar
App scope
engineOnly — standalone API case; no app recipe.
Version label
Version-sensitive fixture; observation limited to the recorded environment.
Primary source IDs
unicode, foundation

Swift: match (2 matches).

  • Match 1 — group 0: "e", [0,1].
  • Match 2 — group 0: "\u{301}", [1,1].

Foundation: match (2 matches).

  • Match 1 — group 0: "e", [0,1].
  • Match 2 — group 0: "\u{301}", [1,1].
A CRLF pair can be one character-class membercrlf-character-class

This pattern spells CR then LF within the class. The observation applies to this pattern and runtime; compare the newline-sequence fixture.

let pattern = "[\r\n]"
let input = "\r\n"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Version-sensitive fixture; observation limited to the recorded environment.
Primary source IDs
unicode, foundation

Swift: match (1 match).

  • Match 1 — group 0: "\r\n", [0,2].

Foundation: match (2 matches).

  • Match 1 — group 0: "\r", [0,1].
  • Match 2 — group 0: "\n", [1,1].
Backslash R preserves a CRLF pairnewline-sequences

Both APIs find two newline sequences and keep the CRLF pair together.

let pattern = "\\R"
let input = "a\r\nb\n"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Version-sensitive fixture; observation limited to the recorded environment.
Primary source IDs
unicode, foundation

Swift: match (2 matches).

  • Match 1 — group 0: "\r\n", [1,2].
  • Match 2 — group 0: "\n", [4,1].

Foundation: match (2 matches).

  • Match 1 — group 0: "\r\n", [1,2].
  • Match 2 — group 0: "\n", [4,1].
Backslash d accepts Arabic-Indic digitsunicode-decimal-digits

Both APIs accept the Arabic-Indic and ASCII digit runs.

let pattern = "\\d+"
let input = "\u{663}\u{664} 42"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Version-sensitive fixture; observation limited to the recorded environment.
Primary source IDs
unicode, foundation

Swift: match (2 matches).

  • Match 1 — group 0: "\u{663}\u{664}", [0,2].
  • Match 2 — group 0: "42", [3,2].

Foundation: match (2 matches).

  • Match 1 — group 0: "\u{663}\u{664}", [0,2].
  • Match 2 — group 0: "42", [3,2].
An ASCII range excludes Arabic-Indic digitsascii-digit-contract

Use the ASCII range when the format requires ASCII digits; searching still extracts the later ASCII run.

let pattern = "[0-9]+"
let input = "\u{663}\u{664} 42"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Version-sensitive fixture; observation limited to the recorded environment.
Primary source IDs
unicode, foundation

Swift: match (1 match).

  • Match 1 — group 0: "42", [3,2].

Foundation: match (1 match).

  • Match 1 — group 0: "42", [3,2].
Backslash w includes an accented letterunicode-word

Both APIs match café as one word run on this runtime.

let pattern = "\\w+"
let input = "caf\u{e9}"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Version-sensitive fixture; observation limited to the recorded environment.
Primary source IDs
unicode, foundation

Swift: match (1 match).

  • Match 1 — group 0: "caf\u{e9}", [0,4].

Foundation: match (1 match).

  • Match 1 — group 0: "caf\u{e9}", [0,4].
Case-insensitive matching includes accented lettersunicode-ignore-case

The option matches CAFÉ while preserving the input text in the capture.

let pattern = "caf\u{e9}"
let input = "CAF\u{c9}"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
caseInsensitive
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Version-sensitive fixture; observation limited to the recorded environment.
Primary source IDs
unicode, foundation

Swift: match (1 match).

  • Match 1 — group 0: "CAF\u{c9}", [0,4].

Foundation: match (1 match).

  • Match 1 — group 0: "CAF\u{c9}", [0,4].
Dot excludes a line feed by defaultdot-excludes-newline

Neither API matches a newline with dot under the default options.

let pattern = "a.b"
let input = "a\nb"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
swift-type, foundation

Swift: noMatch.

Foundation: noMatch.

Dot-all permits a line feeddot-all

The Swift dotMatchesNewlines and Foundation dotMatchesLineSeparators options both permit this match.

let pattern = "a.b"
let input = "a\nb"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
dotAll
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
unicode, foundation

Swift: match (1 match).

  • Match 1 — group 0: "a\nb", [0,3].

Foundation: match (1 match).

  • Match 1 — group 0: "a\nb", [0,3].
Multiline anchors can select an interior linemultiline-anchors

The multiline option changes the anchors, allowing the middle b line to match.

let pattern = "^b$"
let input = "a\nb\nc"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
multiline
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
unicode, foundation

Swift: match (1 match).

  • Match 1 — group 0: "b", [2,1].

Foundation: match (1 match).

  • Match 1 — group 0: "b", [2,1].
Finding digits does not validate the whole inputwhole-token-rejects-extraction

Whole-input matching rejects a digit run with a preceding ID label. Foundation uses a noncapturing wrapper with absolute start/end anchors, recorded in apis.

let pattern = "\\d+"
let input = "ID:42"
Operation and APIs
whole. Swift: Regex.wholeMatch(in:). Foundation: NSRegularExpression.matches with \A(?:pattern)\z.
Requested options
None; engine defaults.
App scope
engineOnly — standalone API case; no app recipe.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
algorithms, foundation

Swift: noMatch.

Foundation: noMatch.

Whole-input matching can try a longer alternativewhole-token-backtracks-alternative

Both whole-input operations select ab, even though a first search could accept a.

let pattern = "(a|ab)"
let input = "ab"
Operation and APIs
whole. Swift: Regex.wholeMatch(in:). Foundation: NSRegularExpression.matches with \A(?:pattern)\z.
Requested options
None; engine defaults.
App scope
engineOnly — standalone API case; no app recipe.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
algorithms, foundation

Swift: match (1 match).

  • Match 1 — group 0: "ab", [0,2]; group 1: "ab", [0,2].

Foundation: match (1 match).

  • Match 1 — group 0: "ab", [0,2]; group 1: "ab", [0,2].
First-match search extracts a substringfirst-extracts-digits

Both first-match APIs return 42 and leave the surrounding text outside the match.

let pattern = "\\d+"
let input = "ID:42,7"
Operation and APIs
first. Swift: Regex.firstMatch(in:). Foundation: NSRegularExpression.firstMatch(in:range:).
Requested options
None; engine defaults.
App scope
engineOnly — standalone API case; no app recipe.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
algorithms, foundation

Swift: match (1 match).

  • Match 1 — group 0: "42", [3,2].

Foundation: match (1 match).

  • Match 1 — group 0: "42", [3,2].
Lookahead checks following context without consuming itlookahead-context

The match contains the amount; the following space and USD remain outside its range.

let pattern = "\\d+(?= USD)"
let input = "42 USD"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
swift-type, foundation

Swift: match (1 match).

  • Match 1 — group 0: "42", [0,2].

Foundation: match (1 match).

  • Match 1 — group 0: "42", [0,2].
Test this lookbehind against the recorded runtimelookbehind-local-support

On the recorded environment Swift rejects this pattern during compilation; Foundation matches 42. Retest this pattern when using another Swift version.

let pattern = "(?<=\\$)\\d+"
let input = "cost $42"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Version-sensitive fixture; observation limited to the recorded environment.
Primary source IDs
swift-type, foundation

Swift: compileError.

Recorded diagnostic: lookbehind is not currently supported.

Foundation: match (1 match).

  • Match 1 — group 0: "42", [6,2].
Capture the amount after a dollar signcapture-context-alternative

Both APIs match the dollar sign and amount, with only the amount in group 1. In the app, choose capture group 1 to inspect the amount.

let pattern = "\\$(\\d+)"
let input = "cost $42"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
swift-type, foundation

Swift: match (1 match).

  • Match 1 — group 0: "$42", [5,3]; group 1: "42", [6,2].

Foundation: match (1 match).

  • Match 1 — group 0: "$42", [5,3]; group 1: "42", [6,2].
An unclosed group is a compilation errormalformed-group

Neither API reaches matching. A compilation error must not become an empty successful match list.

let pattern = "("
let input = "abc"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
swift-type, foundation

Swift: compileError.

Recorded diagnostic: expected ')'.

Foundation: compileError.

Recorded diagnostic: Error Domain=NSCocoaErrorDomain Code=2048 "The value “(” is invalid." UserInfo={NSInvalidValue=(}.

A nonexistent capture reference is rejectedinvalid-backreference

Both APIs reject the reference to group 2 in this pattern on the recorded environment.

let pattern = "(a)\\2"
let input = "aa"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Version-sensitive fixture; observation limited to the recorded environment.
Primary source IDs
swift-type, foundation

Swift: compileError.

Recorded diagnostic: no capture numbered 2.

Foundation: compileError.

Recorded diagnostic: Error Domain=NSCocoaErrorDomain Code=2048 "The value “(a)\2” is invalid." UserInfo={NSInvalidValue=(a)\2}.

A possessive quantifier does not give a letter backpossessive-quantifier

The possessive repetition consumes the available letters, so the final a cannot match.

let pattern = "a*+a"
let input = "aaa"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
swift-type, foundation

Swift: noMatch.

Foundation: noMatch.

Replace with a literal dollar expressionliteral-replacement

Swift inserts $1 literally. Foundation receives escapedTemplate(for:) so its replacement is also literal. This does not call the app replacement parser.

let pattern = "(\\w+)"
let input = "cat"
let replacement = "$1"
Operation and APIs
replaceLiteral. Swift: String.replacing(_:with:) literal. Foundation: NSRegularExpression.stringByReplacingMatches + escapedTemplate.
Requested options
None; engine defaults.
App scope
engineOnly — standalone API case; no app recipe.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
algorithms, foundation

Swift: match (1 match).

  • Match 1 — group 0: "cat", [0,3]; group 1: "cat", [0,3].

Replacement output: "$1".

Foundation: match (1 match).

  • Match 1 — group 0: "cat", [0,3]; group 1: "cat", [0,3].

Replacement output: "$1".

The same replacement string has different API meaningsreplacement-api-syntax

Swift replacing with a string inserts $1 literally; Foundation withTemplate expands group 1. This compares replacement APIs, not different regex matching results or the app parser.

let pattern = "(\\w+)"
let input = "cat"
let replacement = "$1"
Operation and APIs
replaceAPISyntax. Swift: String.replacing(_:with:) literal. Foundation: NSRegularExpression.stringByReplacingMatches template.
Requested options
None; engine defaults.
App scope
engineOnly — standalone API case; no app recipe.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
algorithms, foundation

Swift: match (1 match).

  • Match 1 — group 0: "cat", [0,3]; group 1: "cat", [0,3].

Replacement output: "$1".

Foundation: match (1 match).

  • Match 1 — group 0: "cat", [0,3]; group 1: "cat", [0,3].

Replacement output: "cat".

Build a replacement from captured fieldscapture-replacement

The Swift closure joins capture 2, a space, and capture 1. Foundation uses $2 $1. The fixture names both recipes; the app has its own template parser.

let pattern = "(\\w+), (\\w+)"
let input = "Lovelace, Ada"
Operation and APIs
replaceCaptures. Swift: String.replacing(_:with:) closure: capture 2 + space + capture 1. Foundation: NSRegularExpression.stringByReplacingMatches template: $2 $1.
Requested options
None; engine defaults.
App scope
engineOnly — standalone API case; no app recipe.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
algorithms, foundation

Swift: match (1 match).

  • Match 1 — group 0: "Lovelace, Ada", [0,13]; group 1: "Lovelace", [0,8]; group 2: "Ada", [10,3].

Replacement output: "Ada Lovelace".

Foundation: match (1 match).

  • Match 1 — group 0: "Lovelace, Ada", [0,13]; group 1: "Lovelace", [0,8]; group 2: "Ada", [10,3].

Replacement output: "Ada Lovelace".

A zero-width match has a participating empty rangezero-width-lookahead

Both APIs return two empty group 0 ranges before the letters. No characters are consumed by the lookahead.

let pattern = "(?=a)"
let input = "aa"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
matching — source-reviewed match-extraction inputs; no app UI test.
Version label
Recorded environment only; no cross-version test.
Primary source IDs
swift-type, foundation

Swift: match (2 matches).

  • Match 1 — group 0: "", [0,0].
  • Match 2 — group 0: "", [1,0].

Foundation: match (2 matches).

  • Match 1 — group 0: "", [0,0].
  • Match 2 — group 0: "", [1,0].
An empty engine pattern can match boundariesempty-engine-pattern

Swift matches three boundaries on this runtime; Foundation rejects the empty pattern during compilation. The app handles an empty pattern as an empty workspace state before compiling it.

let pattern = ""
let input = "ab"
Operation and APIs
all. Swift: String.matches(of:). Foundation: NSRegularExpression.matches(in:range:).
Requested options
None; engine defaults.
App scope
engineOnly — standalone API case; no app recipe.
Version label
Version-sensitive fixture; observation limited to the recorded environment.
Primary source IDs
swift-type, foundation

Swift: match (3 matches).

  • Match 1 — group 0: "", [0,0].
  • Match 2 — group 0: "", [1,0].
  • Match 3 — group 0: "", [2,0].

Foundation: compileError.

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

Swift Regex lookbehind on this runtime

For (?<=\$)\d+ against cost $42, the recorded Swift runtime returned compileError with the diagnostic lookbehind is not currently supported. Foundation matched 42 at UTF-16 range [6, 2]. This is the dated lookbehind-local-support case; retest the pattern on another installed runtime.

SE-0448 describes the lookbehind proposal. Proposal status and implementation on a particular runtime answer different questions.

For the amount after a dollar sign, I can consume the dollar sign and capture the digits:

let input = "cost $42"
let regex = try Regex(#"\$(\d+)"#)
for match in input.matches(of: regex) {
    print(match.output[1].substring ?? "")
}

The accepted capture-context-alternative case returns $42 as capture 0 and 42 as capture 1 in both APIs. Selecting capture 1 gives the amount, but the full match still consumes the dollar sign. This changes the match range and is not a general replacement for lookbehind.

Searching and validating use different operations

The first-extracts-digits case finds 42 in ID:42,7. The whole-token-rejects-extraction case rejects ID:42 as a whole-input match for \d+.

Swift’s whole-input cases use throwing Regex.wholeMatch(in:). Foundation uses the pattern wrapper \A(?:pattern)\z, with its absolute start and end anchors. That wrapper allows alternation to backtrack and consume the whole input. For (a|ab), both whole-input operations select ab in whole-token-backtracks-alternative. Checking the length of a first search match would answer a different question.

Replacement strings belong to their API

With (\w+) against cat, both APIs capture the same text. Passing $1 to Swift’s string replacement inserts the literal $1; passing $1 to Foundation’s template API inserts cat. See replacement-api-syntax.

For literal replacement, the accepted Foundation recipe uses NSRegularExpression.escapedTemplate(for:). For capture-based replacement, the Swift recipe uses a closure that joins capture 2, a space, and capture 1; Foundation uses $2 $1. Both produce Ada Lovelace from Lovelace, Ada in capture-replacement.

These are standalone API recipes. Regex for macOS has its own replacement parser, which expands $0–$8 and $$. A result from Swift’s literal replacement API does not describe that parser.

Reproduce a case

The cases come from the Regex-owned Tools/regex-atlas/ source at revision 71402a7. Fixtures are in fixtures.v1.json. Accepted output in data/macos-27.0-swift-6.4.v1.json records the environment, input fingerprints, exact APIs, observations, and checks. Stable case IDs connect the fixture and output records.

With that source checkout, Bun, and an installed Xcode command-line toolchain, run from the repository root:

bun Tools/regex-atlas/run.ts \
  --recorded-on 2026-10-16 \
  --case dot-decomposed-accent \
  --output /tmp/swift-regex-atlas-accent

Use a fresh, empty output directory. Replace the case ID to run another example, or omit --case for the corpus. atlas.json contains the report. Exit 0 means the selected expectations passed, 1 means a semantic mismatch, and 2 means an input, build, or execution problem. A later run needs its own recording date and separate output; it does not silently update the accepted expectations.

The two string declarations in each case expose the exact pattern and input without relying on visible Unicode rendering. \u{301} is a combining acute accent, \r is carriage return, and \n is line feed. Backslashes in a Swift source string are escaped separately from regex syntax. Use each case’s recorded operation and options with the Swift APIs or Foundation API.

Keep the same inputs in Regex

For capture-context-alternative, paste \$(\d+) into Regex’s pattern field and cost $42 into Test Data. Leave all three matching options off. Capture 0 is $42; select capture 1 to inspect 42. Save As a named test, then duplicate it before trying a variant.

Saved Tests keeps the pattern, Test Data, replacement, selected capture group, and matching options together on this Mac. It stores a workspace snapshot. It does not store Atlas expectations or run assertions.

Regex 2.5.0 uses Swift Regex and searches for matches. It does not switch to Foundation, import the Atlas dataset, open a case from a link, expose a scalar-mode control, or perform these standalone whole-input and replacement API probes. The app handles an empty pattern before compilation and exposes capture selection 0–8. The matching labels above describe source-reviewed inputs, not newly executed UI behavior.

Regex 2.5.0 is a one-time US$13.99 purchase in the US storefront and requires macOS 14.2 or later. The app’s minimum OS is separate from the Atlas’s executed environment.

Test your own case on your Mac with Regex.

For the surrounding workflow, see Swift Regex Builder and Regex Tester for macOS. Run the final pattern in its destination engine before using it in JavaScript, grep, sed, nginx, or another tool.

Sources

Case source IDs refer to SE-0350: Regex type overview, SE-0363: Unicode for string processing, SE-0357: Regex-powered string processing algorithms, and the NSRegularExpression reference. The documents describe the APIs and matching model; the dataset records these particular observations.