Chapters

Hide chapters

Swift Apprentice

Section III: Building Your Own Types

Section 3: 8 chapters
Show chapters Hide chapters

Section IV: Advanced Topics

Section 4: 10 chapters
Show chapters Hide chapters

5. Functions
Written by Matt Galloway

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

Functions are a core part of many programming languages. Simply put, a function lets you define a block of code that performs a task. Then, whenever your app needs to execute that task, you can run the function instead of having to copy and paste the same code everywhere.

In this chapter, you’ll learn how to write your own functions, and see firsthand how Swift makes them easy to use.

Function basics

Imagine you have an app that frequently needs to print your name. You can write a function to do this:

func printMyName() {
  print("My name is Matt Galloway.")
}

The code above is known as a function declaration. You define a function using the func keyword. After that comes the name of the function, followed by parentheses. You’ll learn more about the need for these parentheses in the next section.

After the parentheses comes an opening brace, followed by the code you want to run in the function, followed by a closing brace. With your function defined, you can use it like so:

printMyName()

This prints out the following:

My name is Matt Galloway.

If you suspect that you’ve already used a function in previous chapters, you’re correct! print, which prints the text you give it to the console, is indeed a function. This leads nicely into the next section, in which you’ll learn how to pass data to a function and get data back in return.

Function parameters

In the previous example, the function simply prints out a message. That’s great, but sometimes you want to parameterize your function, which lets the function perform differently depending on the data passed into it via its parameters.

func printMultipleOfFive(value: Int) {
  print("\(value) * 5 = \(value * 5)")
}
printMultipleOfFive(value: 10)
func printMultipleOf(multiplier: Int, andValue: Int) {
  print("\(multiplier) * \(andValue) = \(multiplier * andValue)")
}
printMultipleOf(multiplier: 4, andValue: 2)
func printMultipleOf(multiplier: Int, and value: Int) {
  print("\(multiplier) * \(value) = \(multiplier * value)")
}
printMultipleOf(multiplier: 4, and: 2)

func printMultipleOf(multiplier: Int, and: Int)
func printMultipleOf(_ multiplier: Int, and value: Int) {
  print("\(multiplier) * \(value) = \(multiplier * value)")
}
printMultipleOf(4, and: 2)
func printMultipleOf(_ multiplier: Int, _ value: Int) {
  print("\(multiplier) * \(value) = \(multiplier * value)")
}
printMultipleOf(4, 2)
func printMultipleOf(_ multiplier: Int, _ value: Int = 1) {
  print("\(multiplier) * \(value) = \(multiplier * value)")
}
printMultipleOf(4)

Return values

All of the functions you’ve seen so far have performed a simple task: printing something out. Functions can also return a value. The caller of the function can assign the return value to a variable or constant, or use it directly in an expression.

func multiply(_ number: Int, by multiplier: Int) -> Int {
  return number * multiplier
}
let result = multiply(4, by: 2)
func multiplyAndDivide(_ number: Int, by factor: Int)
                   -> (product: Int, quotient: Int) {
  return (number * factor, number / factor)
}
let results = multiplyAndDivide(4, by: 2)
let product = results.product
let quotient = results.quotient
func multiply(_ number: Int, by multiplier: Int) -> Int {
  number * multiplier
}

func multiplyAndDivide(_ number: Int, by factor: Int)
                   -> (product: Int, quotient: Int) {
  (number * factor, number / factor)
}

Advanced parameter handling

Function parameters are constants by default, which means they can’t be modified.

func incrementAndPrint(_ value: Int) {
  value += 1
  print(value)
}
func incrementAndPrint(_ value: inout Int) {
  value += 1
  print(value)
}
var value = 5
incrementAndPrint(&value)
print(value)

Overloading

Did you notice how you used the same function name for several different functions in the previous examples?

func printMultipleOf(multiplier: Int, andValue: Int)
func printMultipleOf(multiplier: Int, and value: Int)
func printMultipleOf(_ multiplier: Int, and value: Int)
func printMultipleOf(_ multiplier: Int, _ value: Int)
func getValue() -> Int {
  31
}

func getValue() -> String {
  "Matt Galloway"
}
let value = getValue()
error: ambiguous use of 'getValue()'
let valueInt: Int = getValue()
let valueString: String = getValue()

Mini-exercises

  1. Write a function named printFullName that takes two strings called firstName and lastName. The function should print out the full name defined as firstName + " " + lastName. Use it to print out your own full name.
  2. Change the declaration of printFullName to have no external name for either parameter.
  3. Write a function named calculateFullName that returns the full name as a string. Use it to store your own full name in a constant.
  4. Change calculateFullName to return a tuple containing both the full name and the length of the name. You can find a string’s length by using the count property. Use this function to determine the length of your own full name.

Functions as variables

This may come as a surprise, but functions in Swift are simply another data type. You can assign them to variables and constants just as you can any other type of value, such as an Int or a String.

func add(_ a: Int, _ b: Int) -> Int {
  a + b
}
var function = add
function(4, 2)
func subtract(_ a: Int, _ b: Int) -> Int {
  a - b
}
function = subtract
function(4, 2)
func printResult(_ function: (Int, Int) -> Int, _ a: Int, _ b: Int) {
  let result = function(a, b)
  print(result)
}
printResult(add, 4, 2)

The land of no return

Some functions are never, ever, intended to return control to the caller. For an example, think about a function that is designed to crash an application. Perhaps this sounds strange, so let me explain: if an application is about to work with corrupt data, it’s often best to crash rather than continue into an unknown and potentially dangerous state. The function fatalError("reason to terminate") is an example of a function like this. It prints the reason for the fatal error and then halts execution to prevent further damage.

func noReturn() -> Never {

}
func infiniteLoop() -> Never {
  while true {
  }
}

Writing good functions

Functions let you solve many problems. The best do one simple task , making them easier to mix, match, and model into more complex behaviors.

Commenting your functions

All good software developers document their code. :]

/// Calculates the average of three values
/// - Parameters:
///   - a: The first value.
///   - b: The second value.
///   - c: The third value.
/// - Returns: The average of the three values.
func calculateAverage(of a: Double, and b: Double, and c: Double) -> Double {
  let total = a + b + c
  let average = total / 3
  return average
}
calculateAverage(of: 1, and: 3, and: 5)

Challenges

Before moving on, here are some challenges to test your knowledge of functions. It is best if you try to solve them yourself, but solutions are available if you get stuck. These came with the download or are available at the printed book’s source code link listed in the introduction.

Challenge 1: Looping with stride functions

In the last chapter you wrote some for loops with countable ranges. Countable ranges are limited in that they must always be increasing by one. The Swift stride(from:to:by:) and stride(from:through:by:) functions let you loop much more flexibly.

for index in stride(from: 10, to: 22, by: 4) {
  print(index)
}
// prints 10, 14, 18

for index in stride(from: 10, through: 22, by: 4) {
  print(index)
}
// prints 10, 14, 18, and 22

Challenge 2: It’s prime time

When I’m acquainting myself with a programming language, one of the first things I do is write a function to determine whether or not a number is prime. That’s your second challenge.

func isNumberDivisible(_ number: Int, by divisor: Int) -> Bool
func isPrime(_ number: Int) -> Bool
isPrime(6) // false
isPrime(13) // true
isPrime(8893) // true

Challenge 3: Recursive functions

In this challenge, you’re going to see what happens when a function calls itself, a behavior called recursion. This may sound unusual, but it can be quite useful.

func fibonacci(_ number: Int) -> Int
fibonacci(1)  // = 1
fibonacci(2)  // = 1
fibonacci(3)  // = 2
fibonacci(4)  // = 3
fibonacci(5)  // = 5
fibonacci(10) // = 55

Key points

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2023 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now