- July 26, 2025
- Mins Read
Defining and Calling Functions
A function bundles work behind a name. Syntax:
func functionName(argumentLabel parameterName: Type) -> ReturnType {
// body
}
Parameters are read-only constants inside the body. Return values are explicit after ->
.
Void
/ ()
: means “nothing meaningful returned.”func parse(_ text: String) -> Int? { Int(text) }
Use empty parentheses:
func resetGame() {
score = 0
}
resetGame()
Even with no inputs, returning a value is fine:
func currentTimestamp() -> TimeInterval { Date().timeIntervalSince1970 }
Comma-separate them. They’re evaluated left to right, and all must be supplied (unless defaults exist).
func drawLine(from start: CGPoint, to end: CGPoint, width: CGFloat) { /*...*/ }
Parameters are immutable—if you want to modify an argument for the caller, consider inout
(see below).
If there’s no ->
, the return type is Void
(an empty tuple ()
).
func log(_ msg: String) { print(msg) } // implicit Void
Use this for side-effect functions. If you later need a result, you can change the signature—call sites may need updates.
Swift favors tuples for lightweight multi-returns:
func stats(of numbers: [Int]) -> (min: Int, max: Int, avg: Double)? {
guard let first = numbers.first else { return nil }
var minV = first, maxV = first, total = 0
for n in numbers { minV = min(minV, n); maxV = max(maxV, n); total += n }
return (minV, maxV, Double(total)/Double(numbers.count))
}
Pros:
Cons:
If the body is a single expression, you can omit return
:
func hypotenuse(a: Double, b: Double) -> Double { sqrt(a*a + b*b) }
This also applies to computed properties and closure literals. For more complex bodies, explicit return
keeps clarity.
Each parameter can have:
Syntax: func f(external internal: Type)
Defaults:
This is why you often see “swifty” APIs read like sentences.
You craft labels to communicate intent:
func insert(_ item: Item, at index: Int) { /*...*/ }
func request(resource url: URL, timeout seconds: TimeInterval) { /*...*/ }
The second example reads well when called:
request(resource: someURL, timeout: 5)
Use _
for the external label to remove it:
func pow(_ base: Int, _ exp: Int) -> Int { /*...*/ }
pow(2, 8)
Great for math-like, positional parameters where labels add noise. Avoid overusing _
: readability matters.
Defaults reduce call-site verbosity:
func toast(_ message: String, duration: TimeInterval = 2.0, position: Position = .bottom) { /*...*/ }
toast("Saved!") // uses both defaults
toast("Saved!", duration: 4.0) // override one
Placement tip: put defaults at the end so callers can omit trailing ones conveniently.
Collect a variable number of args into an array:
func average(_ values: Double...) -> Double {
guard !values.isEmpty else { return .nan }
return values.reduce(0, +) / Double(values.count)
}
average(1, 3, 5, 7)
Rules & gotchas:
[Type]
.inout
or default values on the same parameter is not allowed.inout
lets a function modify the caller’s variable (copy-in, copy-out semantics):
func clamp(_ value: inout Int, to range: ClosedRange<Int>) {
value = min(max(value, range.lowerBound), range.upperBound)
}
var speed = 120
clamp(&speed, to: 0...100) // speed becomes 100
&
.let
struct variable.inout
can obscure data flow; prefer returning a new value when possible.Every function has a first-class type:
(Int, Int) -> Int // takes two Ints, returns Int
() -> Void // takes nothing, returns nothing
(String) throws -> Int // can throw
(Double) async -> Int // async function
Note:
throws
, async
are part of the type—so you must match them when assigning or passing.You can store, pass, and compare functions like any other value:
func add(_ x: Int, _ y: Int) -> Int { x + y }
let op: (Int, Int) -> Int = add
op(2, 3) // 5
Closures ({ ... }
) are just inline values of a function type.
Higher-order functions take functions:
func transform(_ value: Int, with f: (Int) -> Int) -> Int {
f(value)
}
let doubled = transform(8) { $0 * 2 }
Benefits:
Return functions to let callers configure behavior:
func makeFilter(min: Int) -> (Int) -> Bool {
{ $0 >= min }
}
let atLeastTen = makeFilter(min: 10)
atLeastTen(12) // true
Closures capture context (min
) lexically. This is the foundation of many Swift APIs (e.g., map
, filter
, sorted(by:)
).
Define helpers inside other functions to keep them private and close to use:
func chooseDirection(backward: Bool) -> (Int) -> Int {
func stepForward(_ x: Int) -> Int { x + 1 }
func stepBackward(_ x: Int) -> Int { x - 1 }
return backward ? stepBackward : stepForward
}
@discardableResult
: silence “result unused” warnings for functions where ignoring the result is fine.rethrows
: a function only throws if the closure parameter throws.
func map<T, U>(_ values: [T], _ transform: (T) throws -> U) rethrows -> [U] { /*...*/ }
async
/await
: make functions asynchronous. Their type and call sites must use await
and possibly try
.defer
: run code on function exit regardless of return path—great for cleanup.@available
, @inline
, etc.) add metadata and constraints.func swapValues<T>(_ a: inout T, _ b: inout T) { let t = a; a = b; b = t }
@escaping
if stored or executed after the function returns (common in async APIs).func name(label param: Type = default, _ unlabeled: Type..., inoutParam: inout Type) throws -> ReturnType {
// body
return value // or omit if single expression
}
NavigationKit is a lightweight library which makes SwiftUI navigation super easy to use. 💻 Installation 📦 Swift Package Manager Using Swift Package Manager, add ...
An alternative SwiftUI NavigationView implementing classic stack-based navigation giving also some more control on animations and programmatic navigation. NavigationStack Installation ...
With SwiftUI Router you can power your SwiftUI app with path-based routing. By utilizing a path-based system, navigation in your app becomes ...
This package takes SwiftUI's familiar and powerful NavigationStack API and gives it superpowers, allowing you to use the same API not just ...