Operator Overloading in Swift Tutorial
Learn how to extend operators for new types or create entirely new operators in this new Swift tutorial! By Corinne Krych.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Operator Overloading in Swift Tutorial
25 mins
- Operators: An Overview
- Precedence
- Adding Isn’t Just for Ints
- Operator Overloading
- Defining Custom Operators
- Naming Your Operator
- Choosing a Type
- Assigning Precedence and Associativity
- Coding Your Custom Operator
- Bonus Round!
- Remember Related Operators!
- Defining Operators for More Than One Type
- Generics to the Rescue!
- Extending With a Protocol
- How Can I Use Overloading in Real Life?
- Operators and CGPoints
- Overloading in SKTUtils
- Where to Go From Here?
Defining Custom Operators
There are three steps to define a custom operator:
- Name your operator
- Choose a type
- Assign precedence and associativity
Let’s go over these one by one.
Naming Your Operator
Now, you have to pick a character for your operator. Custom operators can begin with one of the ASCII characters /
, =
, -
, +
, !
, *
, %
, <
, >
, &
, |
, ^
, or ~
, or with one of the Unicode characters. This gives you a broad range of possible characters. Don’t go crazy, though—remember that you’ll have to type these characters repeatedly and the fewer keystrokes, the better.
In this case, you’ll copy and paste the Unicode symbol ⊕
, a representation of direct sum that fits your purposes nicely.
Choosing a Type
With Swift, you can define binary, unary and ternary operators. These indicate the number of targets involved.
-
Unary operators involve a single target and are defined either as postfix (
i++
) or prefix (++i
), depending on where they appear in relation to the target. -
Binary operators are infix because they appear in between the two targets, for example
1 + 1
. -
Ternary operators operate on three targets. In Swift, the conditional operator is the only Ternary operator, for example
a ? b : c
.
You should choose between these based on the number of targets of your operation. You want to add two arrays (i.e. 2 targets), so you will define a binary operator.
Assigning Precedence and Associativity
Because operators are defined globally, you need to choose the associativity and precedence of your custom operator with care.
This can be tricky, so a good rule of thumb is to find a comparable standard operator defined in the Swift language reference and use similar semantics. For example, to define vector addition, it makes sense to use the same associativity and precedence as the +
operator.
Coding Your Custom Operator
Back in your playground, enter the following to define your custom operator. You’ll probably want to copy and paste the ⊕ for simplicity.
infix operator ⊕ { associativity left precedence 140 } // 1
func ⊕(left: [Int], right: [Int]) -> [Int] { // 2
var sum = [Int](count: left.count, repeatedValue: 0)
assert(left.count == right.count, "vector of same length only")
for (key, v) in enumerate(left) {
sum[key] = left[key] + right[key]
}
return sum
}
This code is similar to your earlier override, aside from section 1, where you do all of the following:
- Define an infix/binary operator that operates on two targets and is placed in between those targets.
- Name the operator ⊕.
- Set associativity to left, indicating that operands of equal precedence will use left associativity to determine order of operation.
- Set the precedence to 140, which is the same weight as
Int
addition, as found in the Swift language reference.
The code in section 2 is similar to what you’ve seen so far – it adds the two arrays item by item, based on their order in the arrays. Test the new operator by adding the following to your playground:
var sumArray = [1, 2, 3] ⊕ [1, 2, 3]
You’ll see the same results as the earlier function and override, but this time, you have different operators for different semantics.
Bonus Round!
Now that you have a good idea of how to create a custom operator, it’s time to challenge yourself. You’ve already created a ⊕ operator to perform vector addition, so use that knowledge to create a ⊖ operator for subtracting Int
arrays in a similar manner. Give it your best shot, and check your solution against the answer below.
[spoiler title=”Solution”]
infix operator ⊖ { associativity left precedence 140 }
func ⊖(left: [Int], right: [Int]) -> [Int] {
var minus = [Int](count: left.count, repeatedValue: 0)
assert(left.count == right.count, "vector of same length only")
for (key, v) in enumerate(left) {
minus[key] = left[key] - right[key]
}
return minus
}
And for testing:
var subtractionArray = [1, 2, 3] ⊖ [1, 2, 3]
[/spoiler]
Remember Related Operators!
If you define a new operator, don’t forget to define any related operators.
For example, the addition assignment operator (+=
) combines addition and assignment into a single operation. Because your new operator is semantically the same as addition, it’s a good idea to define the assignment operator for it, as well.
Add the following to Operator2.playground:
infix operator ⊕= { associativity left precedence 140 } // 1
func ⊕=(inout left: [Int], right: [Int]) { // 2
left = left ⊕ right
}
Line 1 is exactly the same declaration as for the ⊕ operator, except it uses the compound operator symbol.
The trick is on line 2, where you mark the compound assignment operator’s left input parameter as inout
, which means the parameter’s value will be modified directly from within the operator function. As a result, the operator doesn’t return a value—it simply modifies the input.
Test that this operator works as expected by adding the following to your playground:
sumArray ⊕= [1, 1, 1]
You’ll see the following in the console:
[3, 5, 7]
As you can see, defining your own operators is not difficult at all! :]
Defining Operators for More Than One Type
Now imagine that you want to define vector addition for decimal points, too.
One way is to overload a new operator for the types Double
and Float
, just like you did for Int
.
It’s just a couple of lines of code, but you’ll have to use copy/paste. If you’re like me—a clean code addict—then duplicating code is not your first choice, as it makes your code harder to maintain.
Generics to the Rescue!
Luckily, Swift generics can help with exactly this! If you need a refresher on Swift generics, check out our Swift Generics Tutorial released earlier this week.
Start a new playground so you have a clean slate. Add the following to your new playground:
infix operator ⊕ { associativity left precedence 140 }
func ⊕<T>(left: [T], right: [T]) -> [T] { // 1
var minus = [T]()
assert(left.count == right.count, "vector of same length only")
for (key, v) in enumerate(left) {
minus.append(left[key] + right[key]) // 2
}
return minus
}
On line 1, you define a generic function called ⊕ that works with a placeholder type signified by T
. The playground isn’t happy. You’ll see a compile error that looks like this: Could not find an overload for '+' that accepts the supplied arguments.
The error comes from line 2, where you’re attempting to use the +
operator on the values left
and right
, which are the placeholder type. Swift doesn’t know how to apply a +
operand to those variables, because it has no idea what type they are!