Functions: Reusable Blocks of Code

Swift Chapter 7 of the Ultimate Swift Series 30 min read April 10, 2026 Beginner

In This Article

  1. Function Basics
  2. Parameters and Argument Labels
  3. Default Parameter Values
  4. Return Values
  5. Returning Multiple Values With Tuples
  6. Modifying Parameters With inout
  7. Function Overloading
  8. Functions as Values
  9. Functions That Never Return
  10. Documenting Your Functions
  11. Exercises
  12. Key Points

Every program you've written so far has been a straight sequence of statements. That works for learning, but real apps have tasks that happen over and over — fetching data, formatting dates, validating input. Functions let you wrap a task into a named, reusable block. Define it once, call it anywhere, as many times as you need.

Function Basics

Here's the simplest possible function — it takes no input and returns nothing:

func sayHello() { print("Hello, Swift!") } sayHello() // "Hello, Swift!" sayHello() // "Hello, Swift!" — call it as many times as you want

The anatomy: func keyword, then the function name, then parentheses (required even when empty), then the body in braces. To run the function, write its name followed by parentheses — this is called calling or invoking the function.

Parameters and Argument Labels

Functions become powerful when they accept input. You declare parameters inside the parentheses:

func greet(name: String) { print("Hello, \(name)!") } greet(name: "Alex") // "Hello, Alex!"

When calling the function, you must include the parameter label (name:). Swift is designed so that function calls read like sentences.

External vs. internal names

Swift lets you give a parameter two names: an external name (used when calling) and an internal name (used inside the function):

func multiply(_ number: Int, by multiplier: Int) -> Int { number * multiplier } let result = multiply(4, by: 3) // 12

Breaking this down:

The call reads naturally: "multiply 4 by 3". This is a core Swift design principle — your code should be readable at the call site.

Parameters vs. arguments

A parameter is the variable declared in the function definition. An argument is the actual value you pass when calling the function. The function declares parameters; the caller provides arguments.

Default Parameter Values

You can give parameters default values. If the caller doesn't provide a value, the default is used:

func greet(_ name: String, excited: Bool = false) { if excited { print("HELLO, \(name.uppercased())!!!") } else { print("Hello, \(name).") } } greet("Alex") // "Hello, Alex." greet("Alex", excited: true) // "HELLO, ALEX!!!"

Defaults simplify common calls while keeping flexibility for special cases.

Return Values

Functions can send data back to the caller using -> followed by the return type:

func add(_ a: Int, _ b: Int) -> Int { a + b } let sum = add(10, 20) // 30

For single-expression functions, you can omit the return keyword — Swift returns the expression's value automatically. For multi-line functions, you need an explicit return:

func calculateAverage(of a: Double, and b: Double) -> Double { let total = a + b let average = total / 2 return average }

Returning Multiple Values With Tuples

One of Swift's best features: you can return multiple values from a single function using a tuple:

func divideWithRemainder(_ dividend: Int, by divisor: Int) -> (quotient: Int, remainder: Int) { (dividend / divisor, dividend % divisor) } let result = divideWithRemainder(17, by: 5) print(result.quotient) // 3 print(result.remainder) // 2

Named tuple members make the return value self-documenting. No need to remember which index is which.

Modifying Parameters With inout

By default, function parameters are constants — you can't modify them. If you need a function to change a value directly, use inout:

func doubleInPlace(_ value: inout Int) { value *= 2 } var myNumber = 10 doubleInPlace(&myNumber) print(myNumber) // 20

The & before the argument signals that this value will be modified. This explicitness is intentional — it makes it obvious at the call site that the function has side effects.

Use inout sparingly

Functions that modify their parameters are harder to reason about. Prefer returning a new value instead. Reserve inout for cases where modifying in place is genuinely clearer or more performant.

Function Overloading

Overloading means defining multiple functions with the same name but different parameter lists:

func area(of square: Double) -> Double { square * square } func area(of width: Double, and height: Double) -> Double { width * height } area(of: 5.0) // 25.0 (square) area(of: 5.0, and: 3.0) // 15.0 (rectangle)

Swift distinguishes overloaded functions by their parameter count, types, or external names. The right version is chosen automatically based on how you call it.

You can also overload by return type, but this requires explicit type annotations at the call site and is generally not recommended since it loses the benefit of type inference.

Functions as Values

In Swift, functions are just another type. You can assign them to variables, pass them as arguments, and return them from other functions:

func add(_ a: Int, _ b: Int) -> Int { a + b } func subtract(_ a: Int, _ b: Int) -> Int { a - b } var operation = add // Type: (Int, Int) -> Int print(operation(10, 3)) // 13 operation = subtract print(operation(10, 3)) // 7

The type (Int, Int) -> Int describes a function that takes two integers and returns an integer. Any function matching this signature can be assigned to the variable.

Passing functions to other functions

func printResult(_ operation: (Int, Int) -> Int, _ a: Int, _ b: Int) { print(operation(a, b)) } printResult(add, 10, 3) // 13 printResult(subtract, 10, 3) // 7

This is incredibly powerful. Instead of hardcoding what operation to perform, you pass it in as a parameter. This pattern — passing behavior as data — is the foundation of functional programming and shows up everywhere in Swift (closures, higher-order functions on arrays, SwiftUI).

Functions That Never Return

Some functions are designed to never return control to the caller. Swift has a special return type for this: Never.

func crashAndBurn() -> Never { fatalError("Something went terribly wrong") }

The built-in fatalError() is the most common example. It prints a message and immediately terminates the program. You'd use this when continuing execution would be dangerous — like processing corrupted data.

The Never return type tells the compiler that code after the call is unreachable, allowing it to generate optimized code and warn you about dead code.

Documenting Your Functions

Good developers document their functions. Swift uses triple-slash (///) comments that integrate with Xcode's code completion:

/// Calculates the average of three values. /// - Parameters: /// - a: The first value. /// - b: The second value. /// - c: The third value. /// - Returns: The average of the three values. func average(of a: Double, and b: Double, and c: Double) -> Double { (a + b + c) / 3 }

In Xcode, you can auto-generate this template by placing your cursor on the function and pressing Command-Option-/. The documentation then appears in autocomplete suggestions and Option-click popovers.

Exercises

Try These in Your Playground

  1. Write a function printFullName(firstName:lastName:) that prints a full name. Then change it to use _ for both parameter labels so the call reads printFullName("Alex", "Kim").
  2. Write a function calculateFullName that returns the full name as a String. Then modify it to return a tuple with both the name and its character count.
  3. Write isNumberDivisible(_ number: Int, by divisor: Int) -> Bool, then use it to write isPrime(_ number: Int) -> Bool. Test with 6 (false), 13 (true), and 8893 (true).
  4. Write a recursive fibonacci(_ n: Int) -> Int function. Verify: fibonacci(1)=1, fibonacci(5)=5, fibonacci(10)=55.
  5. Write two functions add and multiply with the same signature (Int, Int) -> Int. Assign them to a variable and call it with different operations.
  6. Challenge: Use stride(from:through:by:) to print a countdown from 10.0 to 0.0, decrementing by 0.5.

Key Points

What You Learned

In the next chapter, we'll explore Optionals — Swift's system for safely handling values that might not exist. This is one of Swift's most important features and the key to writing crash-free code.

Watch the video lessons

Our Swift Fundamentals course covers functions, closures, and advanced patterns in 96 video lessons.

Watch Swift Videos