Functions with argument labels and defaults, and closures — self-contained blocks of code you pass around. Learn trailing closure syntax and higher-order functions like map and filter.
Swift functions have a distinctive feature: argument labels that read like a sentence at the call site, separate from the parameter names used inside. Combined with default values, one function definition covers many call shapes and stays self-documenting. An underscore label omits it entirely when a label would be noise.
// external label 'to' reads at the call site; 'name' is used inside
func greet(_ greeting: String, to name: String, excited: Bool = false) -> String {
return "\(greeting), \(name)\(excited ? "!" : ".")"
}
print(greet("Hello", to: "Ada")) // Hello, Ada.
print(greet("Hi", to: "Ada", excited: true)) // Hi, Ada!
// '_' drops the first label; 'to:' labels the second — reads like prose.A closure is a block of functionality you can store in a variable, pass as an argument, and return — like a lambda. Closures capture the variables from their surrounding context, which is powerful and, as the memory lesson covers, a source of retain cycles to watch for. Swift infers closure types from context, so they stay compact.
// a closure stored in a constant; type is (Int, Int) -> Int
let add = { (a: Int, b: Int) in a + b }
print(add(2, 3)) // 5
// a function taking a closure parameter
func apply(_ x: Int, _ op: (Int) -> Int) -> Int { op(x) }
print(apply(5) { $0 * 2 }) // 10 ($0 is the first argument)
// closures CAPTURE surrounding variables:
var total = 0
let addToTotal = { (n: Int) in total += n }
addToTotal(5); addToTotal(3)
print(total) // 8When a closure is a function’s last argument, Swift lets you write it after the parentheses as a trailing closure — the syntax behind most of SwiftUI and the collection APIs. With a single argument you can omit the parentheses entirely. Recognizing trailing-closure syntax is essential because iOS APIs use it pervasively.
// these are equivalent — the trailing form is idiomatic
let doubled1 = [1, 2, 3].map({ n in n * 2 })
let doubled2 = [1, 2, 3].map { n in n * 2 } // trailing closure
let doubled3 = [1, 2, 3].map { $0 * 2 } // shorthand argument
// completion handlers read naturally with trailing closures:
func load(completion: (String) -> Void) { completion("done") }
load { result in
print(result) // done
}
// SwiftUI (Button { ... }) and most iOS APIs rely on this syntax.Swift collections come with a functional toolkit — map, filter, reduce, sorted, compactMap, and more — that transform data declaratively instead of with manual loops. Chaining them reads clearly and avoids off-by-one bugs. This style is idiomatic Swift and appears constantly when shaping data for a UI.
let nums = [1, 2, 3, 4, 5, 6]
let evens = nums.filter { $0 % 2 == 0 } // [2, 4, 6]
let squares = nums.map { $0 * $0 } // [1, 4, 9, 16, 25, 36]
let sum = nums.reduce(0) { $0 + $1 } // 21
let sorted = nums.sorted { $0 > $1 } // descending
// chain them into a readable pipeline:
let result = nums.filter { $0 % 2 == 0 }.map { $0 * 10 }
print(result) // [20, 40, 60]
// compactMap drops nils while mapping:
let parsed = ["1", "x", "3"].compactMap { Int($0) } // [1, 3]