Swift Regex Builder
Swift has good regular expression support, but I still like to test the pattern outside the code first. It is faster to change sample text and inspect matches in a small Mac app than to keep rebuilding while I am still figuring out the expression.
The workflow I use is boring: test the regex, make the capture groups obvious, then move it into Swift.
Swift also has a RegexBuilder DSL for building regular expressions in code. That is useful when I want the expression to read more like Swift. I still start with the plain regex when I am moving between tools, because it is easier to test the same pattern in a Mac app, a shell command, and a code snippet.
Start with the plain regex
Say I want to match a simple issue key:
AUTH-1284
REGEX-42
BILLING-9001
The plain regex is:
\b([A-Z]+)-(\d+)\b
Before I put this in Swift, I want to know two things:
- The full match is the whole issue key.
- Group 1 is the project key, and group 2 is the number.
That is what I test in Regex for macOS. The app lets me keep the sample text and the expression together while I work.
Move it into Swift
In Swift, a raw string keeps the regex readable:
let pattern = #"\b([A-Z]+)-(\d+)\b"#
If I am using NSRegularExpression, the code looks like this:
import Foundation
let text = "Fixed AUTH-1284 and REGEX-42."
let regex = try NSRegularExpression(pattern: #"\b([A-Z]+)-(\d+)\b"#)
let range = NSRange(text.startIndex..<text.endIndex, in: text)
let matches = regex.matches(in: text, range: range)
for match in matches {
let fullRange = Range(match.range(at: 0), in: text)!
print(text[fullRange])
}
In production code I would handle failed range conversions instead of force-unwrapping. This example keeps the snippet focused.
If I am building a larger parser in Swift, I would look at RegexBuilder too. For small patterns I expect to copy between Swift, Terminal, nginx notes, or documentation, the plain regex is still the most portable starting point.
That is the distinction I care about: RegexBuilder can make Swift code clearer, but it is not the only place to test the matching rule. I still want sample input, visible captures, and a quick way to catch mistakes before I decide how the final Swift code should look.
Check escaping separately
Escaping is where I make mistakes.
There are a few layers:
- regex escaping
- Swift string escaping
- replacement string escaping, if I am replacing text
Raw strings help:
let emailPattern = #"[\w.%+-]+@[\w.-]+\.[A-Za-z]{2,}"#
That is easier to read than doubling backslashes everywhere.
When I would not use a fancy regex
For simple string work, I do not force everything into a regular expression. If a prefix check, suffix check, split, or parser is clearer, I use that.
Regex is good when the input is text-like and the rule is compact. It gets worse when the input is structured data pretending to be text. HTML, JSON, and CSV usually deserve a parser.
My Swift regex checklist
Before I paste a pattern into Swift, I check:
- sample strings that should match
- sample strings that should not match
- capture group order
- Unicode assumptions
- whether I need multiline or case-insensitive options
- whether a parser would be clearer
If I need to iterate on the pattern, I do it in Regex for macOS first, then copy the result into Swift.