The Swift Blog: Functions in Swift

Let’s dive into functions in Swift. What are they, what can you do with them, and when does it make sense to use something else?

What is a function?

Per Apple’s definition functions are “self contained chunks of code that perform a specific task”. But what does that exactly mean? Let’s take a look at a simple example:

func doSomething() {
	//do something here
}

This is the simplest function you can write. It doesn’t take any parameters, returns nothing and performs whatever you include in the curly braces, also known as the function’s body. To use this function, e.g. to execute its task, you need to “call” that function. Calling a function is done by typing out its name including the parentheses doSomething(). While this function doesn’t do anything interesting, we will cover more useful ones a little later.

Function declarations

You start a function declaration with the func keyword, followed by the function name, followed by parentheses with optional parameters - also known as arguments - and finally the function body which is indicated by curly braces. This is the most basic version of a function. You may want to have the function return a value, which will be covered next.

Return values

Remember when I said that basic functions don’t return a value? Strictly speaking this isn’t true. A function that doesn’t explicitly return a value still returns Void, which is a special value that can also be represented by an empty tuple (). Just to recap, if you define a function like this: func doSomething() {}, the under the hood declaration looks like this: func doSomething() -> () {}. Why is this important? Because you can pass functions as parameters into other functions, which we will cover a little later. But why do we need to return anything at all? Let’s take a look at a simple example: Imagine a scenario in which you need to calculate something, in our example a simple addition of Ints. Let’s write a small function for this:

func add(_ int1: Int, to int2: Int) {
	let sum = int1 + int2
}

As said before, this function doesn’t return anything so the constant sum is “trapped” inside the function body. There’s no way for you to use sum outside of the function’s scope. To make the function return sum and give you the ability to use the value anywhere else in your project you need to return the value. This is done by adding -> Int at the end of the function declaration:

func add(_ int1: Int, to int2: Int) -> Int {
	let sum = int1 + int2
}

This is the first step into returning values. The function can now return a value of type Int, but it doesn’t do so yet. To make the function return the value we need to change its body like so:

func add(_ int1: Int, to int2: Int) -> Int {
	let sum = int1 + int2
	return sum

	/*
	Alternatively you can return the addition directly like so:
	return int1 + int2
	*/
}

With this final step you instruct the function to return the value, making it accessible outside of the function’s scope. What can you do with it now? let sum = add(1, to: 1) With this single line of code you call the function, pass 1 into the function arguments and assign the returned value to the constant sum. This wouldn’t have been possible without returning a value.

Functions as parameters for other functions

In the last section you’ve learned that functions always return a value, and that you can explicitly tell the function what kind of value it should return. This is especially useful if you want to use functions as parameters for other functions. For this let’s create another function:

func additionOperation(_ addition: (Int,Int) -> Int, _ int1: Int, _ int2: Int) -> Int {
    return addition(int1, int2)
}

Looks crazy, doesn’t it? Let’s break it down to understand what’s going on here. As always the function starts with the func keyword and the name. The parameters contain a function which takes two Int values and returns an Int: (Int,Int) -> Int. The two Int parameters at the end are the arguments you need to pass into the function mentioned before. If we use the add function from before and assign it as a parameter to additionOperation it would look like this: additionOperation(add, 1, 2). Because this function returns yet another value we can of course assign it to a variable or constant: let sum = additionOperation(add, 1, 2). Still doesn’t look useful? And rightfully so. This is just a very simple example and if you like you can improve this all. Instead of having an additionOperation function why don’t you try to create a mathOperation function that will take as parameters the math operation and two numeric values (Int, Double and Float are all numeric values). This function then calculates the outcome of all the math operations and returns the correct type. Hint: Dividing by 0 will result in an error. ;-)

Methods (Instance and Type)

Methods are functions that belong to a type, for example a class. Instance methods are functions that require you to create an instance of the type before you can call them.

struct User {
	let name: String

	func printName() {
		print(name)
	}
}

As said before, to call the function you need to create an instance of the User struct initializing it properly and then call that method on the instance of the struct.

let user = User(name: "Dennis")
user.printName()

Without initializing the User properly the instance wouldn’t be able to access the method. This is a good thing by the way. Because the method tries to access the name property of the user, if you wouldn’t initialize it properly the property wouldn’t be set, which will end up in a crash.

The other type of methods I want to cover here are type methods. Those type of methods belong to the type itself. Because of this you don’t need to create an instance of the type but call the method directly on it.

struct GameManager {
	private static let questions = [question1, question2, ...]

	static func provideRandomQuestion() -> Question {
		//randomize a question and then return it for easy access at call site
	}
}

In this example we create a GameManager struct which only has one job, and that is to provide a randomly generated question whenever the caller (for example a view controller) asks for it. Instead of creating an instance of the struct, then calling a method on that instance, we can simplify our method here and just call Game.provideRandomQuestion() whenever we need a new randomly generated question. A little note on the side for all of you who wonder why the questions array is marked as private. This keyword ensures that only our GameManager has access to the array, and we can’t accidentally try to fetch the array of questions outside of it, again for example in a view controller.

When shouldn’t we use functions?

As I mentioned at the beginning I also want to talk about when functions don’t make sense. Although functions can do a great amount of work, and we could use them to do almost anything, here some examples about when not to use functions:

Conclusion

Functions are one of the building blocks in every programming language. They enable us to execute code and pass information around in our program. But you also need to take care that you create a function when it makes sense. As described above functions can simplify our code or they can make it harder to reason about. I hope that I could give you a good overview about the general purpose of functions as well as when to use something else. As always, if you have any questions about functions (or other iOS/Swift related topics) please don’t hesitate to reach out to me via Twitter