Basic function declaration
Functions in modern Swift: parameter labels, default values, multiple return values with tuples, variadic parameters and closures.
Functions are the fundamental building blocks of any program. Swift has a very expressive function system with parameter labels, default values and first-class support for functions as types. In this post we cover everything you need to write clear, reusable functions.
Basic declaration
// Function with no parameters or return value
func greet() {
print("Hello from Swift")
}
greet()
// Function with parameters and return value
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}
let result = add(3, 5) // 8
// In single-expression functions, you can omit 'return'
func multiply(_ a: Int, _ b: Int) -> Int {
a * b
}
print(multiply(4, 6)) // 24
Parameter labels — Swift’s big differentiator
Swift distinguishes between the external label (used by whoever calls the function) and the internal name (used by the function body). This makes call sites read almost like sentences.
// Syntax: func name(externalLabel internalName: Type)
func move(from origin: String, to destination: String) {
print("Moving from \(origin) to \(destination)")
}
move(from: "Madrid", to: "Lima") // reads like a sentence
// Use _ to omit the label at the call site
func raise(_ base: Double, to exponent: Int) -> Double {
var result = 1.0
for _ in 1...exponent { result *= base }
return result
}
print(raise(2, to: 10)) // 1024.0 — reads: "raise 2 to 10"
Default values
Parameters can have default values, making them optional at the call site. Always put them at the end of the parameter list.
func connect(to host: String, port: Int = 8080, secure: Bool = true) {
print("Connecting to \(host):\(port) — secure: \(secure)")
}
connect(to: "example.com") // port 8080, secure true
connect(to: "example.com", port: 3000) // secure true by default
connect(to: "example.com", port: 3000, secure: false) // all explicit
Multiple return values with tuples
Unlike other languages, Swift allows returning multiple values from a function using tuples. It’s cleaner than using inout parameters or creating a struct just to return two values.
func statistics(of numbers: [Int]) -> (minimum: Int, maximum: Int, average: Double) {
let minimum = numbers.min() ?? 0
let maximum = numbers.max() ?? 0
let average = Double(numbers.reduce(0, +)) / Double(numbers.count)
return (minimum, maximum, average)
}
let data = [4, 8, 15, 16, 23, 42]
let stats = statistics(of: data)
print("Minimum: \(stats.minimum)") // 4
print("Maximum: \(stats.maximum)") // 42
print("Average: \(stats.average)") // 18.0
// Destructure directly
let (min, max, avg) = statistics(of: data)
print(min, max, avg)
Variadic parameters
Allow passing any number of values of the same type. Inside the function they behave like an array.
func addAll(_ numbers: Int...) -> Int {
numbers.reduce(0, +)
}
print(addAll(1, 2, 3)) // 6
print(addAll(10, 20, 30, 40)) // 100
// Swift's print() is variadic — that's why you can pass multiple values
print("a", "b", "c") // "a b c"
Functions as types — first-class functions
In Swift functions are first-class types: you can store them in variables, pass them as parameters and return them from other functions. This is the foundation of closures and how APIs like map, filter and sort work.
func double(_ n: Int) -> Int { n * 2 }
func triple(_ n: Int) -> Int { n * 3 }
// Store a function in a variable
var operation: (Int) -> Int = double
print(operation(5)) // 10
operation = triple
print(operation(5)) // 15
// Pass a function as a parameter
func apply(_ operation: (Int) -> Int, to number: Int) -> Int {
operation(number)
}
print(apply(double, to: 7)) // 14
print(apply(triple, to: 7)) // 21
Closures — anonymous functions
Closures are functions without a name. They’re ubiquitous in modern Swift, especially when working with collections and async code.
let numbers = [3, 1, 4, 1, 5, 9, 2, 6]
// Full form
let sorted = numbers.sorted(by: { (a: Int, b: Int) -> Bool in
return a < b
})
// Abbreviated form with $0, $1 (shorthand arguments)
let sortedShort = numbers.sorted { $0 < $1 }
print(sortedShort) // [1, 1, 2, 3, 4, 5, 6, 9]
// map — transforms each element
let squares = numbers.map { $0 * $0 }
print(squares) // [9, 1, 16, 1, 25, 81, 4, 36]
// filter — filters elements
let greaterThanFive = numbers.filter { $0 > 5 }
print(greaterThanFive) // [9, 6]
// reduce — accumulates into a single value
let total = numbers.reduce(0) { $0 + $1 }
print(total) // 31
Summary
- External labels: make call sites read like sentences; use _ to omit them.
- Default values: make parameters optional; put them at the end.
- Multiple return with tuples: cleaner than using multiple output parameters.
- Variadic: accepts any number of arguments of the same type.
- Functions as types: store them, pass them, return them.
- Closures: anonymous functions; the foundation of map, filter, sort and async code.
In the last post of this series we’ll look at Classes and Structures — the fundamental difference between value types and reference types, and why in modern Swift you’ll almost always prefer struct.