Defining and Calling Functions swift
  • July 23, 2025

Defining and Calling Functions

A function bundles work behind a name. Syntax:

func functionName(argumentLabel parameterName: Type) -> ReturnType {
    // body
}
  • Scope: You can define at global scope, inside types (methods), or inside other functions (nested).
  • Overloading: Multiple functions can share a name if their parameter types/labels or return type differ.
  • Calling: Use the argument labels the function declares. The compiler enforces correct labels, types, and count.

Function Parameters and Return Values

Parameters are read-only constants inside the body. Return values are explicit after ->.

  • Single return: most common.
  • Void / (): means “nothing meaningful returned.”
  • Optionals: often used instead of throwing or returning sentinel values.
func parse(_ text: String) -> Int? { Int(text) }

Functions Without Parameters

Use empty parentheses:

func resetGame() {
    score = 0
}
resetGame()

Even with no inputs, returning a value is fine:

func currentTimestamp() -> TimeInterval { Date().timeIntervalSince1970 }

Functions With Multiple Parameters

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).


Functions Without Return Values

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.


Functions with Multiple Return Values

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:

  • Named elements increase readability.
  • No need to define a struct for quick utilities.

Cons:

  • Tuples are anonymous types; don’t use them for widely shared APIs—structs/enums scale better.

Functions With an Implicit Return

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.


Function Argument Labels and Parameter Names

Each parameter can have:

  • External (argument) label: seen by callers.
  • Internal (parameter) name: used inside the function.

Syntax: func f(external internal: Type)

Defaults:

  • Free functions (global): the first parameter omits an external label by default; the rest use the internal name as label.
  • Methods: all parameters (including the first) use the internal name as the external label by default.

This is why you often see “swifty” APIs read like sentences.


Specifying Argument Labels

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)


Omitting Argument Labels

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.


Default Parameter Values

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.


Variadic Parameters

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:

  • At most one variadic parameter, and it must be last.
  • Inside, it’s a [Type].
  • Mixing with inout or default values on the same parameter is not allowed.

In-Out Parameters

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
  • Callers must pass a mutable variable preceded by &.
  • Not allowed: literals, constants, or stored properties of a let struct variable.
  • Be mindful: inout can obscure data flow; prefer returning a new value when possible.

Function Types

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:

  • Argument labels are not part of the function type; only types are.
  • throws, async are part of the type—so you must match them when assigning or passing.

Using Function Types

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.


Function Types as Parameter Types

Higher-order functions take functions:

func transform(_ value: Int, with f: (Int) -> Int) -> Int {
    f(value)
}

let doubled = transform(8) { $0 * 2 }

Benefits:

  • Decouple behavior from structure.
  • Replace switch/if chains with composable behaviors.

Function Types as Return Types

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:)).


Nested Functions

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
}
  • Nested functions can access outer variables (they “capture” them).
  • Useful to break a complex function without polluting global scope.

Extras Worth Knowing

  • @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.
  • Attributes (@available, @inline, etc.) add metadata and constraints.
  • Generic functions: Parameterize over types:
    func swapValues<T>(_ a: inout T, _ b: inout T) { let t = a; a = b; b = t }
    
  • Escaping closures: Mark parameter closures @escaping if stored or executed after the function returns (common in async APIs).

Mini Cheat Sheet

func name(label param: Type = default, _ unlabeled: Type..., inoutParam: inout Type) throws -> ReturnType {
    // body
    return value  // or omit if single expression
}
  • Labels craft nice call sites.
  • Defaults reduce boilerplate.
  • Variadics pack many args.
  • In-out mutates caller vars (use sparingly).
  • Tuples for quick multi-returns.
  • Function types enable functional patterns.
  • Nested functions keep helpers local.
YOU MIGHT ALSO LIKE...
🧭 NavigationKit

NavigationKit is a lightweight library which makes SwiftUI navigation super easy to use. 💻 Installation 📦 Swift Package Manager Using Swift Package Manager, add ...

swiftui-navigation-stack

An alternative SwiftUI NavigationView implementing classic stack-based navigation giving also some more control on animations and programmatic navigation. NavigationStack Installation ...

Stinsen

Simple, powerful and elegant implementation of the Coordinator pattern in SwiftUI. Stinsen is written using 100% SwiftUI which makes it ...

SwiftUI Router

With SwiftUI Router you can power your SwiftUI app with path-based routing. By utilizing a path-based system, navigation in your app becomes ...

FlowStacks

This package takes SwiftUI's familiar and powerful NavigationStack API and gives it superpowers, allowing you to use the same API not just ...