Custom Operators in Swift with practical code examples

Custom operators in Swift can make your code easier to read and simpler to maintain. Code can be written less lines of code while keeping it clear what is happening. Custom operators are also known as advanced operators and allow you to combine two instances with a self-chosen infix, prefix, postfix, or assignment operator.

When developing code in Swift we’re all using the default operators quite often. Adding up two numbers by using the + sign is an example of making use of basic operators available in Swift. Let’s dive in and see how you can improve your code with custom-defined operators.

Examples of basic operators available in Swift

To fully understand what operators are we can go over a few operator examples that are available in Swift by default. Some might be more obvious to you than others!

For example, the assignment operator is very often used (obviously) but is also an operator!

/// Assignment `=` operator example.
let name = "Antoine"
let age = 29

There are a few so-called Unary operators like the unary prefix operator !value and the unary postfix operator value!, also known as the force unwrap operator

/// Unary postfix `!` operator example.
let forceUnwrappedURL: URL = URL(string: "www.avanderlee.com")!

/// Unary prefix `!` operator example.
let URLHasValue = !forceUnwrappedURL.absoluteString.isEmpty

A common example of an infix operator is the + sign which falls under the category Binary operators. It’s called an infix operator as it appears in between two values.

/// Infix operator `+` example.
let totalSum = 10 + 4

Swift also comes with a Ternary operator the operates on three targets. The ternary conditional operator allows us to write if statements in a single line:

/// Ternary operator example `?:`, the ternary conditional operator.
let title = page.hasTitle ? page.title : "Untitled"

These are all just a few examples of all available operators in Swift. You can read more about the basic operators in the Swift Language Guide.

Creating a custom operator

A custom operator can be created by defining a static method in an extension of the type you want to work with. There are a few different operators as described before that you can choose to define. Let’s go over a few practical examples that you can use in your codebase.

Creating a custom compound assignment operator

Compound assignment operators combine assignment (=) with another operator. This is the kind of operator that is often customized as it’s easier to think of use-case examples.

Take the following example in which we have a team full of members:

struct Team {
    let title: String
    private(set) var members: [Member]

    mutating func add(_ member: Member) {
        members.append(member)
    }
}

struct Member {
    let name: String
}

This code allows us to write the following code by making use of the add(_:) method:

var team = Team(title: "Developers", members: [Member(name: "Antoine")])

let newMember = Member(name: "Boris")
team.add(newMember)

We can swap this code for an operator that allows us to add members directly to the team by making use of the += operator:

extension Team {
    static func += (lhs: inout Team, rhs: Member) {
        lhs.add(rhs)
    }
}

Let’s go through this code step by step:

  • First, we make an extension on our Team structure
  • The operator is defined as a static method in which the method name defines the operator
  • As we’re working with structs we need to use inout which adds the requirement of having a mutable Team instance
  • Finally, we’re passing in the new member which we can add to the team with the add(_:) method

We can make use of this operator as follows:

let newMember = Member(name: "Boris")
team += newMember

Prefix and postfix operators

Prefix and Postfix operators might be less commonly used as custom operators. It’s a bit a different mindset when thinking about use-case examples. However, there are quite a few nice things you can do with them.

In this example, we’re going to define a prefix operator. However, you could define postfix operators in the same way.

Take the following code example in which we convert a decimal number into a String with the right currency based on the current locale:

let currencyFormatter = NumberFormatter()
currencyFormatter.numberStyle = .currency
currencyFormatter.locale = Locale.current

let priceString = currencyFormatter.string(from: 9999.99)!

// Displays $9,999.99 in the current locale, for example $ dollars.
print(priceString)

It’s likely that we will have to convert a lot of our values into currency strings so it would be great if can shorten this code. We can do this by introducing a self-named Currency operator that transforms a given decimal into a string with the currency applied.

prefix func ~ (value: NSNumber) -> String {
    let currencyFormatter = NumberFormatter()
    currencyFormatter.numberStyle = .currency
    currencyFormatter.locale = Locale.current

    return currencyFormatter.string(from: value)!
}

There are a few things to consider when reading through this defined prefix operator:

  • The operator is globally defined and not tight to a specific type. This is required as we’re only working with the argument value and we don’t have a receiver type that we would otherwise define in the extension
  • We take a NSNumber as an input argument. This allows us to use the operator on any number type.
  • Note that we convert an input number into an output string. This should open your eyes to other use-cases!

You can use this custom operator as follows:

let decimalInputPrice: String = ~843.32
print(decimalInputPrice) // Prints: $843.32

let intInputPrice: String = ~300
print(intInputPrice) // Prints: $300.00

This can improve the readability of your code! However, you need to tell your colleagues about the newly introduced ~ Currency operator so they also understand what is going on.

Calculating with Emojis in Swift

I never thought that I would write about emoji calculation in Swift but it turned out to be a popular April fools joke in 2020.

Many thought that I used Photoshop to create the outcome but the fact is, it was actually all working for real!

You could know this from the line numbers on the left and the green + operator sign in the image used:

Custom operators in Swift to calculate with emojis
Custom operators in Swift to calculate with emojis

Now that you know how you can create your own custom operators it’s a nice practice to try and build this for yourself. However, you can also simply take a look at the code as follows:

extension String {
    static func + (lhs: String, rhs: String) -> String {
        switch (lhs, rhs) {
        case ("πŸ›Ή", "❄️"):
            return "πŸ‚"
        case ("😬", "❄️"):
            return "πŸ₯Ά"
        case ("😒", "πŸ”₯"):
            return "πŸ₯΅"
        case ("πŸ₯•", "πŸ₯¬"):
            return "πŸ₯¬"
        case ("πŸ₯¬", "πŸ₯’"):
            return "πŸ₯’"
        case ("πŸ₯’", "πŸ…"):
            return "πŸ₯—"
        case ("πŸ§”", "πŸ’ˆ"):
            return "πŸ‘ΆπŸ»"
        case ("🦏", "🌈"):
            return "πŸ¦„"
        case ("πŸ”¨", "πŸ”§"):
            return "πŸ› "
        default:
            print("\(lhs) and \(rhs) not matched")
            return "⁉️"
        }
    }
}

As you can see, it’s simply a custom infix operator on strings that allows to “calculate” with different emojis. You can now try it out for yourself and start adding your own combinations of which I would obviously love to hear from you which combination you created!

Conclusion

Custom operators in Swift can help you to reduce duplicate code and improve the readability. You can consider creating infix, prefix, postfix, or assignment operators that work for your use-case. The above practical examples show how you can add operators to your own custom defined types.

Not getting enough out of this? How about defining your own custom subscripts in Swift.

If you like to improve your Swift knowledge, even more, check out the Swift category page. Feel free to contact me or tweet to me on Twitter if you have any additional tips or feedback.

Thanks!