Table of Contents
Operators are one of the basic constructs of any programming language. They are represented as symbols and have various associated properties, and understanding them is crucial to mastering any programming language.
In this article, we’ll look at some of the operators that Swift ships with and also create our own operators.
What is an operator in Swift?
Before we begin looking at the different operators that Swift provides us with and creating custom operators, let’s understand a few basic terms related to them.
Here’s an example:
let a = 5 let b = 10 let sum = a + b
Here, we can see a few variables and constants being defined, along with a few symbols (e.g.,
+). Let’s define two terms here:
- Operators: any symbol that is used to perform a logical, computational, or assignment operation.
=is used to assign and
+is used to add in the above example and are therefore operators
- Operands: variables on which operations are performed, e.g., on line 1 we have
aas an operand, and on line 3, we have
bas two operands on which the
+operator is being applied
Types of operators in Swift
There are three different types of operators, which are defined by the number of operands they work on.
- Unary operators: operators that work on only one operand (e.g.,
- Binary operators: work on two operands, such as
- Ternary operator: works on three operands, e.g.,
Notation defines the position of the operator when used with the operands. There are again three types:
Infix: when the operators are used in between operands. Binary and ternary operators are always
Prefix: when the operators are used before an operand, e.g.,
Postfix: when the operators are used after an operand, e.g.,
Operator precedence and associativity in Swift
When working with operators in Swift, you should also know the priority order in which the operator is executed. Operator precedence only matters for
infix operators, as they work on multiple operands.
For example, if an expression has multiple operators in it, Swift needs to know which operator needs to be executed first.
var result = 4 + 6 * 2 // 6*2 is done first which yields 12, then we do 4 + 12 = 16
In the above example,
* has more precedence compared to
+, which is why
6*2 is executed before
In case you have two operators with the same precedence being used in an expression, they’ll fall back on their associativity. Associativity defines the direction in which the expression will start resolving in case the precedence of the operators are the same.
Associativity is of two types:
left: this means that the expression to the left will be resolved first
right: this means the expression to the right will be resolved first
For example, we have the following statement:
let result = 5 + 10 - 2 //13
- have the same precedence, Swift falls back to the associativity of the operators.
- have associativity of
left, so we resolve the expression from the left so that
+ is evaluated first.
Here’s a table of the precedence order that Swift follows. The table lists the precedence in the order of decreasing priority.
|Precedence Name||Operators Included|
Now that we know the basics of what Swift operators are and how they are used, let’s look at popular operators that ship with Swift.
Common operators in Swift
The most common operators that Swift ships with fall under the following categories:
1. Assignment operator
Assignment operators are used to assign values to a constant or variable. The symbol is
2. Arithmetic operators
These operators are used to perform basic arithmetic operations, such as
3. Logical operators
Logical operators are used to combine two conditions to give a single boolean value output, e.g.,
4. Comparison operators
These operators are used to compare numbers and give out a
5. Ternary operator
This operator is a shorthand operator for writing if-else conditions. Even though it does the same job, it is not a good idea to consider it a replacement for if-else conditions because it hampers readability.
Therefore, when you have different code blocks to run for different conditions, it’s better to use if-else blocks. For shorter, simpler cases, use the ternary operator. The operator,
?:, is used like this:
var value = 5 > 2 ? "5 is greater than 2" : "5 is less than 2"
6. Nil coalescing operator
This is a shorthand operator to return default values in case a particular variable is nil. The operator symbol is
?? and commonly used like this:
var identity: String? = nil let nonNillableIdentity: String = identity ?? "Jane Doe" // since `identity` is nil, we return a default value of Jane Doe and store it in the nonNilalbleIdentity variable
7. Range operators
These operators are used to define ranges, e.g.,
..<. They are mainly used with array/strings to extract sub arrays/strings.
var array = [1,2,3,4,5] let a = array[1...3] // [2,3,4] let b = array[1..<3] // [2,3] let c = array[2...] // [3,4,5] let d = array[...3] // [1,2,3,4]
For a list of all the basic operators and their descriptions, read the Swift documentation.
Creating custom operators in Swift
Now that we’ve seen the operators that Swift ships with, let’s create our own operators. Custom operators are generally defined for three purposes:
- To define a completely new operator because you think the symbol’s meaning can express what the operator will do better than a function with a name can
- To define an operator that exists for basic data types like numbers or strings but does not exist for classes or structs that have been defined by you
- You want to override an operator that already exists for your classes and structs, but you need it to behave differently
Let’s look at all three purposes with examples.
Creating a new operator with a new symbol
Let’s say you want to define an operator with the
△ symbol that calculates the hypotenuse of a right angle triangle given its two sides.
infix operator △ func △(lhs: Double, rhs: Double) -> Double return sqrt(lhs*lhs + rhs*rhs) var hypotenuse = 3△4 // 5
Creating an operator for a custom class or struct
We know that the
+ operator is used to add two numbers, but let’s say we wanted to add two instances of our struct called
Velocity, which is a two-dimensional entity:
struct Velocity var xVelocity: Double var yVelocity: Double
If we were to run the following command, you’d get a compiler error:
let velA = Velocity(xVelocity: 2, yVelocity: 4) let velB = Velocity(xVelocity: 2, yVelocity: 4) let velC = velA + velB // Binary operator + cannot be applied to two Velocity operands
In this case, it makes sense to define a
+ operator that takes care of adding two
extension Velocity static func +(lhs: Velocity, rhs: Velocity) -> Velocity return Velocity(xVelocity: lhs.xVelocity + rhs.xVelocity, yVelocity: lhs.yVelocity + rhs.yVelocity)
Now the earlier statement will execute properly.
Overriding a Swift operator that already exists for a class/struct
Let’s say you have a struct that is used to define an item in a supermarket.
struct Item: Equatable let id = UUID() let itemName: String let itemType: String let itemPrice: Double
When we need to compare two items, such that if two items have the same
itemPrice, we need to consider them equal regardless of what their
If you run the following piece of code, you’ll see that the two variables are not equal:
let detergentA = Item(itemName: "Tide", itemType: "Detergent", itemPrice: 56.5) let detergentB = Item(itemName: "Tide", itemType: "Detergent", itemPrice: 56.5) let areDetergentsEqual = detergentA == detergentB // false
The reason for this is that instances
detergentB have different
ids. In this case, we need to override the
== operator and custom logic to determine equality.
extension Item static func ==(lhs: Item, rhs: Item) -> Bool return lhs.itemName == rhs.itemName && lhs.itemPrice == rhs.itemPrice && lhs.itemType == rhs.itemType
Now that we know the different scenarios in which we define custom operators, let’s define our custom operator.
Defining a custom operator in Swift
A custom operator is defined just like a function is defined in Swift. There are two types of operators you can define:
- Global operator
- Operator specific to a class/struct
The definition looks like the following:
func <operator symbol>(parameters) // operator logic
There are different types of parameters you can define based on whether it is an
infix or a
If it’s an
infix operator, you can have two parameters, and, when it’s a
postfix operator, you can have a single parameter.
With global operators, we first define the operator with the notation keyword (
postfix) and the
operator keyword, like this:
<notation> operator <operator symbol>
Here’s an example:
infix operator ^ // the operator is an infix operator with the symbol ^
Operator for class/struct
When we want to define an operator for a class or a struct, we need to define the operator function as a static function. The notation parameter is optional in case the operator is an
infix operator (i.e., you have two parameters in the function signature), else you need to specify
extension <class name> static <notation> func ^(parameters) // note that this needs to be static function // operator logic
Given that we’ve already looked at defining basic custom operators like
==, let’s review some examples of how you can define custom compound operators or operators that mutate the parameters themselves.
We’ll do this by defining a custom compound arithmetic operator for the
Velocity struct from earlier:
extension Velocity static func +=(lhs: inout Velocity, rhs: Velocity) // note that the first parameter is an inout variable lhs.xVelocity = lhs.xVelocity + rhs.xVelocity lhs.yVelocity = lhs.yVelocity + rhs.yVelocity
Now let’s execute the following statement:
var velA = Velocity(xVelocity: 5, yVelocity: 5) var velB = Velocity(xVelocity: 5, yVelocity: 5) velA += velB print(velA) // Velocity(xvelocity: 10.0, yVelocity: 10.0)
Now, we’ll specify the notation of a custom operator.
Specifying the notation of a custom operator in Swift
In order to define the notation of a custom operator, you have three keywords for the corresponding notation:
When defining a global operator, you need to first define the operator’s notation, then define its functions. Note that this is not a compulsory step, but it’s necessary if you want to define global operators or hope to assign a precedence to it (which we’ll discuss in the next section).
Let’s look at the hypotenuse operator again, which is being defined as a global operator. You can see that we first define the operator with the notation and the symbol, then implement its logic:
infix operator △ // define the operator with its notation and symbol func △(lhs: Double, rhs: Double) -> Double // implement the operator logic return sqrt(lhs*lhs + rhs*rhs) var hypotenuse = 3△4 // 5
When defining an operator for your own class/struct, you can directly use the notation keyword with the method definition. For example, let’s define a negation operator for
Velocity as well, which negates both the
yVelocity properties. This operator is going to be defined as a
extension Velocity // defining a prefix negation operator static prefix func -(operand: Velocity) -> Velocity return Velocity(xVelocity: -val.xVelocity, yVelocity: -val.yVelocity) let vel = Velocity(xVelocity: 4, yVelocity: 4) print(-vel) // xVelocity: -4, yVelocity: -4
Setting precedence of custom Swift operators
You can also define the precedence of your operator using the precedence keywords, as mentioned in the precedence table above. This is usually done in the definition step:
infix operator ~: AdditionPrecedence // now this operator's priority is the same at the operators mentioned in the AdditionPrecedence func ~(lhs: Int, rhs: Int) -> Double return sqrt(Double(lhs*lhs-rhs*rhs))
This way, the compiler knows which operator needs to be executed first. In case no precedence is specified, it defaults to
DefaultPrecedence, which is higher than
This brings us to the conclusion of what operators are in Swift and how you can create your own. Although they are pretty simple to use, understanding them is important because they are one of the most foundational constructs of any programming language.
You should be familiar with basic operators, as you will be using them frequently in your code, and, when it comes to custom operators, define them only if the symbol’s original meaning makes sense for you.
LogRocket: Full visibility into your web apps
LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.