Swift: Swift Return Values and Function Types
Functions can return more than basic types — they can return multiple values, and even return other functions. This lesson explores the full spectrum of Swift function return values and the core concept of functions as first-class citizens.
1. What You'll Learn
- Return multiple values using tuples
- Use functions as parameters and return values
- Name function types with
typealias - Work with nested functions and their scoping rules
- Understand the basics of function overloading
2. An E-commerce Developer's Real Story
(1) Pain: Writing a new function for every discount tier
Charlie is building a promotions engine. Different user tiers get different discounts:
func vipDiscount(_ price: Double) -> Double {
return price * 0.8
}
func memberDiscount(_ price: Double) -> Double {
return price * 0.9
}
func normalDiscount(_ price: Double) -> Double {
return price
}
Every time a new discount type is added, he writes another function. When the promotion rules change, a pile of code needs updating. Strategy switching relies on hand-written if-else; there's no way to configure it dynamically.
(2) The Functions-as-Types Solution
In Swift, functions are "first-class citizens" — you can pass and store them just like Int or String:
// Define a discount strategy type
typealias DiscountStrategy = (Double) -> Double
// Use a function dictionary instead of if-else
let strategies: [String: DiscountStrategy] = [
"vip": { $0 * 0.8 },
"member": { $0 * 0.9 },
"normal": { $0 }
]
// Switch strategy in one line
func applyDiscount(_ price: Double, level: String) -> Double {
return strategies[level]?(price) ?? price
}
(3) Result: Configurable strategies, extensible code
| Dimension | Before | After |
|---|---|---|
| Add a discount | Write function + modify if-else | Add a dictionary entry |
| Strategy switching | Hard-coded if-else | Dynamic key-based selection |
| Reusability | Each strategy standalone, not composable | Functions composable and passable |
| Test coverage | One test per strategy | Unified testing framework |
3. Multiple Return Values and Function Overloading
(1) Returning Multiple Values with Tuples
Swift tuples let a function return several values at once:
graph LR
A[Function returns tuple] --> B[Element 1]
A --> C[Element 2]
A --> D[Element 3]
B --> E["Named access: result.name"]
C --> F["Named access: result.age"]
D --> G["Indexed access: result.0"]
| Approach | Syntax | When to Use |
|---|---|---|
| Anonymous tuple | -> (Int, String) |
Simple grouping, names not important |
| Named tuple | -> (sum: Int, avg: Double) |
Semantic field access needed |
| Optional tuple | -> (Int, String)? |
Function may fail and return nil |
▶ Example: Array Statistical Analysis
// ============================================
// Return multiple statistics using a named tuple
// ============================================
func analyze(_ numbers: [Int]) -> (count: Int, sum: Int, average: Double) {
let count = numbers.count
let sum = numbers.reduce(0, +)
let average = count > 0 ? Double(sum) / Double(count) : 0
return (count, sum, average)
}
let result = analyze([85, 90, 78, 92, 88])
print("Count: \(result.count)")
print("Sum: \(result.sum)")
print("Average: \(result.average)")
Output:
TEXT 📖 Display onlyCount: 5 Sum: 433 Average: 86.6
(2) Function Overloading
Swift supports multiple functions with the same name as long as the parameter types or counts differ:
func area(width: Double, height: Double) -> Double {
return width * height
}
func area(radius: Double) -> Double {
return Double.pi * radius * radius
}
print("Rectangle area: \(area(width: 10, height: 5))")
print("Circle area: \(area(radius: 3))")
Output:
TEXT 📖 Display onlyRectangle area: 50.0 Circle area: 28.274333882308138
4. Functions as Types
(1) Function Type Syntax
Every function has a specific type, determined by its parameter types and return type:
| Function Signature | Function Type |
|---|---|
func add(a: Int, b: Int) -> Int |
(Int, Int) -> Int |
func log() -> Void |
() -> Void or () -> () |
func greet(_ name: String) |
(String) -> Void |
func double(_ n: Int) -> Int |
(Int) -> Int |
▶ Example: Functions as Variables and Parameters
// ============================================
// Functions passed as variables and parameters
// ============================================
// 1. Define two simple functions
func add(_ a: Int, _ b: Int) -> Int { return a + b }
func multiply(_ a: Int, _ b: Int) -> Int { return a * b }
// 2. Function type variable
var operation: (Int, Int) -> Int = add
print("add: \(operation(3, 4))")
// 3. Swap the function
operation = multiply
print("multiply: \(operation(3, 4))")
// 4. Function as a parameter
func execute(_ a: Int, _ b: Int, using op: (Int, Int) -> Int) -> Int {
return op(a, b)
}
print("execute add: \(execute(5, 6, using: add))")
print("execute multiply: \(execute(5, 6, using: multiply))")
Output:
TEXT 📖 Display onlyadd: 7 multiply: 12 execute add: 11 execute multiply: 30
(2) typealias for Function Type Naming
When a function type is complex or used in many places, give it a name with typealias:
// Define a function type alias
typealias IntOperation = (Int, Int) -> Int
// Use the alias for variables and parameters
func calculate(_ a: Int, _ b: Int, using op: IntOperation) -> Int {
return op(a, b)
}
let op: IntOperation = { $0 * $0 + $1 * $1 }
print("Result: \(calculate(3, 4, using: op))")
Output:
TEXT 📖 Display onlyResult: 25
5. Nested Functions and Functions Returning Functions
(1) Nested Functions
You can define functions inside other functions. Inner functions are only accessible within the outer function's scope:
// ============================================
// Nested functions: define helper functions inside
// ============================================
func formatFullName(first: String, last: String) -> String {
// Nested helper
func capitalize(_ text: String) -> String {
return text.prefix(1).uppercased() + text.dropFirst().lowercased()
}
return "\(capitalize(first)) \(capitalize(last.uppercased()))"
}
print(formatFullName(first: "alice", last: "JOHNSON"))
Output:
TEXT 📖 Display onlyAlice Johnson
(2) Functions Returning Functions
Another form of higher-order functions — returning a function:
// ============================================
// Function factory: generate different strategy functions by parameter
// ============================================
func makeMultiplier(factor: Double) -> (Double) -> Double {
func multiplier(_ value: Double) -> Double {
return value * factor
}
return multiplier
}
let double = makeMultiplier(factor: 2)
let triple = makeMultiplier(factor: 3)
print("double(5) = \(double(5))")
print("triple(5) = \(triple(5))")
Output:
TEXT 📖 Display onlydouble(5) = 10.0 triple(5) = 15.0
▶ Example: Discount Strategy Factory
// ============================================
// Discount factory with nested functions and function return values
// ============================================
typealias DiscountRule = (Double) -> Double
func discountFor(level: String) -> DiscountRule? {
// Nested discount functions
func vip(_ price: Double) -> Double { return price * 0.8 }
func member(_ price: Double) -> Double { return price * 0.9 }
func normal(_ price: Double) -> Double { return price }
switch level {
case "vip": return vip
case "member": return member
case "normal": return normal
default: return nil
}
}
if let rule = discountFor(level: "vip") {
print("VIP discount price: $\(rule(100))")
}
Output:
TEXT 📖 Display onlyVIP discount price: $80.0
6. Full Example: Promotions Engine
// ============================================
// Full example: Promotions engine
// Combines: multiple returns + function types + typealias + nested functions
// ============================================
import Foundation
// 1. Define function type aliases
typealias Discount = (Double) -> Double
typealias DiscountResult = (original: Double, final: Double, saved: Double)
// 2. Discount factory — returns a nested function
func createDiscount(minSpend: Double, rate: Double) -> Discount {
func discount(_ price: Double) -> Double {
return price >= minSpend ? price * (1 - rate) : price
}
return discount
}
// 3. Multiple return values — return discount result details
func applyDiscount(price: Double, rule: Discount) -> DiscountResult {
let final = rule(price)
let saved = price - final
return (price, final, saved)
}
// 4. Function as parameter — batch process orders
func processOrders(_ prices: [Double], using rule: Discount) -> [DiscountResult] {
return prices.map { applyDiscount(price: $0, rule: rule) }
}
// 5. Use the promotions engine
let memberRule = createDiscount(minSpend: 50, rate: 0.15)
let orderResults = processOrders([120, 45, 200], using: memberRule)
for result in orderResults {
print("$\(result.original) → $\(result.final) (saved $\(result.saved))")
}
Output:
TEXT 📖 Display only$120.0 → $102.0 (saved $18.0) $45.0 → $45.0 (saved $0.0) $200.0 → $170.0 (saved $30.0)
❓ FAQ
(a: Int) -> Void and (b: Int) -> Void are the same type.typealias Transformer<T> = (T) -> T defines a generic function type alias. However, in Swift, typealias cannot directly express generic constraints — other mechanisms are needed for that.📖 Summary
- Use named tuples to return multiple semantically meaningful values from a single function
- Functions are first-class citizens: assign to variables, pass as parameters, return as values
typealiasnames function types, improving code readability and reusability- Nested functions are scoped within the outer function and are ideal for encapsulating helper logic
- Functions returning functions (function factories) dynamically generate strategies with different behaviors
- Function overloading provides multiple versions of a function with different parameter types or counts
📝 Exercises
- Beginner: Write a
divide(_:_:)function that returns both quotient and remainder via a tuple. Call and print the result ofdivide(17, 5). - Intermediate: Write a
transformfunction for[Int]arrays that takes an(Int) -> Inttype parameter and returns a new array. Then define a typealias for that function type. - Challenge: Implement an "operation chain" — write a
chain(_:_:)function that takes an initialIntvalue and an array of[(Int) -> Int]functions, applies each function to the value in sequence, and returns the final result. Create three transformation functions and compose a chain.