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.