Regular expressions in Swift
How to work with them?
Regular expressions, those cryptic strings of characters that can make even seasoned developers break out in a cold sweat. For years, regex has been both a powerful tool and a daunting puzzle, its complexity often leaving programmers wary of diving too deep. But with Swift 5.7 introduced a set of features that changes this forever. With its new regex features, Swift is stepping up to tame the beast, offering a fresh, approachable way to harness the power of pattern matching without the usual headaches. In this post, we’ll explore how Swift’s latest reflex enhancements are making regex less of a nightmare and more of a dream for developers everywhere.
What is regex?
Regular expressions, or regex, are patterns used to match, search, and manipulate text. They consist of special characters and sequences that define rules for identifying specific strings or patterns within larger bodies of text. Think of them as a super-flexible search tool that can find anything from exact words to complex combinations like email addresses or phone numbers. While they can seem cryptic at first, regex is widely used in programming for tasks like validation, text parsing, and data extraction.
Let's have a look at two example regexes
1. ^\+(\d{1,3})[-. ]?(\d{1,4})[-. ]?(\d{2,4})[-. ]?(\d{2,4})$
2. [a-z]{3}
If you have never seen a regex before, looking at the first example you may start to realise why many people fear them. Of course with the introduction of AI coding agents, tackling regexes become a lot more approachable but yet, its always useful to know them. Let’s see how Swift 5.7 changed the way we can work with regexes
While this article is not a regex tutorial, for the sake of clarity, let’s mention that the first regex matches international phone numbers while the second one matches 3 letter words
The regex object
At the core of Swift’s regex handling lies the Regex
object. It’s straightforward to use: instantiate it with a pattern and apply it to search text.
let pattern = try Regex(“[a-z]{3}”)
let matches = text.ranges(of: pattern)
This works, but it has a catch. Because the regex string is parsed at runtime, the try keyword is required, and error, like a malformed pattern, only surface when the code executes. This delay in feedback can slow development and introduce runtime risks.
The regex literal
Enter the regex literal, a game-changer in Swift’s regex arsenal. Here’s how it looks:
let regex = /[a-z]{5} (\d+)/
let result = try? regex.wholeMatch(in: "hello 12")
print(result?.1)
Unlike the Regex
object, regex literals are parsed at compile time. This brings two significant advantages: early pattern validation catches errors during development, and Swift’s type safety integrates seamlessly with the pattern. Consider this enhancement:
let regex = /[a-z]{5} (?‹age>\d+)/
let result = try? regex.wholeMatch(in: "hello 12")
print(result?.age)
Named groups are nothing new in regexes, but being able to reference the names used in a regex pattern at compile time in the rest of the code simply wouldn't be possible with a runtime solution. This feature brings working with regex matches to a whole new level of clarity and safety.
The regex DSL
Our final option is just as exciting as the previous ones. The regex Domain-Specific Language (DSL) provides a descriptive way of constructing regular expressions using a syntax that resembles SwiftUI. The DSL implementation relies on result builders and represents an excellent example of how to use them outside of SwiftUI. Let's see how our previous example would look:
Import RegexBuilder
let age = Reference(Substring.self)
let regex = Regex {
Repeat(count: 5) {
("a"..."z")
}
" "
Capture(as: age) {
OneOrMore(.digit)
}
}
let result = try? regex.wholeMatch(in: "hello 12")
print(result?[age])
As you can see, this syntax is highly descriptive, making it easy to understand the purpose of the regex even for those unfamiliar with traditional regular expression syntax. Furthermore, the DSL makes it simple to convert matches to custom data types in place, eliminating the need to extract a string match and then convert it.
let age = Reference(Int.self)
//…
TryCapture(as: age) {
OneOrMore(.digit)
} transform: { match in
Int(match)
}
TryCapture
ensures not only that result?[age]
will have a type of Int
but also handles rejecting the match if the group matched is not convertible to Int
, that is when Int(match)
returns nil
.
This approach may seem too verbose, I personally prefer the regex literal for simple tasks, however its true power becomes evident when we take a look again at the first regex example from this article
\+(\d{1,3})[-. ]?(\d{1,4})[-. ]?(\d{2,4})[-. ]?(\d{2,4})
This challenging regex is difficult to decode, not necessarily because of individual rules but due to its length and complexity. Here's how it would look using the regex builder:
let pattern = Regex {
"+"
Capture {
Repeat(1...3) {
One(.digit)
}
}
Optionally(CharacterClass.anyOf("-. "))
Capture {
Repeat(1...4) {
One(.digit)
}
}
Optionally(CharacterClass.anyOf("-. "))
Capture {
Repeat(2...4) {
One(.digit)
}
}
Optionally(CharacterClass.anyOf("-. "))
Capture {
Repeat(2...4) {
One(.digit)
}
}
}
Reading this structured code should be much more approachable for anyone, making the regex DSL a perfect fit for simplifying complex regex patterns.
💡Note: If you already have a regex and you want to convert it into a regex builder, Xcode can easily do it for you. Just highlight the pattern, right click on it and choose
Refactor
>Convert to regex builder
Conclusion
Swift 5.7's regex enhancements represent a significant leap forward in making regular expressions more accessible and maintainable for developers. Through the introduction of three complementary approaches, the traditional Regex object, the compile-time validated regex literal, and the expressive Regex DSL, Swift has transformed what was once a cryptic, error-prone aspect of programming into a safer, more intuitive experience.
The regex literal brings compile-time safety to pattern validation, eliminating many runtime issues while enabling the power of named capture groups with Swift's type system. Meanwhile, the Regex DSL provides unprecedented readability for complex patterns through its declarative syntax, making even the most intricate matching logic comprehensible at a glance.
These innovations don't merely make regex easier to write, they fundamentally change how developers interact with pattern matching in their code. Complex text processing tasks that once required careful documentation and cautious testing can now be expressed clearly in code that practically documents itself. The ability to transform matched text directly into strongly-typed Swift objects further bridges the gap between pattern matching and Swift's type safety.
As developers embrace these new tools, we can expect to see more confident and creative uses of regular expressions in Swift applications. What was once a necessary evil becomes a powerful ally, allowing for more robust input validation, more sophisticated text processing, and ultimately more reliable software. Swift's regex revolution proves that even the most intimidating aspects of programming can be reimagined to become more accessible without sacrificing power or flexibility.
Related articles
Here are some more articles that may interest you. Check them out!
The RawRepresentable protocol
published on February 15, 2025
SwiftIf you’ve ever used a Swift enum with a raw value, you may not even realise that you have already worked with the RawRepresentable protocol. But what is it and how can you make use of it? In this article we are going to investigate the RawRepresentable protocol and I’m going to show you some neat ways for using the protocol to our advantage.
Read moreProportional layout with SwiftUI
published on January 25, 2025
SwiftUISwiftStarting from iOS 16, SwiftUI gives us the option to build custom layouts that can control very precisely where each subview should be placed. We have the option to build layouts that can be used just as the VStack, HStack or any other built in Layout you may be familiar with from SwiftUI.
Read moreThe @ and # symbols in Swift
published on January 2, 2025
SwiftWhen a new feature is introduced in a programming language, we as developers are often quick to assume it will solve many of their challenges. The excitement to experiment with such features is natural, but it’s often accompanied by the realisation that every solution brings its own set of unique challenges. This was no different when actors were introduced.
Read more