Post

Functions in Swift

Practical guide to Swift functions - declarations, parameters, return types, inout, overloading, function types and higher-order functions with clear examples.

Functions are used in every programming language. They are a block of code can perform a particular task, return back values after some computation, and also be passed around as arguments inside other functions. As usual, we will look at different aspects of functions through maximum code and minimum theory.

Defining Functions

We will create a function that takes two in 2 integers as parameters and returns their product. Look carefully at the syntax used for defining and calling functions —

1
2
3
4
func multiply(number: Int, with: Int) -> Int {
    return number * with
}
multiply(number: 5, with: 6) // Output - 30

We can also create functions that do not take any arguments, but still return something.

1
2
3
func noParameters() -> String {
    return "This function has no parameters!"
}

You can assign the value generated by this function to a variable/constant.

1
let noParameterString = noParameters()

Similarly, we can also create functions that take in arguments but do not return anything.

1
2
3
func noReturnValue(text: String) {
    print(text)
}

The above function would simply print the text provided as argument while calling the function. You’ve already noticed the print() function at a lot of places till now. It is also a function provided by the swift library that prints the contents inside it to the console. As you can see, we can also call other functions inside a function.

Functions with multiple return values

Functions can return multiple values in the form of tuples.

1
2
3
4
5
6
func plusMinus(a: Int, b: Int) -> (plus: Int, minus: Int) {
    return (a + b, a - b)
}
let result = plusMinus(a: 7, b: 4)
print(result.plus) // Output - 11
print(result.minus) // Output - 3

Functions with Optional return types

Functions can return optional types. If you are not familiar with optionals, you can read about them here! We will look at an example where a function returns a tuple in which one of the values is an optional.

1
2
3
4
5
6
7
8
9
10
func plusMinusOptional(a: Int, b: Int) -> (plus: Int, minus: Int?) {
    if a > b {
        return (a + b, a - b)
    } else {
        return (a + b, nil)
    }
}
let optionalResult = plusMinusOptional(a: 5, b: 7)
print(optionalResult.plus) // Output - 12
print(optionalResult.minus) // Output - nil

The above function returns a tuple with the sum and difference of the input arguments. The difference is only return if the first number is greater than the second number, otherwise returns nil.

Functions with Optionals input types

We will also look at an example where the argument type in a function is an optional.

1
2
3
4
func fullName(firstName: String, lastName: String?) -> String {
    if lastName != nil { return firstName + " " + lastName! }
    else { return firstName }
}

In the above example, the function takes in two arguments, out of which the lastName is an optional. Even though one of the argument is an optional, we return a non-optional string as the output. We do that by handling the optional value inside our function as seen above.

In-Out Functions

Parameters in functions are constants by default. You cannot modify them. But in certain scenarios, it is required by the function to change its parameter value. This is similar to passing in variables by reference which you must be familiar with from other languages.

1
2
3
4
5
6
7
var winner = "A"
func change(winner: inout String, to: String) {
    winner = to
}
print(winner) // Output - A
change(winner: &winner, to: "B")
print(winner) // Output - B

Argument Labels and Parameter Names

Functions can have both argument labels and parameter names for its input parameters. They are also known as external and internal names respectively. You use argument labels while calling the function, and parameter names inside the function.

1
2
3
4
func doSomething(with word: String, and number: Int) {
    // Do something here
}
doSomething(with: "A", and: 1)

As we’ve seen in all our previous examples, we can also chose to not have an argument label, in which case the parameter name serves the job of both.

1
2
3
4
func doSomething(word: String, number: Int) {
    // Do something here
}
doSomething(word: "A", number: 1)

We can also chose to omit the argument label, which then would allow us to call the function without having to specify the argument names.

1
2
3
4
func doSomething(_ word: String, _ number: Int) {
    // Do something here
}
doSomething("A", 1)

Function Overloading

In the above examples, you must have noticed we use the same function name for different functions. That is known as function overloading. Now you cannot do that in every scenario, as we need to follow some rules for it. Now we will discuss the conditions under which we are allowed to overload function.

Let’s look at our base function and then understand how can we overload it —

1
2
3
func overloadedFunction(with: String, and: Int) {
    // Some code here
}
Different Argument type
1
2
3
func overloadedFunction(with: String, and: String) {
    // Some code here
}

The base function had a String and an Int as its input while the second one takes in two String.

Different number of Arguments
1
2
3
func overloadedFunction(with: String, and: Int, for: Int) {
    // Some code here
}
Different Argument name
1
2
3
func overloadedFunction(_ with: String, _ and: Int) {
    // Some code here
}

It is important to understand here that the argument name (external name) must be different even though the parameter name can be same.

Different Return type
1
2
3
func overloadedFunction(with: String, and: Int) -> String {
    return with + String(and)
}

Now this is a tricky way of overloading function and can lead to errors in your code. Since we are changing the return type of the function without making any other changes, the caller of this function should be explicitly aware of the type of value it is expecting from this function.

To understand this better, take a look at this example —

overloadedFunction(with: "Test", and: 1)

Since we are not using the returned value from the function, Swift cannot understand if we want to use the first defined function or this last one. This would thus throw us an error.

The correct way to make use of this is —

let index: String = overloadedFunction(with: "Test", and: 1)

Now Swift knows that you are expecting the output of the function to be of type String and thus it would automatically call the last function that returns the type String.

Function Type

Every function in Swift has a type. Let’s look at a simple example —

1
2
3
func sum(a: Int, b: Int) -> Int {
    a + b
}

The type for this function is (Int, Int) -> Int. Which basically means this function takes in two integers as parameters and returns an integer as output.

Functions can be used as any other type in Swift. You can assign functions to variables as well. You can create variables/constants that have function types!

var arithmeticOperation: (Int, Int) -> Int

You can also assign these variables to existing functions like you can for any other type in Swift.

arithmeticOperation = sum

Note that you assign arithmeticOperation to sum and not sum(). The () mean you want to execute that function and the type would become whatever the return type of the function is.

You can call these variables just like you would a function, with the only difference being we cannot specify argument labels —

arithmeticOperation(5, 8) // Output - 13

You can also assign the arithmeticOperation variable to another function that is of the same type.

1
2
3
4
5
6
func product(a: Int, b: Int) -> Int {
    a * b
}
product(a: 4, b: 6) // Output - 24
arithmeticOperation = product
arithmeticOperation(5, 7) // Output - 35

Function as a Parameter

Since Functions in Swift are just like any other type, we can also pass them into other functions as inputs to that function.

1
2
3
4
5
func operation(a: Int, b: Int, operand: (Int, Int) -> Int) {
    print(operand(a, b))
}
operation(a: 5, b: 8, operand: product) // Output - 40
operation(a: 5, b: 8, operand: arithmeticOperation) // Output - 40

Note that we tested both passing the function as an input as well as passing the variable assigned to that function as an input.

Function as a Return Type

Just as for any other type in Swift, functions can also return functions.

1
2
3
4
func choseOperand(from: String) -> (Int, Int) -> Int {
    if from == "Add" { return sum }
    else { return product }
}

That’s it! That was a lot of ground to cover and we did it! It will take some time to get your head around these concepts at the beginning, but its important to practice and be patient at the same time. Feel free to reach out to me in case you have any queries!


Producing and maintaining these guides takes time and resources. If you found this article helpful and would like to support future content, consider a small contribution via Buy Me a Coffee. Contributions help cover hosting and creation costs and make it possible to keep publishing free, practical material. No pressure — sharing this post with your network or starring the project is equally appreciated. Thank you for reading.

This post is licensed under CC BY 4.0 by the author.