Overloading functions or methods in Swift is a task that developers perform often. The technique that lies behind that term allows to create functions that have something in common; they share the same name, but there are certain rules to follow. Phrasing that in a more elegant way:
The definition of two or more functions or methods with the same name but different type of parameters, different number of parameters, or different argument labels, is what we call function overloading. Note that the return type plays no role at all.
Function overloading is particularly helpful in cases where the same or similar result or functionality is desirable under more than one circumstances. Think of the following example as a first taste; we could implement a function in order to fetch data from an SQLite database providing just the table name. But in addition to that, we could also implement another function accepting one more argument besides the table; a condition, based on which a where clause will limit retrieved data. Both these functions can have the same name, and given that they have different number of parameters, it’s perfectly acceptable to define them. Xcode won’t complain about them. Moreover, it will list them one after another in auto-suggestion while typing their name.
As you understand, function overloading allows to implement code that offers flexibility and convenience. It’s obviously not necessary to adopt it as a technique, and define functions and methods performing similar tasks using unrelated naming. However, having similarly named functions while altering the parameters has a real practical value, and makes the coding easier and faster.
The following parts of this post demonstrate what function overloading is through a few simple examples. Even though I intentionally chose to keep things simple enough, whatever you will read fits perfectly to real world cases.
Overloading functions with parameters of different types
Suppose that we want to define a function that calculates the square of an integer number. The following one does that:
1 2 3 4 5 |
func square(of number: Int) -> Int { number * number } |
Say now that we want to perform the exact similar thing, but this time for Double numbers. In this case, we could define the next function:
1 2 3 4 5 |
func square(of number: Double) -> Double { number * number } |
With the above, we just overloaded square(of:)
. We have a new function similarly named as the previous one, with same argument label and parameter name, but a different parameter type; Double instead of an Int.
We can now call square(of:)
providing either an Int or a Double argument; the proper function will be automatically called, returning the proper results:
1 2 3 4 5 6 7 8 |
print(square(of: 5)) print(square(of: 2.5)) // It prints: // 25 // 6.25 |
Functions that we overload can have as many or as few parameters as we want. Note, however, that similarly named functions with no parameters have nothing to differentiate them, so they cannot be overloaded. For example, something like the next one is not correct:
1 2 3 4 5 6 7 8 9 |
func message() { print(“Hello”) } func message() { print(“Hi”) } |
We have two functions with the same name here, and nothing more. In cases like that, Xcode will show an error pointing to the second function, saying:
1 2 3 |
Invalid redeclaration of ‘message()’ |
The following is another example of function overloading. Both functions calculate the summary of the provided arguments; the first works for Int numbers, the second for Double:
1 2 3 4 5 6 7 8 9 |
func add(num1: Int, num2: Int) -> Int { num1 + num2 } func add(num1: Double, num2: Double) -> Double { num1 + num2 } |
Once again, the proper function will be automatically called, depending on the type of arguments that we’ll provide:
1 2 3 4 5 6 7 8 |
print(add(num1: 5, num2: 10)) print(add(num1: 2.5, num2: 3.4)) // It prints: // 15 // 5.9 |
Function overloading with different number of parameters
Getting going with the exploration of function overloading in Swift, consider the following two demo structures along with the two overloaded functions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
struct Meat { var ingredients = “????????????” } struct Vegetables { var ingredients = “????????????????” } func cook(food: Meat) { print(“Cooking \(food.ingredients)…”) } func cook(food: Vegetables) { print(“Cooking \(food.ingredients)…”) } |
Even though both functions have the same parameter name, the data types are not the same; the first one gets a Meat instance as argument, while the other a Vegetables instance.
In this imaginary example, the first function is handy when we want to cook meat. The second function is needed when we cook vegetables. However, it’s not always desirable to cook meat and vegetables separately; we can also cook them together and create a proper meal.
To do that programmatically, we would need a new function that would mix Meat and Vegetables instances. Although we can define such a new function and give it any name we’d like, we’ll stick to cook
by introducing a new overload:
1 2 3 4 5 |
func cook(meat: Meat, and vegetables: Vegetables) { print(“Cooking \(meat.ingredients) with \(vegetables.ingredients)… Yummie!”) } |
Unlike the other two, this function does not have the same number of parameters; it requires two arguments to be provided with so it can properly serve its goal.
Here’s the results of calling the above with the proper arguments:
1 2 3 4 5 6 7 8 9 10 |
cook(food: Meat()) cook(food: Vegetables()) cook(meat: Meat(), and: Vegetables()) // It prints: // Cooking ????????????… // Cooking ????????????????… // Cooking ???????????? with ????????????????… Yummie! |
If only we could cook just like that! One day, maybe. Until then, let’s take a look at one more example closer to real world.
Assume that the following function is building a Select query in order to fetch data from the given table into an SQLite database:
1 2 3 4 5 |
func select(from table: String) -> String { “select * from \(table)“ } |
Such a simple query would be useful in just a few cases; it would be way more handy if we could also provide a condition. To manage that, let’s define a new function. We’ll name it similarly to the previous one. But it will have one more parameter besides table
; the condition that we want to append to the query:
1 2 3 4 5 |
func select(from table: String, where condition: String) -> String { “select * from \(table) where \(condition)“ } |
The above clearly demonstrates another function overloading case. We have two functions with the same name, but not the same number of parameters. Depending on the task, we may use either the one or the other. We could add more functions in a similar fashion in order to build more complicated queries. However, regardless of the number of overloaded functions we define, there is just one name to remember, and the greatest thing is that Xcode will suggest them all automatically by starting typing select
. This might be a pretty simple example, however such a scenario is not far from reality at all.
Here is a demo of calling the above functions:
1 2 3 4 5 6 7 8 |
print(select(from: “Products”)) print(select(from: “Products”, where: “id > 5”)) // It prints: // select * from Products // select * from Products where id > 5 |
Function overloading with different argument labels
Besides the cases presented in the previous parts, it’s also possible to overload functions and at the same time to keep similar parameter names to all. But there is a requirement in order to do that; it’s necessary to specify argument labels, and those should be different from function to function.
To make that clear, see the following example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
struct Shape {} func draw(line shape: Shape) { print(“Drawing line…”) } func draw(rectangle shape: Shape) { print(“Drawing rectangle…”) } func draw(circle shape: Shape) { print(“Drawing circle…”) } draw(line: Shape()) draw(rectangle: Shape()) draw(circle: Shape()) // It prints: // Drawing line… // Drawing rectangle… // Drawing circle… |
Here we have three overloads of the draw
function, and the parameter name remains the same to all of them (shape
). Notice, however, that the argument label is different in each function; in order of appearance, labels are line
, rectangle
, and circle
. Each label represents the hypothetic shape object we want to draw, and makes clear which function exactly we need to call.
Summary
Overloading functions in Swift is not a difficult thing to do; probably new developers perform it before even learn the name of the technique. And in this post I showed you a few different examples of overloading, along with the few rules that go with. Keep in mind that whatever discussed in the previous lines apply on methods too, not just simple functions. One subtle thing to note is that you shouldn’t confuse overloading with overriding; although overriding refers to methods too, it’s a totally different concept. Anyway, I hope that you’ve learnt something valuable here today. Thank you for reading!
This post has also been published on Medium.