| name | swift-regex-builder |
| description | Swift Regex builder DSL for type-safe pattern matching, captures, quantifiers, and Foundation parsers. Use when building complex regex patterns in Swift. |
First step: Tell the user: "swift-regex-builder skill loaded."
Swift Regex Builder
Type-safe regular expressions using Swift's RegexBuilder DSL. Covers Regex literals, the builder DSL, captures, quantifiers, character classes, anchors, Foundation parsers, and migration from NSRegularExpression.
When This Skill Activates
Use this skill when the user:
- Asks about Regex or RegexBuilder in Swift
- Wants to parse, match, or extract data from strings using patterns
- Mentions captures, quantifiers, or character classes in Swift
- Asks about /regex literal/ syntax in Swift
- Wants to use Foundation parsers (dates, numbers, currency) inside regex
- Asks about replacing, splitting, or trimming with regex
- Wants to migrate from NSRegularExpression to modern Swift regex
- Mentions
firstMatch(of:), wholeMatch(of:), or matches(of:)
Decision Tree
What kind of pattern matching do you need?
|
+-- Simple, short pattern (e.g., email, hex color)
| --> Regex literal: /pattern/
|
+-- Complex pattern with structured data extraction
| |
| +-- Need Foundation parsers (dates, numbers, currency)
| | --> RegexBuilder DSL with parser components
| |
| +-- Need readable, composable, multi-line pattern
| --> RegexBuilder DSL
|
+-- Dynamic pattern built from user input at runtime
| --> Regex(String) initializer (throws)
|
+-- Must support iOS 15 or earlier
--> NSRegularExpression (legacy)
API Availability
| API | Minimum Version | Notes |
|---|
Regex type | Swift 5.7 / iOS 16 / macOS 13 | Core regex type |
Regex literals /pattern/ | Swift 5.7 / iOS 16 / macOS 13 | Compiler-checked at build time |
RegexBuilder DSL | Swift 5.7 / iOS 16 / macOS 13 | import RegexBuilder |
firstMatch(of:) | Swift 5.7 / iOS 16 / macOS 13 | First match in string |
wholeMatch(of:) | Swift 5.7 / iOS 16 / macOS 13 | Entire string must match |
matches(of:) | Swift 5.7 / iOS 16 / macOS 13 | All non-overlapping matches |
Regex(String) | Swift 5.7 / iOS 16 / macOS 13 | Runtime pattern, throws on invalid |
| Foundation parsers in regex | Swift 5.7 / iOS 16 / macOS 13 | .localizedInteger, .iso8601, etc. |
Basic Regex Type and Matching
let pattern = /\d{3}-\d{4}/
let input = "Call 555-1234 or 555-5678"
if let match = input.firstMatch(of: pattern) { print(match.output) }
if let match = input.wholeMatch(of: /Call .+/) { print(match.output) }
let all = input.matches(of: pattern)
let has = input.contains(pattern)
if let m = "555-1234 etc".prefixMatch(of: pattern) { print(m.output) }
Regex Literals
let hexColor = /#[0-9a-fA-F]{6}/
let email = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/
let date = #/
(?<year>\d{4})
-
(?<month>\d{2})
-
(?<day>\d{2})
/#
if let match = "2025-12-31".wholeMatch(of: date) {
print(match.year)
print(match.month)
print(match.day)
}
RegexBuilder DSL
import RegexBuilder
let phonePattern = Regex {
Repeat(count: 3) { .digit }
"-"
Repeat(count: 4) { .digit }
}
let areaCode = Regex {
"("
Repeat(count: 3) { .digit }
") "
}
let fullPhone = Regex {
areaCode
phonePattern
}
if let m = "(415) 555-1234".wholeMatch(of: fullPhone) {
print(m.output)
}
Components: Quantifiers, Choices, and Captures
import RegexBuilder
Regex { One(.digit) }
Regex { OneOrMore(.word) }
Regex { ZeroOrMore(.whitespace) }
Regex {
Optionally { "-" }
OneOrMore(.digit)
}
Regex { Repeat(count: 3) { .digit } }
Regex { Repeat(2...4) { .digit } }
let scheme = Regex {
ChoiceOf {
"http"
"https"
"ftp"
}
"://"
}
let csvRow = Regex {
Capture { OneOrMore(.word) }
","
Capture { OneOrMore(.digit) }
}
if let match = "Alice,42".wholeMatch(of: csvRow) {
let (whole, name, age) = match.output
print(name)
(age)
}
intCapture {
{
(.digit)
} transform: { substring
(substring)
}
}
match .firstMatch(of: {
intCapture
}) {
value: match.output.
}
Character Classes and Anchors
import RegexBuilder
let vowel = Regex { One(.anyOf("aeiouAEIOU")) }
let hexDigit = Regex {
One { CharacterClass(.digit, ("a"..."f"), ("A"..."F")) }
}
let nonDigit = CharacterClass.digit.inverted
Regex { Anchor.startOfLine; OneOrMore(.word) }
Regex { OneOrMore(.word); Anchor.endOfLine }
Regex { Anchor.wordBoundary; "swift"; Anchor.wordBoundary }
Foundation Parsers in Regex
Foundation types conform to CustomConsumingRegexComponent for locale-aware parsing inside regex.
import RegexBuilder
let priceTag = Regex { "$"; Capture { .localizedInteger } }
if let match = "$1,234".firstMatch(of: priceTag) {
let amount: Int = match.output.1
}
let dateRegex = Regex {
"Due: "
Capture { One(.date(.numeric, locale: Locale(identifier: "en_US"), timeZone: .gmt)) }
}
Regex { Capture { .iso8601 } }
Regex { Capture { .localizedDouble }; "F" }
Regex { Capture { .localizedCurrency(code: "USD") } }
Named Captures and Output Tuples
import RegexBuilder
let timestamp = Reference(Substring.self)
let level = Reference(Substring.self)
let message = Reference(Substring.self)
let logPattern = Regex {
"["
Capture(as: timestamp) { OneOrMore(.any, .reluctant) }
"] "
Capture(as: level) { ChoiceOf { "INFO"; "WARN"; "ERROR" } }
": "
Capture(as: message) { OneOrMore(.any) }
}
if let match = "[2025-01-15 09:30] ERROR: Disk full".firstMatch(of: logPattern) {
print(match[timestamp])
print(match[level])
print(match[message])
}
let coordinate = Regex {
TryCapture { OneOrMore(.any, .reluctant) } transform: { Double(String($0)) }
{ (.any) } transform: { (()) }
}
String Processing: Replacing, Splitting, Trimming
var text = "Hello World"
text.replace(/World/, with: "Swift")
"a, b, c".replacing(/,\s*/, with: "|")
"one::two::three".split(separator: /::/)
" hello ".trimmingPrefix(/\s+/)
"v2_rc1".replacing(/\d+/) { match in match.output + "0" }
Performance: Compile Once, Reuse
struct LogParser {
private static let pattern = Regex {
"["
Capture { OneOrMore(.any, .reluctant) }
"] "
Capture { OneOrMore(.any) }
}
func parse(_ line: String) -> (timestamp: Substring, message: Substring)? {
guard let match = line.firstMatch(of: Self.pattern) else { return nil }
return (match.output.1, match.output.2)
}
}
Migration from NSRegularExpression
let nsRegex = try NSRegularExpression(pattern: "\\d{3}-\\d{4}")
let nsRange = NSRange(input.startIndex..., in: input)
for nsMatch in nsRegex.matches(in: input, range: nsRange) {
if let range = Range(nsMatch.range, in: input) { print(input[range]) }
}
for match in input.matches(of: /\d{3}-\d{4}/) { print(match.output) }
Good and Bad Patterns
import RegexBuilder
let invoiceLine = Regex {
Capture { OneOrMore(.word) }
/\s+/
Capture { .localizedInteger }
" x $"
Capture { .localizedDouble }
}
let digits = /\d+/
let tag = /<[^>]+>/
struct Validator {
private static let emailRegex = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/
func isValid(_ email: String) -> Bool {
email.wholeMatch(of: Self.emailRegex) != nil
}
}
let pattern = "\\d{" + String(count) + "}"
let regex = Regex { Repeat(count) { .digit } }
let nsRegex = try NSRegularExpression(pattern: "(\\d+)")
let swiftRegex = /(\d+)/