Swift Regex: Beginner’s Guide (With Examples)

Regex looks like keyboard mash the first time you see it. But once it clicks, it becomes one of the most useful tools in your Swift toolkit.
Written by

Chris C

Updated on

Apr 17 2026

Table of contents

    If you’ve ever needed to check whether a user typed a valid email address, extract a phone number from a block of text, or find every URL in a string, you’ve run into a problem that Swift regex is designed to solve.

    Regex stands for “regular expression.” It’s a way to describe a pattern in text using a special mini-language. Instead of writing out a long chain of string operations to hunt for something specific, you write a compact pattern and let Swift do the matching for you. Think of it like a search query on steroids. Where a normal search finds exact words, a regex finds anything that fits a shape you define.

    Here’s the thing though: regex has a reputation for being intimidating. You look at something like /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i and your brain goes blank. That’s completely normal. In this guide, I’m going to take you from zero and build up the concepts one at a time. By the end, you’ll understand how to use regex in Swift, which approach to choose (there are three), and how it shows up in real iOS code like form validation.

    The Basic Syntax

    In Swift 5.7 and later, there are three ways to create a regex. We’ll cover all three later, but let’s start with the most common one: a regex literal. It looks like a forward slash sandwich.

    Here’s the simplest possible regex example in Swift:

    import Foundation
    
    let input = "Order number: 4821"
    
    let pattern = /\d+/
    
    if let match = input.firstMatch(of: pattern) {
        print("Found: \(match.0)")  // Found: 4821
    } else {
        print("No match found")
    }

    Let’s go line by line:

    LineWhat it does
    let input = "Order number: 4821"This is the string we want to search through. In a real app this might be user input or data from an API.
    let pattern = /\d+/This is a regex literal. The slashes are delimiters, like quotation marks for a String. Inside, \d means “any digit character” and + means “one or more of the thing before it.” So this pattern matches one or more digits.
    input.firstMatch(of: pattern)firstMatch(of:) is a method on String that searches for the first part of the string matching your pattern. It returns an optional because the match might not exist.
    if let match = ...Standard optional binding. If a match was found, we unwrap it and use it. If not, we fall through to the else.
    match.0The .0 gives you the portion of the string that was matched. It returns a Substring, which you can convert to a String if needed.

    Breaking It Down

    Before we go further, let me explain the key building blocks. You don’t need to memorize all of these right now. Think of this as a vocabulary list you’ll refer back to as you use regex more.

    Metacharacters: the special symbols

    Regular characters in a regex just match themselves. The letter a matches the letter “a”. But certain characters have special meaning, and these are called metacharacters. Here are the ones you’ll use most:

    • \d matches any single digit (0 through 9)
    • \w matches any word character (letters, digits, underscores)
    • \s matches any whitespace character (space, tab, newline)
    • . matches any single character except a newline
    • ^ matches the beginning of a string
    • $ matches the end of a string

    Quantifiers: how many to match

    After any character or metacharacter, you can add a quantifier to say how many times it should appear:

    • + means one or more
    • * means zero or more
    • ? means zero or one (makes something optional)
    • {3} means exactly three times
    • {2,4} means between two and four times

    Character classes: match a set of options

    Square brackets let you define a set of characters to match. [aeiou] matches any vowel. [A-Z] matches any uppercase letter. [0-9] matches any digit (same as \d).

    Three ways to create a regex in Swift

    Swift 5.7 (iOS 16+) introduced a proper native Regex type. Before that, developers used NSRegularExpression, which is verbose and requires working with NSRange instead of Swift ranges. Today you have three modern options:

    1. Regex literal: /pattern/ — compiled at build time, type-safe, Xcode highlights it
    2. Regex from string: try Regex("pattern") — created at runtime, useful for dynamic patterns
    3. RegexBuilder DSL: A result builder API that reads like Swift code, easier to understand for complex patterns
    iOS Version Note: The native Regex type, regex literals (/.../), and RegexBuilder all require iOS 16+ / Swift 5.7+. If you need to support older iOS versions, you will still need to use NSRegularExpression. For any new project targeting iOS 16 or later, use the modern approach covered in this article.

    Variations and Syntax Patterns

    Now let’s look at the most important variations you’ll use in real code.

    Regex literal Compile-time checked pattern between forward slashes
    let pattern = /[A-Z][a-z]+/  // matches a capitalized word
    
    let name = "Hello, Chris!"
    if let match = name.firstMatch(of: pattern) {
        print(match.0)  // "Hello"
    }
    The most common way to write a regex in modern Swift. The compiler checks it at build time, which means typos in your pattern are caught immediately, not at runtime. Use this whenever your pattern is fixed and known ahead of time.
    Regex from String Runtime construction from a String value
    let userPattern = "[0-9]+"  // could come from an API or user input
    
    do {
        let regex = try Regex(userPattern)
        if let match = "Total: 42".firstMatch(of: regex) {
            print(match.0)  // "42"
        }
    } catch {
        print("Invalid pattern: \(error)")
    }
    Use this when the pattern isn’t known until runtime, like when it comes from a server or user input. The tradeoff is that Swift can’t check it at compile time, so you need to handle the error if the pattern is invalid. Note that this returns Regex<AnyRegexOutput> rather than a typed regex.
    RegexBuilder DSL Readable Swift-style regex construction using result builders
    import RegexBuilder
    
    let pattern = Regex {
        OneOrMore(.word)      // one or more word characters
        "@"                  // literal @ sign
        OneOrMore(.word)      // domain name part
        "."                  // literal dot
        OneOrMore(.word)      // TLD like "com"
    }
    
    if let match = "Email: chris@cwc.com".firstMatch(of: pattern) {
        print(match.0)  // "chris@cwc.com"
    }
    Import RegexBuilder to get access to this DSL. It’s more verbose but dramatically more readable for complex patterns. You use named components like OneOrMore, ZeroOrMore, Optionally, and ChoiceOf instead of cryptic symbols. Great for team codebases where others need to understand your regex at a glance.
    Captures Extract specific parts of a match
    // Named captures using regex literal
    let pattern = /(?<first>\w+)\s+(?<last>\w+)/
    
    let fullName = "Chris Ching"
    if let match = fullName.firstMatch(of: pattern) {
        print(match.first)   // "Chris"
        print(match.last)    // "Ching"
    }
    Captures let you pull out specific pieces of the match rather than just the whole thing. In regex literals, name a capture with (?<name>pattern). Swift then lets you access the captured value using match.name directly. The whole match is always at match.0.
    wholeMatch vs firstMatch Matching the whole string vs finding a match within it
    let zipCode = "M5V 3A8"
    
    // firstMatch finds the pattern anywhere in the string
    let hasDigits = zipCode.firstMatch(of: /\d/) != nil  // true
    
    // wholeMatch requires the pattern to match the ENTIRE string
    let isValidZip = zipCode.wholeMatch(of: /[A-Z]\d[A-Z]\s\d[A-Z]\d/) != nil  // true
    firstMatch(of:) finds the first occurrence of the pattern anywhere inside the string. wholeMatch(of:) requires the pattern to match the entire input string, with nothing left over. Use wholeMatch for validation (like checking if a field is a valid email). Use firstMatch for searching or extraction.
    replacing and split Modify strings using regex patterns
    let messy = "Phone:   416-555-1234   ext.  21"
    
    // Replace multiple spaces with a single space
    let clean = messy.replacing(/\s+/, with: " ")
    // "Phone: 416-555-1234 ext. 21"
    
    // Split a string on any non-digit
    let digits = "416-555-1234".split(separator: /\D/)
    // ["416", "555", "1234"]
    Swift’s String API gained regex-powered versions of replacing(_:with:) and split(separator:) in iOS 16. These are great for cleaning up data before processing it. \D (uppercase) is the inverse of \d and matches any non-digit character.

    How Swift Regex Appears in Real iOS Code

    The most practical use of regex in iOS development is form validation. If you’ve ever built a registration screen, a contact form, or a settings page, you’ve needed to check things like “is this a valid email?” or “is this phone number formatted correctly?”

    Here’s a realistic example: a SwiftUI form view with regex-powered validation built using RegexBuilder for readability.

    import SwiftUI
    import RegexBuilder
    
    struct SignUpFormView: View {
        @State private var email = ""
        @State private var phone = ""
    
        // A simple email validator using regex literal
        var isEmailValid: Bool {
            let pattern = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/
            return email.wholeMatch(of: pattern) != nil
        }
    
        // A North American phone validator using RegexBuilder
        var isPhoneValid: Bool {
            let pattern = Regex {
                Optionally { "1-" }                // optional country code
                Repeat(count: 3) { One(.digit) }  // area code: 3 digits
                "-"                                 // dash separator
                Repeat(count: 3) { One(.digit) }  // prefix: 3 digits
                "-"                                 // dash separator
                Repeat(count: 4) { One(.digit) }  // line: 4 digits
            }
            return phone.wholeMatch(of: pattern) != nil
        }
    
        var body: some View {
            Form {
                Section("Contact Info") {
                    TextField("Email address", text: $email)
                        .keyboardType(.emailAddress)
                        .autocapitalization(.none)
                        .foregroundStyle(isEmailValid ? .primary : .red)
    
                    TextField("Phone (416-555-1234)", text: $phone)
                        .keyboardType(.phonePad)
                        .foregroundStyle(isPhoneValid ? .primary : .red)
                }
    
                Button("Create Account") {
                    // submit action
                }
                .disabled(!isEmailValid || !isPhoneValid)
            }
        }
    }

    Here’s what each piece is doing:

    The email validation uses a regex literal. The pattern checks for characters before an @, characters in the domain, a literal dot, then at least two letters for the top-level domain. wholeMatch ensures the entire field matches, not just part of it. So “hello@” would fail, but “hello@cwc.com” would pass.

    The phone validation uses RegexBuilder. This makes the pattern easier to read and maintain. Optionally { "1-" } means the user can optionally include a country code. Repeat(count: 3) { One(.digit) } matches exactly three digits. The pattern expects the format 416-555-1234.

    The computed properties (isEmailValid and isPhoneValid) return a Bool. SwiftUI re-evaluates these automatically whenever the state changes, so validation is live as the user types.

    The button is disabled until both fields are valid. This is a pattern you’ll use constantly in real apps.

    Common Mistakes to Avoid

    These are the mistakes I see come up most often in the CWC community when people first start using regex in Swift.

    1. Using firstMatch when you need wholeMatch for validation

    This one trips up almost everyone. Imagine you’re validating an email field and you write:

    let email = "notanemail hello@cwc.com garbage"
    let isValid = email.firstMatch(of: /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/) != nil
    // isValid is TRUE, even though the full string is not a valid email

    firstMatch found a valid email address within the string and returned true, even though the overall string is clearly not just an email. For validation, always use wholeMatch. It requires the entire string to match the pattern.

    Rule of thumb: If you’re checking “is this field a valid X?”, use wholeMatch. If you’re searching “does this text contain an X?”, use firstMatch.

    2. Forgetting to escape backslashes in string-based regex

    In regex literals (/.../), you write \d directly. But when creating a Regex from a string, the backslash needs to be escaped.

    // Regex literal — works fine
    let p1 = /\d+/
    
    // String-based — backslash must be escaped with another backslash
    let p2 = try Regex("\\d+")  // correct
    let p3 = try Regex("\d+")   // wrong — Swift sees \d as an escape sequence

    You can also use raw string literals with #"..."# to avoid the double backslash: try Regex(#"\d+"#). This matches how the regex literal behaves and is easier to read.

    3. Not handling the throwing initializer for string-based Regex

    When you create a Regex from a string, it throws if the pattern is invalid. A common mistake is using try! and forgetting about it:

    // Risky — crashes if the pattern is ever invalid
    let regex = try! Regex(someUserProvidedPattern)
    
    // Safe — handles the error gracefully
    do {
        let regex = try Regex(someUserProvidedPattern)
        // use the regex
    } catch {
        print("Invalid pattern: \(error.localizedDescription)")
    }

    If the pattern is a hardcoded literal you wrote yourself and you know it’s valid, try! is acceptable. If the pattern comes from outside your code (an API, user input, a config file), always use a do-catch.

    4. Expecting regex to handle everything

    Regex is a powerful tool, but it’s not always the right one. A common beginner mistake is trying to write a single regex to fully validate complex formats like email addresses or URLs. Email validation in particular is surprisingly complex. The pattern I showed earlier in this article catches the most common cases but isn’t RFC 5322 compliant.

    For most forms, a simple regex plus a meaningful error message works better than a “perfect” regex that nobody on your team can understand. Pair regex with other Swift validation logic when needed rather than trying to encode everything into one pattern.

    Quick Reference

    SyntaxWhat It Does
    /pattern/Regex literal (compile-time checked, iOS 16+)
    try Regex(“pattern”)Create regex from string at runtime
    \dAny digit (0–9)
    \wAny word character (letter, digit, underscore)
    \sAny whitespace (space, tab, newline)
    .Any single character except newline
    +One or more of the preceding item
    *Zero or more of the preceding item
    ?Zero or one (makes the preceding item optional)
    {n}Exactly n times
    [A-Z]Any character in the specified range
    ^Start of string (or line in multiline mode)
    $End of string (or line in multiline mode)
    (?<name>pattern)Named capture group
    .firstMatch(of:)Find first occurrence of pattern anywhere in string
    .wholeMatch(of:)Require entire string to match the pattern
    .prefixMatch(of:)Require string to start with the pattern
    .replacing(_:with:)Replace matches with a different string
    .split(separator:)Split string on regex pattern matches
    OneOrMore(.digit)RegexBuilder: one or more digits
    Optionally { }RegexBuilder: optional component
    Capture { }RegexBuilder: capture a matched group
    ChoiceOf { }RegexBuilder: match one of several options

    Using AI to Go Deeper with Swift Regex

    AI tools like Claude, Copilot, and Cursor have become a normal part of how developers work. With regex especially, knowing how to use AI as a tutor can save you hours of frustration. The key is using AI to build understanding, not just to get a pattern you paste without knowing what it does.

    Deepen Your Understanding Use AI as a tutor — no code generation required
    Explain Swift regex to me like I’m a beginner who knows Swift but has never used regular expressions before. Use a plain-English analogy for what a regex is, then show me the simplest possible Swift 5.7 regex example and walk through it line by line.
    I think I understand Swift regex literals but I’m fuzzy on when to use firstMatch vs wholeMatch vs prefixMatch. Can you quiz me on it? Give me a scenario and ask me which method I should use, then explain if I’m right or wrong.
    Build a Practice Example Get a heavily commented example you can study and learn from
    Write a Swift 5.7+ example that uses a regex literal to validate a Canadian postal code (format: A1A 1A1). Add a comment on every single line explaining what each character in the pattern means and why. Write the comments for a complete beginner to regex.
    Give me three Swift regex examples: one that uses a regex literal, one that uses Regex(“string”), and one that uses RegexBuilder. Make all three do the same thing (find a phone number). Add inline comments throughout comparing the approaches so I can see the tradeoffs clearly.
    Audit Your Own Code Get feedback on regex you’ve already written without a full rewrite
    Here’s some Swift code I wrote that uses regex for form validation: [paste your code]. Is my regex pattern correct for what I’m trying to do? Are there any edge cases it misses? Explain before suggesting changes — I want to understand the issue, not just get a fixed version.
    Review this Swift regex pattern I wrote: [paste your pattern]. Can you explain in plain English exactly what this pattern matches and what it doesn’t match? Are there any inputs that might unexpectedly pass or fail?
    Remember: If AI generates a regex pattern for you, don’t ship it until you can explain every character. Paste it into a comment and write out in plain English what each piece does. If you can explain it, you own it. If you can’t, go back and ask AI to teach you what you’re missing.

    Summary

    Here’s a quick recap of what you learned about Swift regex:

    • A regex is a pattern written in a special mini-language that describes what text you’re looking for
    • Swift 5.7 (iOS 16+) introduced a native Regex type with three creation methods: literals (/.../), strings (try Regex("...")), and RegexBuilder DSL
    • Regex literals are the most common choice and offer compile-time checking and Xcode syntax highlighting
    • Use wholeMatch for validation, firstMatch for searching, and prefixMatch for prefix checking
    • Named captures let you extract specific parts of a match with clean property access like match.name
    • RegexBuilder is more verbose but dramatically more readable for complex patterns used in production code
    • The most common mistake is using firstMatch when you need wholeMatch for validation

    The best way to get comfortable with regex is to use it on a real field in a real form. Pick something simple like a Canadian postal code or a 5-digit US zip code and build a validator. Once you’ve done that once, the rest comes naturally.

    A great next topic after regex is Swift string manipulation more broadly, including contains, hasPrefix, components(separatedBy:), and trimmingCharacters(in:). Regex and these built-in methods complement each other well in real iOS apps.



    Get started for free

    Join over 2,000+ students actively learning with CodeWithChris