Swift Parameters and Return Values Explained with Examples
Swift functions become much more useful when they can accept input and send results back. This article explains how parameters and return values work in Swift, how argument labels affect function calls, when to use default values or inout, and how to avoid the most common mistakes beginners make.
Quick answer: In Swift, parameters let a function receive values from the caller, and return values let the function send a result back. You define parameter names and types inside the function declaration, then optionally declare a return type after ->.
Difficulty: Beginner
Helpful to know first: basic Swift syntax, variables and constants, and simple types like String, Int, and Bool.
1. What Is Parameters & Return Values?
A Swift function can take input values and produce an output value. The input values are called parameters, and the output value is called the return value.
- A parameter gives a function data it needs to do its job.
- A return value gives the caller the result of that work.
- Parameters have types, just like variables and constants.
- A function may have no parameters, one parameter, or many parameters.
- A function may return nothing, a single value, or a tuple containing multiple values.
For example, a function that adds two numbers needs two parameters. A function that checks whether a number is even might return a Bool. A function that prints a greeting may not return anything at all.
In Swift, this topic is often confused with argument labels and parameter names. They are closely related but not identical. A parameter name is used inside the function body, while an argument label is used when calling the function. You will see that distinction throughout this article.
2. Why Parameters & Return Values Matters
Without parameters, functions would be limited to hard-coded values. Without return values, functions could do work but would have no clean way to hand back a result.
Parameters and return values matter because they help you write code that is:
- Reusable: one function can work with many different inputs.
- Readable: argument labels can make calls read like English.
- Testable: functions that return values are easier to verify.
- Modular: small functions can handle one task and return the result to other code.
You should use parameters whenever a function needs information from outside. You should use return values whenever the caller needs a result back. If a function only performs a side effect, such as printing to the console, a return value may not be needed.
3. Basic Syntax or Core Idea
Declaring a function with parameters
This example shows the basic structure of a function that receives two values.
func add(a: Int, b: Int) {
print(a + b)
}
Here, a and b are parameters. Both are of type Int. This function does not return a value; it only prints one.
Declaring a function with a return value
To return a result, add -> followed by the return type.
func add(a: Int, b: Int) -> Int {
return a + b
}
This function returns an Int instead of printing directly. That makes the function more flexible because the caller can store, print, or further process the result.
Calling the function
When you call the function, you pass arguments that match the parameters.
let total = add(a: 4, b: 6)
print(total)
The values 4 and 6 are arguments. They are passed into the parameters a and b.
4. Step-by-Step Examples
Example 1: A function with one parameter and no return value
This is the simplest useful pattern: give the function one input and let it perform an action.
func greet(name: String) {
print("Hello, \(name)!")
}
greet(name: "Mina")
The function receives a String and uses it in the greeting. Because it does not return anything, its purpose is just the side effect of printing.
Example 2: A function with one parameter and one return value
Here the function computes something and returns the result.
func square(number: Int) -> Int {
return number * number
}
let result = square(number: 5)
print(result)
The caller decides what to do with the returned value. That is usually better than printing inside the function.
Example 3: External argument label and internal parameter name
Swift lets you use one name when calling the function and another inside the function body. This can improve readability.
func multiply(first number: Int, by factor: Int) -> Int {
return number * factor
}
let product = multiply(first: 3, by: 4)
print(product)
Inside the function, the names are number and factor. At the call site, the labels are first and by.
Example 4: Omitting the argument label with an underscore
Sometimes a label would make the call awkward. In that case, use _.
func isEven(_ value: Int) -> Bool {
return value % 2 == 0
}
let check = isEven(8)
print(check)
The underscore removes the external argument label, so the call becomes shorter.
Example 5: Default parameter values
Default values let a caller skip some arguments when the default behavior is acceptable.
func welcome(name: String, punctuation: String = "!") -> String {
return "Welcome, \(name)\(punctuation)"
}
print(welcome(name: "Ava"))
print(welcome(name: "Ava", punctuation: "!!!"))
The first call uses the default value. The second call overrides it.
Example 6: Returning multiple values with a tuple
Swift functions can return more than one value by returning a tuple.
func minMax(numbers: [Int]) -> (min: Int, max: Int) {
var currentMin = numbers[0]
var currentMax = numbers[0]
for number in numbers {
if number < currentMin {
currentMin = number
}
if number > currentMax {
currentMax = number
}
}
return (min: currentMin, max: currentMax)
}
let range = minMax(numbers: [3, 9, 1, 7])
print(range.min)
print(range.max)
This is a useful pattern when two or more returned values naturally belong together.
5. Practical Use Cases
- Passing a username, password, or email into a validation function.
- Providing numbers to a math helper that returns a calculated result.
- Sending a list into a function that returns summary information such as count, minimum, or average.
- Creating formatting functions that receive text and return a display-ready string.
- Using default parameters to reduce repeated boilerplate in common function calls.
- Returning tuples when a function needs to provide closely related values, such as coordinates or a min/max pair.
6. Common Mistakes
Mistake 1: Using the wrong argument label
Swift checks function calls carefully, including the labels used at the call site. If the labels do not match the declaration, the call fails.
Problem: This call uses a label that the function does not expect, which can produce an error such as extraneous argument label or missing argument for parameter.
func greet(name: String) {
print("Hello, \(name)!")
}
greet(person: "Lina")
Fix: Call the function with the exact argument label defined in the function declaration.
func greet(name: String) {
print("Hello, \(name)!")
}
greet(name: "Lina")
The corrected version works because the argument label now matches the function signature.
Mistake 2: Forgetting to return a value from a function that declares a return type
If a function says it returns a type, every valid path through that function must provide a value of that type.
Problem: This function declares that it returns an Int but does not return anything, which causes a compile-time error.
func double(number: Int) -> Int {
number * 2
}
Fix: Use the return keyword to send a value back from the function.
func double(number: Int) -> Int {
return number * 2
}
The corrected version works because the function now returns the Int it promised.
Mistake 3: Expecting a parameter to change the original value without using inout
By default, Swift passes function arguments by value. Changing a parameter inside the function does not change the original variable outside the function.
Problem: This code changes only the local copy of the parameter, so the original value remains unchanged after the function call.
func increase(value: Int) {
var value = value
value += 1
}
var score = 10
increase(value: score)
print(score)
Fix: Use an inout parameter when the function should modify the caller's variable, and pass the argument with &.
func increase(inout value: Int) {
value += 1
}
var score = 10
increase(value: &score)
print(score)
The corrected version works because inout allows the function to modify the caller's variable directly.
Mistake 4: Passing a constant to an inout parameter
An inout parameter requires a mutable variable because the function may change it.
Problem: This code passes a constant to an inout parameter, which causes a compile-time error because constants cannot be modified.
func increase(inout value: Int) {
value += 1
}
let score = 10
increase(value: &score)
Fix: Pass a var value instead of a let constant.
func increase(inout value: Int) {
value += 1
}
var score = 10
increase(value: &score)
The corrected version works because mutable variables can be safely passed to inout parameters.
7. Best Practices
Use clear argument labels for readable calls
Good labels make function calls easier to understand without reading the implementation.
// Less clear
func move(x: Int, y: Int) {
print("Move to \(x), \(y)")
}
// Preferred
func move(toX x: Int, y: Int) {
print("Move to \(x), \(y)")
}
Readable calls reduce mistakes and make APIs easier to use correctly.
Return values instead of printing inside utility functions
A function that returns data is usually more reusable than one that prints directly.
// Less flexible
func area(width: Int, height: Int) {
print(width * height)
}
// Preferred
func area(width: Int, height: Int) -> Int {
return width * height
}
The returned value can be stored, tested, compared, or displayed later.
Use default parameters to simplify common calls
If most callers use the same value, a default parameter reduces repetition while still allowing overrides.
func repeatText(_ text: String, times: Int = 2) -> String {
var result = ""
for _ in 1...times {
result += text
}
return result
}
This keeps the simple case simple while preserving flexibility.
Use tuples only when the returned values naturally belong together
Tuples are convenient, but they are best for small grouped results. If the data becomes more complex, a custom type may be clearer.
func dimensions() -> (width: Int, height: Int) {
return (width: 1920, height: 1080)
}
This pattern is clean for a small result pair such as width and height.
8. Limitations and Edge Cases
- Function parameters are constants by default inside the function body. You cannot assign a new value to them unless you create a local variable or use inout.
- A function with a declared return type must return a value on every possible path.
- Returning a tuple is convenient, but very large tuples can make code harder to read and maintain.
- inout should be used carefully because it makes data flow less obvious than returning a new value.
- If an array might be empty, a tuple-returning function like minMax needs extra handling. Accessing numbers[0] on an empty array would crash.
- Default parameter values can improve call sites, but too many defaults in one function can make behavior harder to understand.
- Some "not working" function calls are really label mismatches. In Swift, labels are part of the function's public calling syntax.
9. Practical Mini Project
This mini project builds a small score-reporting function set. It uses parameters, a tuple return value, and a default parameter so you can see several ideas working together.
func calculateStats(scores: [Int], bonus: Int = 0) -> (total: Int, average: Double, passed: Bool) {
var sum = 0
for score in scores {
sum += score
}
sum += bonus
let average = Double(sum) / Double(scores.count)
let passed = average >= 60
return (total: sum, average: average, passed: passed)
}
let report = calculateStats(scores: [70, 85, 90], bonus: 5)
print("Total: \(report.total)")
print("Average: \(report.average)")
print("Passed: \(report.passed)")
This example accepts an array of scores and an optional bonus. It returns three related values as a tuple: the total, average, and pass status. The result is easy to use because each returned value has a descriptive tuple label.
In production code, you would usually add protection for an empty scores array to avoid dividing by zero.
10. Key Points
- Parameters give a function the input it needs to perform work.
- Return values give the caller the result of that work.
- Swift function calls use argument labels as part of the calling syntax.
- You can omit an external label with _ when that makes the call clearer.
- Default parameter values reduce repetition in common function calls.
- Use inout only when a function must modify the caller's variable.
- Tuples let a function return multiple related values in one result.
- If a function declares a return type, every valid path must return a matching value.
11. Practice Exercise
Try building a function that calculates a discounted price.
- Create a function named discountedPrice.
- It should accept a required price parameter of type Double.
- It should accept a discountPercent parameter of type Double with a default value of 10.
- It should return the final price after applying the discount.
- Call the function once with the default discount and once with a custom discount.
Expected output: two discounted prices printed to the console.
Hint: Convert the percentage into a decimal value before subtracting it from the original price.
func discountedPrice(price: Double, discountPercent: Double = 10) -> Double {
let discountAmount = price * (discountPercent / 100)
return price - discountAmount
}
let salePrice1 = discountedPrice(price: 80.0)
let salePrice2 = discountedPrice(price: 80.0, discountPercent: 25.0)
print(salePrice1)
print(salePrice2)
12. Final Summary
Swift parameters and return values are the foundation of useful functions. Parameters allow you to pass information into a function, and return values allow the function to send results back in a clean, predictable way. Once you understand that flow of input and output, your functions become much easier to reuse and combine.
You also saw several important Swift-specific details: argument labels, omitted labels with _, default parameter values, tuple returns, and inout parameters. These features make Swift functions expressive, but they also introduce some common mistakes, especially with labels and missing return statements.
A strong next step is to learn how Swift function types, closures, and parameter passing patterns work. Those topics build directly on the syntax and ideas covered here.