In the last article, we touched on default parameters with the findDirections example. This time, let's slow down and really dig into why they exist, when to reach for them, and a few places they show up that you might not expect. 🍥
The Problem Default Parameters Solve
Imagine you're writing a function for an anime training montage generator 🥋:
func startTraining(student: String, intensity: String, withMusic: Bool) {
print("\(student) begins training at \(intensity) intensity!")
if withMusic {
print("🎵 Dramatic music plays 🎵")
}
}
startTraining(student: "Naruto", intensity: "extreme", withMusic: true)
Now imagine 90% of your training montages are "extreme intensity, with music" — because of course they are. Calling this function with all three parameters every single time gets repetitive fast.
This is exactly the gap default parameters fill: let the common case be short, and the unusual case still possible.
func startTraining(student: String, intensity: String = "extreme", withMusic: Bool = true) {
print("\(student) begins training at \(intensity) intensity!")
if withMusic {
print("🎵 Dramatic music plays 🎵")
}
}
startTraining(student: "Naruto")
startTraining(student: "Sakura", intensity: "moderate")
startTraining(student: "Rock Lee", intensity: "extreme", withMusic: false)
All three calls are valid. The first uses both defaults, the second overrides just intensity, and the third overrides both extra parameters. 🎉
The Rule: Defaults Go Last (Usually)
Swift doesn't strictly require default parameters to come last, but it's strongly recommended — and here's why.
func describeCharacter(name: String, power: String = "unknown", age: Int) {
print("\(name) is \(age) years old with \(power) power.")
}
This compiles, but calling it gets awkward:
describeCharacter(name: "Goku", age: 30)
This actually works fine here because both power and age have labels — Swift matches by label, not position, for parameters after the first. But the moment you mix in unlabeled parameters (using _), putting a default in the middle can create confusing or even invalid call sites. The convention exists to keep things predictable: put your "always changes" parameters first, and your "rarely changes" parameters with defaults at the end.
func describeCharacter(name: String, age: Int, power: String = "unknown") {
print("\(name) is \(age) years old with \(power) power.")
}
describeCharacter(name: "Goku", age: 30)
describeCharacter(name: "Goku", age: 30, power: "Kamehameha")
Much cleaner. ✨
Default Parameters Are Everywhere in Swift's Standard Library
This is the part that clicked for me once I started noticing it: Swift itself uses default parameters constantly, and once you know to look for them, a lot of "magic" behavior makes more sense.
Example 1 — removeAll(keepingCapacity:)
var characters = ["Lana", "Pam", "Ray", "Sterling"]
characters.removeAll()
// same as:
characters.removeAll(keepingCapacity: false)
keepingCapacity defaults to false, because most of the time when you clear an array, you're done with that memory. But if you're about to refill it with a similar number of items, removeAll(keepingCapacity: true) avoids Swift having to re-allocate memory from scratch.
Example 2 — String comparison
let name = "naruto"
name.compare("Naruto") // default options
name.compare("Naruto", options: .caseInsensitive)
compare(_:options:) has a default empty options set, so the simple case (case-sensitive comparison) needs no extra arguments — but case-insensitive comparison is one parameter away when you need it.
Example 3 — print() itself!
You've been using default parameters this whole time without realizing it:
print("Hello")
print("Hello", separator: " ", terminator: "\n")
print has default values for separator (a single space) and terminator (a newline). That's why print("a", "b", "c") outputs a b c with a line break at the end — and why you can override terminator: "" if you want multiple prints to appear on the same line.
print("Loading", terminator: "")
print("...", terminator: "")
print("done!")
// Output: Loading...done!
A Quick Gotcha: Defaults Don't Mean "Optional"
It's easy to mentally lump default parameters together with optionals (Int?, String?), but they're solving different problems:
- A default parameter means "if the caller doesn't specify this, use this fallback value." The parameter inside the function is never
nil— it's always a real value (either what was passed, or the default). - An optional means "this value might genuinely not exist, and the function needs to handle that."
// Default parameter — always has a real value inside the function
func greet(name: String = "friend") {
print("Hello, \(name)!")
}
// Optional — might be nil, function must check
func greet(name: String?) {
if let name = name {
print("Hello, \(name)!")
} else {
print("Hello, friend!")
}
}
Both greet() calls above can produce "Hello, friend!" — but they get there very differently, and mixing up which one you need can lead to unnecessary if let checks when a default parameter would've done the job.
Wrap Up
Default parameters are one of those features that feel small at first but show up everywhere once you start looking — both in your own code and throughout Swift's standard library. The core idea is simple: make the common case effortless, while keeping the door open for customization.
A good rule of thumb when designing your own functions: if you find yourself passing the same value to a parameter 90% of the time, that's a strong candidate for a default. 🍥
I know these Swift concepts well from hands-on practice — I use AI to help draft and organize my explanations, and every example and structure choice is something I've reviewed and stand behind.
Top comments (1)
Episode 28 complete ✅ 💪🏻