Swift Advanced Custom Pattern Matching

Mar 17, 2023#patterns#swift

When you use pattern matching in Swift, the compiler uses the ~= operator behind the scenes to match the value against each case pattern.

By default, the ~= operator compares two values of the same type using the == operator. It can also match a value with a range of values, by checking whether the value is contained within the range.

The Swift Standard Library provides default implementations of the ~= operator for several built-in types, such as Int, String, and Optional, but developers can also define their own implementations of the operator for custom types or custom pattern matching logic.

To overload the ~= operator in Swift, you need to define a function that takes two parameters: a value to match and a pattern to match against.

Here are some examples to demonstrate how this works:

Matching against an array pattern

func ~=<T>(pattern: [T], value: T) -> Bool {
    return pattern.contains(value)

let fruits = ["apple", "banana", "orange"]
let fruit = "banana"

if case fruit = fruits {
    print("The fruit is in the list")
} else {
    print("The fruit is not in the list")

In this example, we’ve defined an overload for the ~= operator that matches a value against an array pattern. The function takes an array pattern and a value, and returns true if the value is contained in the pattern. We then use this operator overload in a case statement to match a string fruit against an array of fruits, and print a message indicating whether the fruit is in the list or not.

Matching against a range pattern

func ~=<T: Comparable>(pattern: ClosedRange<T>, value: T) -> Bool {
    return pattern.contains(value)

let number = 5
if case 1...10 = number {
    print("The number is in the range")
} else {
    print("The number is outside the range")

In this example, The overload function takes a ClosedRange pattern and a value, and returns true if the value is contained within the pattern.

Matching against a regular expression pattern

import Foundation

func ~= (pattern: NSRegularExpression, value: String) -> Bool {
    return pattern.firstMatch(in: value, range: NSRange(value.startIndex..., in: value)) != nil

let email = "johndoe@example.com"

if let regex = try? NSRegularExpression(pattern: "[a-z]+@[a-z]+\\.[a-z]+") {
    if case regex = email {
        print("The email is valid")
    } else {
        print("The email is invalid")

// Prints: The email is valid

In this example, the overload function takes an NSRegularExpression pattern and a String value, and returns true if the value matches the regular expression.

Matching against custom pattern

protocol MyPattern {
    associatedtype Input
    func matches(_ value: Input) -> Bool

func ~=<P: MyPattern>(pattern: P, value: P.Input) -> Bool {
    return pattern.matches(value)

struct MyIntPattern: MyPattern {
    typealias Input = Int
    let patternValue: Int

    func matches(_ value: Int) -> Bool {
        return value % patternValue == 0

    init(_ patternValue: Int) {
        self.patternValue = patternValue

let number = 10

if case MyIntPattern(5) = number {
    print("\(number) is a multiple of 5")
} else {
    print("\(number) is not a multiple of 5")

// Prints: 10 is a multiple of 5

In this example, we define a protocol MyPattern. Then we overload the ~= operator to work with any type that conforms to the MyPattern protocol. Finally, we define a concrete implementation of the MyPattern protocol with MyIntPattern. It takes an Int pattern value and checks whether a given value is a multiple of the pattern.