Skip to main content

Swift Regular Expressions

Regular expressions (regex) are powerful tools for working with text patterns in Swift. They provide a concise and flexible way to search, match, and manipulate strings based on pattern rules. Swift has built-in support for regular expressions that makes processing text more efficient and expressive.

What are Regular Expressions?

Regular expressions are special string patterns that describe search patterns. Think of them as a mini-language for specifying text patterns. They can be used to:

  • Validate input (like email addresses or phone numbers)
  • Search for specific patterns in text
  • Replace or extract parts of strings
  • Parse and transform text

Basic Regular Expression Syntax in Swift

Swift offers multiple ways to work with regular expressions. Let's start with the traditional approach using the NSRegularExpression class.

Using NSRegularExpression

swift
import Foundation

let text = "Contact me at [email protected] or visit [email protected]"
let pattern = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"

do {
let regex = try NSRegularExpression(pattern: pattern)
let nsRange = NSRange(text.startIndex..<text.endIndex, in: text)
let matches = regex.matches(in: text, range: nsRange)

for match in matches {
let matchRange = Range(match.range, in: text)!
let email = String(text[matchRange])
print("Found email: \(email)")
}
} catch {
print("Invalid regex: \(error.localizedDescription)")
}

Output:

Found email: [email protected]
Found email: [email protected]

Swift 5.7+ Regex Literals

Swift 5.7 introduced a more modern way to work with regular expressions using regex literals:

swift
import Foundation

let text = "Swift 5.7 was released in 2022"

// Using a regex literal
let yearPattern = /\d{4}/
if let match = text.firstMatch(of: yearPattern) {
print("Found year: \(match.0)")
}

Output:

Found year: 2022

Common Regex Patterns

Let's explore some common regex patterns that you'll frequently use:

1. Character Classes

Character classes match specific sets of characters:

swift
let text = "Swift 5.7"

// Match digits
let digitPattern = /\d+/
if let match = text.firstMatch(of: digitPattern) {
print("Digits: \(match.0)") // Prints: Digits: 5
}

// Match letters
let letterPattern = /[a-zA-Z]+/
if let match = text.firstMatch(of: letterPattern) {
print("Letters: \(match.0)") // Prints: Letters: Swift
}

2. Quantifiers

Quantifiers specify how many times something should match:

swift
let text = "Hello"

// + means one or more
let oneOrMorePattern = /l+/
if let match = text.firstMatch(of: oneOrMorePattern) {
print("One or more 'l': \(match.0)") // Prints: One or more 'l': ll
}

// * means zero or more
let zeroOrMorePattern = /e*/
if let match = text.firstMatch(of: zeroOrMorePattern) {
print("Zero or more 'e': \(match.0)") // Prints: Zero or more 'e': e
}

// ? means zero or one
let optionalPattern = /lo?/
if let match = text.firstMatch(of: optionalPattern) {
print("Optional 'o': \(match.0)") // Prints: Optional 'o': lo
}

3. Anchors

Anchors match positions rather than characters:

swift
let text = "Swift programming"

// ^ matches start of string
let startPattern = /^Swift/
if text.firstMatch(of: startPattern) != nil {
print("Text starts with 'Swift'")
}

// $ matches end of string
let endPattern = /ing$/
if text.firstMatch(of: endPattern) != nil {
print("Text ends with 'ing'")
}

Using Capture Groups

Capture groups allow you to extract specific parts of a matched pattern:

swift
import Foundation

let text = "Date: 2023-07-15"
let datePattern = /Date: (\d{4})-(\d{2})-(\d{2})/

if let match = text.firstMatch(of: datePattern) {
let (fullMatch, year, month, day) = match.output
print("Year: \(year), Month: \(month), Day: \(day)")
}

Output:

Year: 2023, Month: 07, Day: 15

Regex Builder API

Swift 5.7 also introduced the regex builder API, which allows you to build complex regular expressions programmatically:

swift
import RegexBuilder

let text = "Contact: John (ID: 12345)"

let pattern = Regex {
"Contact: "
Capture {
OneOrMore(.word)
}
" (ID: "
Capture {
OneOrMore(.digit)
}
")"
}

if let match = text.firstMatch(of: pattern) {
let (_, name, id) = match.output
print("Name: \(name), ID: \(id)")
}

Output:

Name: John, ID: 12345

Real-World Applications

Let's look at some practical examples of using regular expressions in Swift.

Example 1: Email Validation

swift
import Foundation

func isValidEmail(_ email: String) -> Bool {
let pattern = /^[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/
return email.wholeMatch(of: pattern) != nil
}

// Test the function
let emails = ["[email protected]", "invalid@email", "[email protected]"]
for email in emails {
print("\(email) is \(isValidEmail(email) ? "valid" : "invalid")")
}

Output:

[email protected] is valid
invalid@email is invalid
[email protected] is valid

Example 2: Extracting URLs from Text

swift
import Foundation

func extractURLs(from text: String) -> [String] {
let pattern = /(https?:\/\/[^\s]+)/

var urls: [String] = []
var searchRange = text.startIndex..<text.endIndex

while let match = text[searchRange].firstMatch(of: pattern) {
urls.append(String(match.1))
searchRange = match.range.upperBound..<text.endIndex
}

return urls
}

let blogPost = "Check out my website at https://example.com and my blog at http://blog.example.com/posts"
let urls = extractURLs(from: blogPost)
print("Found URLs:")
urls.forEach { print("- \($0)") }

Output:

Found URLs:
- https://example.com
- http://blog.example.com/posts

Example 3: Parsing Structured Data

swift
import Foundation
import RegexBuilder

struct Person {
let firstName: String
let lastName: String
let age: Int
}

func parsePerson(from text: String) -> Person? {
let pattern = Regex {
"Name: "
Capture {
OneOrMore(.word)
}
" "
Capture {
OneOrMore(.word)
}
", Age: "
Capture {
OneOrMore(.digit)
}
}

if let match = text.firstMatch(of: pattern) {
let (_, firstName, lastName, ageString) = match.output
if let age = Int(ageString) {
return Person(firstName: String(firstName), lastName: String(lastName), age: age)
}
}
return nil
}

let text = "Name: John Smith, Age: 32"
if let person = parsePerson(from: text) {
print("Parsed Person: \(person.firstName) \(person.lastName), \(person.age) years old")
}

Output:

Parsed Person: John Smith, 32 years old

Performance Considerations

Regular expressions are powerful but can be computationally expensive:

  1. Compile once, use many times: If you use the same regex multiple times, compile it once and reuse it.
  2. Be specific: More specific patterns are often more efficient.
  3. Avoid backtracking: Complex patterns with many optional parts can cause excessive backtracking.
  4. Consider alternatives: For simple string operations, Swift's built-in string methods might be faster.
swift
// Inefficient - creates regex each time
func countMatches(in texts: [String]) -> Int {
var count = 0
for text in texts {
if try! NSRegularExpression(pattern: "\\d+").firstMatch(in: text, range: NSRange(text.startIndex..<text.endIndex, in: text)) != nil {
count += 1
}
}
return count
}

// Efficient - creates regex once
func countMatchesEfficient(in texts: [String]) -> Int {
let regex = try! NSRegularExpression(pattern: "\\d+")
var count = 0
for text in texts {
if regex.firstMatch(in: text, range: NSRange(text.startIndex..<text.endIndex, in: text)) != nil {
count += 1
}
}
return count
}

Summary

Regular expressions in Swift provide a powerful way to work with text patterns. We've covered:

  • Basic regex syntax and patterns
  • Using NSRegularExpression for traditional regex operations
  • Modern Swift regex literals and builders
  • Capture groups for extracting matched components
  • Real-world applications like validation and data extraction
  • Performance considerations

With these skills, you can efficiently handle complex text processing tasks in your Swift applications. Whether you're validating user input, parsing data, or extracting information from text, regular expressions are an essential tool in your Swift programming toolkit.

Additional Resources

Exercises

  1. Create a function that validates phone numbers in the format (XXX) XXX-XXXX.
  2. Write a regex to extract hashtags (words prefixed with #) from a tweet.
  3. Parse a log file with the format "[YYYY-MM-DD HH:MM:SS] [LEVEL] Message" to extract the date, log level, and message.
  4. Create a function that replaces all URLs in a text with clickable HTML links.
  5. Build a regex to validate passwords that require at least 8 characters, one uppercase letter, one lowercase letter, and one number.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)