The Ultimate Guide to Operators in Swift

Written by LearnAppMaking on April 21 2021 in App Development, Swift

The Ultimate Guide to Operators in Swift

Operators in Swift are tiny symbols that produce a result. You’ve got comparison operators, logical operators, and operators like + and - for simple math. In this tutorial, we’re diving deep into Swift’s operators, including precedence and associativity, and even write our own custom operators.

Here’s what we’ll discuss:

  • The most important operators: math, assignment, comparison, logic, ranges
  • How you can code your own custom operators
  • Important but complex topics: precedence, associativity and grouping
  • Pretty much every operator under the sun: from !a to ??

Ready? Let’s go.

  1. Operators in Swift
  2. Math Operators
  3. Assignment Operators
  4. Comparison Operators
  5. Logical Operators
  6. Range Operators
  7. Really Cool Operators
  8. Precedence, Associativity and Grouping
  9. Custom Operators
  10. Further Reading

Operators in Swift

What’s an operator? Let’s start with an example. Check this out:

let result = 3 + 4
print(result)
// Output: 7

In the above Swift code, the + is an operator. You know it as the “plus” symbol, which is used to add one number to another and return the result.

In Swift, and in programming in general, we’ve got lots and lots of operators. This tutorial will show you how to use them, and “rules” to keep in mind, like precedence and associativity.

Before we continue, we’ll have to discuss some terminology:

  • Operator: Function that produces a result based on 1, 2 or 3 input values. We’ve got arithmetic, logical, comparison, range operators – the works!
  • Operand: The input for an operator, i.e. the numbers left and right of the + sign. You can also call them targets or parameters.
  • Expression: A combination of operators and operands, including function calls, variables, types, etc., which represents a (return) value.
  • Result: The value that’s returned by an expression, i.e. 7 is returned by the expression 3 + 4.

In this tutorial, you’ll often see the placeholders or variables a, b and c. They usually refer to the first, second and third operands (inputs) of a function. We’ll sometimes say !isLoggedIn, but it may be clearer to write !a when discussing how the logical NOT operator ! works.

In general, we can separate operators in Swift into 4 groups:

  1. Math: These operators involve simple arithmetic, like addition +, multiplication = and remainder %.
  2. Comparison: These operators return a boolean value, like a > b. You use them to compare numbers and strings.
  3. Logical: These operators are used to evaluate and combine boolean values, such as the logical NOT operator !a.
  4. Advanced: These operators provide a bit of syntactical sugar around common operations, such as the nil-coalescing operator a ?? b.

In programming we also distinguish between unary, binary and ternary operators. They refer to the number of inputs (operands) an operator has. This is important for remembering syntax, but also because it tells you something about what an operator does.

  1. Unary: A unary operator has 1 operand (input). An example is !a or !isLoggedIn, for the logical NOT operator ! to invert a boolean value.
  2. Binary: A binary operator has 2 operands (inputs). An example is a > b or 5 > 6, for the greater-than comparison operator >.
  3. Ternary: A ternary operator has 3 operands (inputs). Swift only has 1 ternary operator, the ternary conditional operator (discussed below).

The last bit of terminology revolves around the position of the operator:

  1. Prefix: A prefix operator is written before the operand, like ! in !a (logical NOT).
  2. Infix: An infix operator is written in between 2 operands, like + in a + b.
  3. Postfix: A postfix operator is written after the operand, like ! in a! (force unwrapping).

Quick Tip: Unary has “uno” for 1, binary has “bi” for 2, and ternary has “tri” for 3. Each of these terms comes from Latin (unus, binarius, ternarius).

Math Operators

Let’s start with the simplest operators of the bunch: mathematics. They work exactly as you’d expect in code, as they do in algebra class.

Arithmetic

In Swift, we’ve got the 4 standard arithmetic operators + (plus), - (minus), * (multiplication) and / (division). Each of these operators are binary, so you’ll add one number to another with a + b for example. They (mostly) work with numbers, like integers and doubles.

3 + 4 //  7
7 - 9 // -2
4 * 4 // 16
9 / 3 //  3

You can also use + to combine 2 strings into one. This is called string concatenation. Like this:

let line = "So long" + " and thanks for all the fish!"
// Output: So long and thanks for all the fish!

Speaking of strings and integers – because Swift is a strongly typed language, you can only use operators on 2 operands that have the same type. That means – strictly speaking – you can only add 2 integers, or 2 strings, or 2 doubles to each other.

There are plenty of exceptions, though. If you try to add an integer to a double, for example:

let value = 1 + 2.0
print(value)
// Output: 3.0

In the above code, Swift uses type inference to figure out that the type of value must be Double because one of its operands is Double.

Behind the scenes, Swift will convert 1 to 1.0. This makes programming considerably easier, and easier to read, because you don’t have to manually type cast that integer to a double.

Plus Minus Sign Operators

You can use the unary minus operator -a to toggle (or “flip”) the sign of a number, i.e. from positive to negative and vice versa. You can also use the unary plus operator +a, but that won’t do anything!

Let’s take a look at an example:

let positiveNumber = 42
let negativeNumber = -positiveNumber
let whatNumber     = -negativeNumber
print(positiveNumber, negativeNumber, whatNumber)
// Output: 42 -42 42

As you can see in the above code, the first number is positive, and then the -positiveNumber flips the sign to negative, and a second time back to positive, with -negativeNumber.

When we change the 3rd line to this …

let whatNumber = +negativeNumber

… the value of whatNumber is still -42. It’ll stay unchanged from negativeNumber because the +a operator doesn’t do anything!

You may find that, in day-to-day coding, the unary minus operator is impractical and hard to read. In those scenarios where you want to make explicitly clear that the sign of an expression has changed, you can use the operation a * -1. Like this:

let value = (3 + 4 / (2 * 3 * 4) - 4 / (4 * 5 * 6) + 4 / (6 * 7 * 8)) * -1
print(value)
// Output: -3.145238095

The expression (···) * -1 is often clearer than -(···). That depends on your use case, of course, but it’s good to know these little math tricks exist.

Remainder: isMultiple(of:)

The last math operator we’ll discuss is the remainder operator a % b. It’ll return the remainder of a division, i.e. what number remains if you fit a in b as many times as possible.

An example:

let value = 7 % 2
print(value)
// Output: 1

The above expression 7 % 2 returns 1 because you can fit 3 times 2 in 7, which leaves 1. It’s the basic “John has 7 apples and 3 friends, how many apples are left if he gives each friend 2 apples?”

You often use the remainder operator a % b to calculate if a given number is a multiple of another number, or is divisible by that number. The most common scenario is, of course, to calculate if a given number is even or odd.

Check this out:

for i in 0..<10 {
    let value = i % 2 == 0 ? "\(i) is even" : "\(i) is odd"
    print(value)
}
// Output: 0 is even, 1 is odd, 2 is even, 3 is odd ···

Instead of the remainder operator, you can use the function isMultiple(of:). Like this:

let value = 144
let result = value.isMultiple(of: 12)
print(result)
// Output: true

Awesome! Let’s continue with assignment operators.

Assignment Operators

You use the assignment operator a = b in Swift to assign a value b to a value a. Differently said, you use the assignment operator to assign a value to a variable, constant or property.

Check this out:

let age = 42
print(age)
// Output: 42

In the above code, we’ve assigned the integer 42 to the constant age. What’s on the right side of the = symbol is assigned to what’s on the left side of it.

That sounds so simple, but there’s more! For example, the assignment operator does not return a value. The expression age = 42 does not have a result, unlike, for example, the expression a + 4.

It’s easy to mistake the = operator with the equals operator ==, which is why Swift won’t return a value for age = 42, which helps you avoid coding errors, for example with making a typo in if age = 42 { ··· }.

Compound Assignment Operator

Although the = assignment operator is technically the only one in Swift to assign a value to a variable, we’ve also got 4 compound assignment operators. In short, they perform an operation, like addition, and also assign the result to a variable.

Here’s an example:

var i = 3
i += 1
print(i)
// Output: 4

The a += b adds b to a and assigns the result to a. It’s increasing the value of a by b. The a += b syntax is identical to a = a + b.

The above code also shows how Swift is different from other programming languages. C-style languages, like Java, have the a++ and a-- operators. They increase or decrease a by one. Swift doesn’t have those, it just has a += 1 and a -= 1. (Try it, ++ really won’t work!)

In total, we’ve got 10 compound assignment operators, of which 4 are the simplest and most common. They are:

  • a += b: add and assign
  • a -= b: subtract and assign
  • a *= b: multiply and assign
  • a /= b: divide and assign

The other compound assignment operators involve bit shifting and bitwise operators. They are <<=, >>=, &= (AND), |= (OR), and ^= (XOR).

It’s easy to see how += and -= work, but what about /= and *=? Let’s take a look at an example:

func power(base: Int, exponent: Int) -> Int
{
    var result = 1

    for _ in 0..<exponent {
        result *= base
    }

    return result
}

let value = power(base: 2, exponent: 4)
print("2 to the power 4 is \(value)")
// Output: 2 to the power 4 is 16

In the above function, we’re calculating the power (exponent) of a number, or result = baseexp. We do this by multiplying result and base a number of times in a for loop.

In the function power(), we’re using the compound assignment operator *= to multiply result with base and assign the value back to result. Neat!

Comparison Operators

Alright, let’s move on to comparison operators. You use comparison operators in Swift to compare things with each other. A comparison operator will always return a boolean, i.e. true or false, of type Bool.

We’ve got 6 comparison operators in Swift:

  • a == b: Equal to
  • a != b: Not equal to
  • a > b: Greater than
  • a < b: Less than
  • a >= b: Greater than or equal to
  • a <= b: Less than or equal to

If you look closely, you’ll see that comparison operators have 3 groups of sorts: equal or not, greater/less than, and greater/less than or equal. Especially the >= and <= operators will come in handy.

Identity Operators

Swift also has 2 identity operators, === and !== (“triple bar”). You use them to check if 2 values are identical to each other. Note the difference between equality and identicality:

  • Equality: Values are said to be equal when their representations are the same, according to the type you’re using.
  • Identicality: Objects are said to be identical when they refer to the exact same object.

You may find that 2 pairs shoes of the same type are equal because they have the same brand, type and color, but they aren’t identical because they’re worn by 2 different persons. Your shoes aren’t their shoes (unless you share them, which would be mildly disturbing).

Equal and Not Equal

You use a == b and a != b to check if 2 values are equal to or not equal to each other. Both of these expressions return a value that’s either true or false, which means you’ll typically use the equality operators – and other comparison operators – together with if statements.

Let’s take a look at an example:

for i in 0..<5 {
    if i == 3 {
        print("i is 3")
    } else {
        print("i is not 3")
    }
}

The output of the above code is:

i is not 3
i is not 3
i is not 3
i is 3
i is not 3

In the above code, we’re using the == operator to test if i is equal to 3. If it is, we’ll print out a bit of text. With the else clause in the conditional, we’re taking action when i == 3 evaluates to false.

Because comparison operators like == return either true or false, we can invert the above conditional. Like this:

if i != 3 {
    print("i is not 3")
} else {
    print("i is 3")
}

The result of both pieces is the same, because the == operator is the opposite of !=. When the expression a == b returns false, the expression a != b returns true. That’s because true and false are opposites. Swift is logical like that!

Quick Note: If you want to learn more about logic, conditionals and how to use them, check out this tutorial: Conditionals in Swift with If, Else If, Else

Greater Than, Less Than, Or Equal

Next up, let’s talk about the other 4 comparison operators. They are:

  • a > b: Greater than
  • a < b: Less than
  • a >= b: Greater than or equal to
  • a <= b: Less than or equal to

You can use these operators on numbers or on strings. They let you compare things with each other. For example, if one number is greater than the other. Just like == and !=, the above operators will always return either true or false.

Of course, 5 > 4 will always return true. In programming you often work with user input, or dynamic values from some data source, which means you’ll want to test that variable against a fixed number. That’s where comparison operators can help.

Check out the following example:

let numbers = (0..<10).map { _ in Int.random(in: -10...10) }

for number in numbers {
    if number > 0 {
        print("\(number) is above zero")
    }
}
// Output: 4 is above zero, 1 is above zero, 10 is above zero

In the above code, we’re using the > operator to check if number is greater than zero. If the expression number > 0 returns true, we’ll print a bit of text. The first line of code generates an array of 10 random numbers between -10 and +10.

Let’s take a look at another example:

let likes = 3

if likes == 0 {
    print("0 likes")
} else if likes == 1 {
    print("1 like")
} else if likes >= 2 {
    print("\(likes) likes")
}
// Output: 3 likes

In the above example, we’re using the equal to == and greater than or equal to >= operators to compare the value of the likes constant. Based on its value, we print a plural/singular line of text: 0 likes, 1 like, 3 likes.

As you’ve guessed, the a >= b operator returns true if a is greater than b or if a is equal to b. It’s == and > rolled into one. But why do you need that >= operator, if you’ve got == and >?

In programming, there’s always more ways than one to solve a problem. Your job as a coder isn’t necessarily to solve the problem, but to find the approach that’s easiest to implement, or the most bug-free, or the easiest to maintain and extend in the long run. You’ve got many tools in your toolkit to get there!

Comparison operators will often revolve around the bounds of things. For example, we can assert that the last index number of an array in Swift is equal to the size of that array minus one.

The first index outside the bounds of that same array is also equal to or greater than the number of items in the array. The operators >= and <= help you with correctly expressing those bounds in code, without needing to + 1 or - 1 everywhere.

Comparing Strings

Before we move on, one last thing: you can also compare strings with each other in Swift. If you give each letter from A to Z a number, you’ll see that you can compare text just as you would numbers.

Here’s an example:

let names = ["Ford", "Arthur", "Zaphod", "Trillian"]
print(names.sorted(by: <))
// Output: ["Arthur", "Ford", "Trillian", "Zaphod"]

In the above code, we’re using the less than operator < to sort the array with names alphabetically in ascending order (A-Z). The sorted(by:) function will compare each of the strings in names against each other, and come up with a definitive sort order.

let value = "Ford" > "Arthur"
print(value)
// Output: true

For example, the name Ford is greater than Arthur because the F comes after the A in the alphabet.

Logical Operators

So far, we’ve looked at mathematical operators like + and -, at (compound) assignment operators like = and +=, and at comparison operators like != and >. The fact that comparison operators, like a > b, produce boolean values is the perfect segway into the next topic: logical operators.

With logical operators you can transform boolean values. They produce a boolean value based on some logical rule. This allows you to create complex logical expressions. You can use these to take decisions in your code.

Swift has 3 common logical operators:

  1. a && b: Logical AND
  2. a || b: Logical OR
  3. !a: Logical NOT

Logical operators take boolean values as inputs, and produce a boolean value as output. You’ll always use &&, || and ! together with true and false. As you’ve guessed, the inputs for logical operators often come from the results of comparison operators.

Let’s take a look at an example:

if threatLevel > 5 && officerRank >= 3 || isPresident {
    print("!!! STRATEGIC CANDYBAR RESERVE DOORS OPENING !!!")
} else {
    print("no candy for you")
}

In the above if statement, we’ve got an expression that uses many operators, including && and ||. Let’s break that down:

  • threatLevel > 5 && officerRank >= 3 ··· (AND)
  • ··· || isPresident (OR)

We can assert that the doors to the Strategic Candybars Reserve will open if:

  • The threat level is greater than 5 AND your officer’s rank is greater or equal to 3
  • OR you’re the president

Double-check this with me: If you’re the president, the doors will open anyway. And if your officer rank is equal or over 3, and the threat level is sufficiently high – the doors open as well. We can assert that a high threat level and officer rank merits the same “authority” as the president.

Based on what we’ve discussed so far, you see how the && (AND) and || (OR) operators will produce a boolean result based on some rules. In short, we can summarize those rules like this:

  1. a && b (AND) returns true if both a and b are true
  2. a || b (OR) returns true if either a or b is true

You can also put those rules in a truth table. Like this:

a b AND OR
true true true true
true false false true
false true false true
false false false false

See what’s happening? The OR operator will return true if either operand is true. The AND operator will only do that if they’re both true. Neat!

This leaves the !a NOT operator. How it works is simple: it’ll invert the value a. So when a is true, !a is false. And when a is false, !a is true.

Here’s a lovely truth table for the NOT operator:

a NOT
true false
false true

Consequently, !!true – a valid expression – is… not not true, which is not false, which is true. Intriguing, right? You wouldn’t use that in everyday programming, but it goes to show that the logical statements in your code are solid, cast in stone. It’s either true or false – no doubt about it.

This also means you can often invert or switch logical operations to make them easier to comprehend. A logical expression says as much about what it confirms, as what it denies. Check this out:

func openDoorsIfAllowed() 
{
    if threatLevel <= 5 && officerRank < 3 && !isPresident {
        return
    }

    toggleDoors(.open)
}

See what’s going on in the above code? Take a minute to compare it to the previous conditional.

We’re playing around with code here. The outcome of the above conditional looks almost the same as the previous one. In this function, it makes sense to exit the function when the conditions for opening the doors aren’t valid.

If isPresident is true, that’ll cause the condition to fail, which opens the doors. If threatLevel is greater than 5 or officerRank is greater than or equal to 3 or isPresident is true, the doors open as well.

Wait a minute… You see 2 && operators, right? Then why are we talking about OR there? That’s because the && operator returns false if either of the operands is false. As it turns out, both conditionals aren’t exactly the same – but they look a lot alike!

Quick Note: When in doubt, break up your logical expression into multiple if statements. Coding 2 separate if blocks is the same as “OR” (if this, or if that), and 2 nested if blocks is the same as “AND” (if this and then if that).

Range Operators

The Swift Programming Language also provides operators that make your life as a coder easier, such as range operators. They don’t involve math or logic. Instead, you can use them to concisely code something that normally would have taken more code to get to the same result. This is often referred to as “syntactic sugar”.

You use range operators in Swift to denote a range between values, like from zero to 100. You can also use ranges with indices in collections, which is quite handy.

Swift has 2 range operators, in 2 groups:

  1. a ... b: The closed range operator, so a range from a to b including b
  2. a ..< b: The half-open range operator, so a range from a to b but not including b

You can use both the ... and ..< operators for full ranges, and for one-sided ranges. Let’s take a look at a few examples, to understand how that works.

First, the closed range operator:

let numbers = Array(1...10)
print(numbers)
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Then, the half-open range operator:

let numbers = Array(1..<10)
print(numbers)
// Output: [1, 2, 3, 4, 5, 6, 7, 8, 9]

See how the first example includes numbers including 10, and the second does not? That’s the difference between ... and ..<. The half-open range operator is especially useful when dealing with arrays. Check this out:

let names = ["Arthur", "Ford", "Zaphod", "Trillian", "Marvin"]

for i in 0..<names.count {
    print(names[i])
}
// Output: Arthur, Ford, Zaphod, ···

See how we’re using the ..< operator to make a range of integer numbers from zero up to but not including names.count? That’s because the size of an array is equal to its last index number. If we would have used ..., we would have run into that index out of bounds problem.

You can also use the ... and ..< operators to get a slice of an array, i.e. part of the array as defined by the bounds of the range. Check this out:

print(names[0..<names.count]) // Full range
// ["Arthur", "Ford", "Zaphod", "Trillian", "Marvin"]

print(names[0...]) // One-sided, full range
// ["Arthur", "Ford", "Zaphod", "Trillian", "Marvin"]

print(names[2...]) // One-sided, index 2 up to end
// ["Zaphod", "Trillian", "Marvin"]

print(names[...2]) // One-sided, beginning up to index 2
// ["Arthur", "Ford", "Zaphod"]

print(names[..<2]) // One-sided, beginning up to but not incl. 2
// ["Arthur", "Ford"]

print(names[2..<4]) // Full range, 2 to 4 (not including)
// ["Zaphod", "Trillian"]

Quick Note: In the above code, 2 refers to the third item in the array. This item has integer index number 2. Arrays indices in Swift start at zero!

Really Cool Operators

Swift has 2 really cool operators: the nil-coalescing operator and the ternary conditional operator.

Nil-coalescing Operator

Check this out:

let author:String? = nil
let label = author ?? "Unknown author"
print(label)
// Output: Unknown author

In the above code, we’re using the nil-coalescing operator a ?? b to provide a default value if author is nil. The nil-coalescing operator will return a if a is not nil, and if a is nil, it’ll return b. The ?? operator also unwraps the optional.

Like this:

let author:String? = "J.K. Rowling"
let label = author ?? "Unknown author"
print(label)
// Output: J.K. Rowling

This is the exact same code, except now the optional author has a string value instead of nil. Unlike before, the value of author is assigned to label, which subsequently prints the string "J.K. Rowling".

The code we’ve used so far is identical to the code below, which goes to show that the a ?? b operator saves you from writing a whole lotta code.

let author:String? = nil
var label = ""

if author != nil {
    label = author!
} else {
    label = "Unknown author"
}

print(label)

Ternary Conditional Operator

The only operator in Swift that has 3 operands is the ternary conditional operator. Let’s look at an example:

let temp = 25 // °C
let value = temp > 18 ? "it's hot" : "it's cold"
print(value)
// Output: it's hot

In the above code we’re using the a ? b : c operator to choose between b and c based on the expression a. When a is true, b is returned, and when a is false, c is returned. It’s a short-hand for a conditional.

The above code is exactly the same as this:

let temp = 25 // °C
var value = ""

if temp > 18 {
    value = "it's hot"
} else {
    value = "it's cold"
}
// it's hot

Just as the nil-coalescing operator, the ternary conditional is much more concise. This usually – not always – makes your code easier to read and comprehend.

Did you know you can implement the ?? operator with ?:, and vice versa? It’s a bit silly, of course – but nevertheless, you can, because they’re both syntactic sugar around more verbose bits of code. Check this out!

1. a ? b : c with a ?? b:

let temp = 25 // °C
let value = { 
    if temp > 18 { return "it's hot" } else { return nil }
}() ?? "it's cold"

print(value)
// Output: it's hot

2: a ?? b with a ? b : c:

let author:String? = nil
let label = author != nil ? author! : "Unknown author"
print(label)
// Output: Unknown author

Precedence, Associativity and Grouping

Before we call it quits, we’ll have to discuss 3 important but often overlooked concepts for working with operators in Swift. They are:

  1. Precedence: When using multiple operators together, which operations are done before the others?
  2. Associativity: When 2 operators have the same precedence, do the operands on the left or the right belong to the operator?
  3. Grouping: Changing the precedence of operators by grouping operators together within parentheses ( ) or emphasizing their existing order by adding parentheses ( ) (without changing their order).

Precedence

First, let’s talk about precedence. It sounds more complex than it actually is! Check out the following calculation:

let a = 2 + 3 * 4
print(a)
// ???

What’s the value of a in the above code?

  1. 20
  2. 14

*tick* *tock* *tick* *tock*

It’s 14! Because multiplication goes before addition, so the result 3 * 4 is calculated first (12) to which 2 is added (14). The other way around – which is incorrect – would be 2 + 3 = 5 times 4 is 20.

This is a simple example, but it gets more complicated if you consider all operators that Swift has. Precedence, i.e. the order of operators, not only affects arithmetic, but also extends to comparison- and logical operators.

Let’s discuss a few ways of looking at this. In practical, day-to-day Swift programming, you’ll need to remember this:

  1. Precedence rules follow the rules you learned in math: * / + -
  2. Math operators go before comparison operators go before logical operators
  3. Logic operator rules: && (AND) goes before || (OR)
  4. Prefix operators !a, +a and -a go first

That’s it! This should get you pretty far. When in doubt, you can always look up the documentation on precedence rules in Swift.

But what’s the real explanation here, beyond shorthands? In reality, precedence rules are divided into groups. The precedence rule for the group determines what, for example, the order of logical operator && is in combination with >= and +.

In short, the first-to-last order is:

  1. MultiplicationPrecedence for * / %
  2. AdditionPrecedence for + -
  3. RangeFormationPrecedence for ranges
  4. CastingPrecedence for type casting operators
  5. NilCoalescingPrecedence for ??
  6. ComparisonPrecedence for all comparison operators
  7. LogicalConjunctionPrecedence for && ||
  8. TernaryPrecedence for ?:
  9. AssignmentPrecedence for all assignment operators

Awesome!

Associativity

In the above list for precedence you can see that multiple operators are grouped together. How do you determine the order of operators in a group, when they have the same importance? That’s where associativity comes in.

You’ve got 3 types of associativity:

  1. Left-associative: Grouped from the left, so left-most operator “grabs” left-most operands first
  2. Right-associative: Grouped from the right, so right-most operator “grabs” the operands first
  3. Non-associative: Operators cannot be grouped (most notably, comparison operators b/c their input type is different from their output type)

Here’s a simple example:

let a = 7 - 4 + 2
print(a)
// Output: ?

We’ve seen in the list for precedence that both the a + b and a - b operators have the same precedence group. When you combine them, which operation goes first?

  • (7 - 4) + 2 = 5 (left-associative)
  • 7 - (4 + 2) = 1 (right-associative)

Arithmetic operators like + - * / are left-associative. This means that, when they’re used together, we can “grab” the left-most operation together. Kinda lika a cowboy throwing a lasso/rope around them. Like this:

let a = (7 - 4) + 2
print(a)
// Output: 5

The operation 7 - 4 = 3 goes first, after which 2 is added, resulting in 5. Because - and + are left-associative, the left-most operator groups its operands (7 and 4), which goes first, and then + goes second. This results in a definitive order of operations!

In short, associativity rules for operators in Swift are as follows:

  1. * / % + - are left-associative
  2. ?? and ?: are right-associative
  3. && and || are left-associative
  4. = and assignment operators are right-associative

What about groups that have no associativity? In Swift, all comparison operators are non-associative. They do not have associativity rules between them. How is that even possible!?

It’s simpler than it looks. Because comparison operators take numbers (and strings) as input, but produce booleans, you’d end up with a type conflict if they were left- or right-associative. It’s only logical to either avoid that conflict, or to throw an error because the expression is invalid.

let a = 5 > c < 4
// Invalid expression – this wouldn't make sense!

On top of that, you only combine comparison operators with logical operators (if at all). Logical operators are left-associative, which means you can use their associativity to prevent a conflict between multiple comparisons.

let a = followerCount >= 0 && userLevel > 3 || !isLoggedIn
// What's the order? Comparison first, then &&, then ||

Who grabs the operand userLevel > 3 in the above code? It’s the &&, because AND goes before OR. There is no conflict among the comparison operators, because they aren’t combined and belong to their own group.

Grouping

You can create groups in your expressions with parentheses ( ). This enables you to change the order of operations, because the expressions within the group are evaluated before the ones outside of it.

You can do this for 2 reasons:

  1. Improve the readability of your code, to make implicit precedence rules clearer
  2. Change the order of operations for expressions that would otherwise return an incorrect result

Let’s go back to that previous conundrum with + and *. This one:

let a = 2 + 3 * 4
// Output: 14

What if you actually needed 20 here, instead of 14? That’s when you can use grouping with parentheses to get to the right result. Like this:

let b = (c + d) * e

In the above code, the expression c + d is calculated before multiplying its result with e. If you wouldn’t have used the parentheses, the calculation d * e would have gone first.

The other reason to use grouping with parentheses is to make an expression clearer. We’ve discussed precedence and associativity rules, but they aren’t always as easy to remember. On top of that, most conditional expressions involve complex variable names that can impair readability.

You can help yourself and other developers by grouping expressions together, according to their pre-existing precedence rules, to emphasize the order of operations. Take these 2 expressions, for example:

if threatLevel > 5 && officerRank >= 3 || isPresident { ···
if (threatLevel > 5 && officerRank >= 3) || isPresident { ···

The results of both conditionals are exactly the same. We know that && goes before ||, and the officerRank >= 3 isn’t grabbed by the || operator. Instead, they’re left-associative, which means it’s an operand for the && operator and not ||.

We’re emphasizing this order with the parentheses. At a glance, we can see that the code inside the parentheses is evaluated first. Its result is then evaluated with ··· || isPresident.

You’ll want to use this sparingly, of course, and not as an excuse to avoid learning more about precedence rules and associativity. If you add parentheses for all precedence rules involved in your expression, you lose the effect of hinting at what goes first.

Custom Operators

Last but not least: custom operators. You can build your own operators in Swift! Let’s take a look at an example:

infix operator ^^

func ^^ (base: Int, exponent: Int) -> Int {
    var result = 1

    for _ in 0..<exponent {
        result *= base
    }

    return result
}

let value = 2 ^^ 4
print(value)
// Output: 16

In the above code, we’ve created an infix operator a ^^ b that calculates the power of a number. Apart from the first line of code, this custom operator looks exactly like an ordinary Swift function.

You’ll need 2 things to create a custom operator in Swift:

  1. Operator declaration, like infix operator ^^
  2. Implementation of the operator with a function

In the above code, on the first line, we’re creating a custom operator ^^. It’s an infix operator, so it’ll go between 2 operands. We could also have chosen postfix or prefix. The declaration also includes the word operator.

Then, we’re declaring a function with the “name” of the operator. We’re giving this function 2 parameters, one on each side of the operator. You’ll often see these parameters as lhs and rhs, meaning left-hand side and right-hand side. For our ^^ operator, it makes more sense to call them base and exponent.

In the last part of the code, we’re putting the a ^^ b operator to good use. 2 to the 4th power is 16!

You’ll notice that the ^^ operator, as implemented above, is limited to the integer Int type. You can only use it with integers; use it with anything else and you’ll get a type error.

The power of custom operators lies within its combination with existing Swift types. You can use extensions, generics, protocols and “where” to add custom types to existing types in Swift. Thanks to the flexibility of Swift’s type system, you can declare your custom operator once and use it with any or all types.

Check this out:

prefix operatorextension Sequence where Self.Element: Numeric {
    static prefix func(items: Self) -> Self.Element {
        return items.reduce(0, { $0 + $1 })
    }
}

The above code adds a prefix operator (Capital sigma) to the Sequence type, via an extension. The operator produces the sum of the sequence’s members.

Using the where keyword, this extension only applies to sequences whose elements are numeric. You can use the operator on arrays, sets, sequences, ranges of integers, doubles, floats, etcetera.

∑(0...10)
// 55

∑[1, 2, 3]
// 6

∑[0.1, 3.9, 4.4, -2]
// 6.4

∑["Ford", "Trillian", "Arthur"].map { $0.count }
// 18

Awesome!

Quick Tip: You can also add existing operators to custom types in Swift. You can, for example, add the a + b operator to your own struct, to be able to add 2 of the struct’s instances to each other! The code you need to do this is exactly the same as discussed in this section, except for the operator ··· declaration.

Further Reading

Pfew! Operators look so small but they can do so much. In essence, they’re tiny functions you can stick between a bunch of operands and voilá – you’ve got a result.

In this tutorial, we’ve discussed math-, assignment-, comparison-, range- and logical operators. We came across really cool operators like ?? and :?. We’ve talked about operator precedence, associativity and grouping. And finally, we even built our own custom operator.

This tutorial pairs well with a few more in-depth guides:

Want to learn more? Check out these resources:

LearnAppMaking

LearnAppMaking

At LearnAppMaking.com, app developers learn how to build and launch awesome iOS apps.