Functions

Functions are self-contained chunks of code that perform a specific task. You give a function a name that describes what it does, and you call it by that name to run it. In this chapter you'll learn every part of Swift's function syntax — from the simplest no-argument function to functions that return other functions.

Defining and calling functions

You define a function with the func keyword, followed by the name, a list of parameters in parentheses, and an optional return type introduced by ->. You call it by writing its name and passing arguments.

func greet(person: String) -> String {
    let greeting = "Hello, " + person + "!"
    return greeting
}

print(greet(person: "Anna"))   // → Hello, Anna!
print(greet(person: "Brian"))  // → Hello, Brian!

A function with one parameter and a string return value.

A function without parameters still needs parentheses, and a function that doesn't return anything can omit the -> entirely (its return type is implicitly Void, an empty tuple ()).

func sayHello() {
    print("Hello, world!")
}

sayHello()   // → Hello, world!

Parameters and return values

Functions can take any number of parameters, including none, and can return any type or nothing at all. The return value flows out of the function when a return statement runs.

func minMax(array: [Int]) -> Int {
    var currentMin = array[0]
    for value in array[1...] where value < currentMin {
        currentMin = value
    }
    return currentMin
}

print(minMax(array: [8, -6, 2, 109, 3]))  // → -6

Multiple return values with tuples

A function can return a tuple to hand back several values at once as a single compound value. Naming the tuple's elements lets the caller access them by name.

func minMax(array: [Int]) -> (min: Int, max: Int) {
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1...] {
        if value < currentMin { currentMin = value }
        else if value > currentMax { currentMax = value }
    }
    return (currentMin, currentMax)
}

let bounds = minMax(array: [8, -6, 2, 109, 3])
print("min is \(bounds.min), max is \(bounds.max)")
// → min is -6, max is 109

Optional tuple return types

If the whole tuple might have "no value" — for example when the input array is empty — make the return type an optional tuple by writing ? after the closing parenthesis: (min: Int, max: Int)?. That's different from a tuple containing optional values.

func minMax(array: [Int]) -> (min: Int, max: Int)? {
    if array.isEmpty { return nil }
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1...] {
        if value < currentMin { currentMin = value }
        else if value > currentMax { currentMax = value }
    }
    return (currentMin, currentMax)
}

if let bounds = minMax(array: []) {
    print("min is \(bounds.min)")
} else {
    print("empty array")   // → empty array
}

Argument labels and parameter names

Each parameter has both an argument label, used when calling the function, and a parameter name, used inside the function body. By default they're the same, but you can write a distinct label before the name to make call sites read like English.

func greet(_ person: String, from hometown: String) -> String {
    return "Hello \(person)! Glad you could visit from \(hometown)."
}

print(greet("Bill", from: "Cupertino"))
// → Hello Bill! Glad you could visit from Cupertino.

Here _ omits the label for the first argument, and from is a label that differs from the internal name hometown. If you don't want any label, write _ before the parameter name.

Default parameter values

You can give a parameter a default value by assigning to it in the definition. Callers can then omit that argument. Put parameters with defaults at the end so calls without them stay unambiguous.

func makeCoffee(_ kind: String = "espresso", shots: Int = 1) -> String {
    return "\(shots)× \(kind)"
}

print(makeCoffee())                  // → 1× espresso
print(makeCoffee("latte", shots: 2))  // → 2× latte

Variadic parameters

A variadic parameter accepts zero or more values of a type. Write the parameter type followed by .... Inside the function the values arrive as an array. Swift allows multiple variadic parameters as long as the arguments stay unambiguous.

func mean(_ numbers: Double...) -> Double {
    if numbers.isEmpty { return 0 }
    var total = 0.0
    for number in numbers { total += number }
    return total / Double(numbers.count)
}

print(mean(1, 2, 3, 4, 5))  // → 3.0
print(mean(3, 8.25, 18.75))  // → 10.0

In-out parameters

Function parameters are constants by default — you can't change them inside the function. An in-out parameter lets a function modify a value and have the change persist after the call returns. Mark the parameter with inout and pass the argument with an ampersand (&).

func swapValues(_ a: inout Int, _ b: inout Int) {
    let temp = a
    a = b
    b = temp
}

var x = 3, y = 107
swapValues(&x, &y)
print("x is \(x), y is \(y)")  // → x is 107, y is 3

Watch out: in-out parameters can't have default values, can't be variadic, and the argument you pass must be a variable (var) — not a constant or a literal.

Function types

Every function has a function type made of its parameter types and return type, written like (Int, Int) -> Int. You can use function types as ordinary types: store a function in a variable, pass one as a parameter, or return one from another function.

func add(_ a: Int, _ b: Int) -> Int { a + b }
func multiply(_ a: Int, _ b: Int) -> Int { a * b }

var operation: (Int, Int) -> Int = add
print(operation(2, 3))   // → 5
operation = multiply
print(operation(2, 3))   // → 6

Function types as parameters and return types

Passing functions around is the foundation of higher-order programming. A function can take another function as input, and it can return a function.

// A function type as a parameter
func printResult(_ op: (Int, Int) -> Int, _ a: Int, _ b: Int) {
    print("Result: \(op(a, b))")
}
printResult(add, 5, 7)  // → Result: 12

// A function type as a return value
func chooser(reverse: Bool) -> (Int) -> Int {
    func stepForward(_ n: Int) -> Int { n + 1 }
    func stepBackward(_ n: Int) -> Int { n - 1 }
    return reverse ? stepBackward : stepForward
}

let move = chooser(reverse: true)
print(move(10))  // → 9

Nested functions

Functions defined inside other functions are nested functions. They're hidden from the outside world but can be called by the enclosing function, and they can capture values from it. The chooser example above already used nested functions — the enclosing function returned one of them, which then lives on past the call.

func makeCounter() -> () -> Int {
    var count = 0
    func increment() -> Int {
        count += 1          // captures `count` from the enclosing scope
        return count
    }
    return increment
}

let next = makeCounter()
print(next(), next(), next())  // → 1 2 3

Tip: nested functions are really named closures. The capturing behavior you just saw is exactly what you'll explore next in the Closures chapter.

Recap

You now know how to define functions, label and default their parameters, accept a variable number of arguments, mutate values with inout, return multiple results via tuples, and treat functions themselves as first-class values. These tools underpin almost everything else in Swift.