Updated for Xcode 14.2
You’ve seen how Swift developers like to name their function parameters, because it makes it easier to remember what they do when the function is called. For example, we could write a function to roll a dice a certain number of times, using parameters to control the number of sides on the dice and the number of rolls:
func rollDice(sides: Int, count: Int) -> [Int] {
// Start with an empty array
var rolls = [Int]()
// Roll as many dice as needed
for _ in 1...count {
// Add each result to our array
let roll = Int.random(in: 1...sides)
rolls.append(roll)
}
// Send back all the rolls
return rolls
}
let rolls = rollDice(sides: 6, count: 4)
Even if you came back to this code six months later, I feel confident that rollDice(sides: 6, count: 4)
is pretty self-explanatory.
This method of naming parameters for external use is so important to Swift that it actually uses the names when it comes to figuring out which method to call. This is quite unlike many other languages, but this is perfect valid in Swift:
func hireEmployee(name: String) { }
func hireEmployee(title: String) { }
func hireEmployee(location: String) { }
Yes, those are all functions called hireEmployee()
, but when you call them Swift knows which one you mean based on the parameter names you provide. To distinguish between the various options, it’s very common to see documentation refer to each function including its parameters, like this: hireEmployee(name:)
or hireEmployee(title:)
.
Sometimes, though, these parameter names are less helpful, and there are two ways I want to look at.
First, think about the hasPrefix()
function you learned earlier:
let lyric = "I see a red door and I want it painted black"
print(lyric.hasPrefix("I see"))
When we call hasPrefix()
we pass in the prefix to check for directly – we don’t say hasPrefix(string:)
or, worse, hasPrefix(prefix:)
. How come?
Well, when we’re defining the parameters for a function we can actually add two names: one for use where the function is called, and one for use inside the function itself. hasPrefix()
uses this to specify _
as the external name for its parameter, which is Swift’s way of saying “ignore this” and causes there to be no external label for that parameter.
We can use the same technique in our own functions, if you think it reads better. For example, previously we had this function:
func isUppercase(string: String) -> Bool {
string == string.uppercased()
}
let string = "HELLO, WORLD"
let result = isUppercase(string: string)
You might look at that and think it’s exactly right, but you might also look at string: string
and see annoying duplication. After all, what else are we going to pass in there other than a string?
If we add an underscore before the parameter name, we can remove the external parameter label like so:
func isUppercase(_ string: String) -> Bool {
string == string.uppercased()
}
let string = "HELLO, WORLD"
let result = isUppercase(string)
This is used a lot in Swift, such as with append()
to add items to an array, or contains()
to check whether an item is inside an array – in both places it’s pretty evident what the parameter is without having a label too.
The second problem with external parameter names is when they aren’t quite right – you want to have them, so _
isn’t a good idea, but they just don’t read naturally at the function’s call site.
As an example, here’s another function we looked at earlier:
func printTimesTables(number: Int) {
for i in 1...12 {
print("\(i) x \(number) is \(i * number)")
}
}
printTimesTables(number: 5)
That code is valid Swift, and we could leave it alone as it is. But the call site doesn’t read well: printTimesTables(number: 5)
. It would be much better like this:
func printTimesTables(for: Int) {
for i in 1...12 {
print("\(i) x \(for) is \(i * for)")
}
}
printTimesTables(for: 5)
That reads much better at the call site – you can literally say “print times table for 5” aloud, and it makes sense. But now we have invalid Swift: although for
is allowed and reads great at the call site, it’s not allowed inside the function.
You already saw how we can put _
before the parameter name so that we don’t need to write an external parameter name. Well, the other option is to write a second name there: one to use externally, and one to use internally.
Here’s how that looks:
func printTimesTables(for number: Int) {
for i in 1...12 {
print("\(i) x \(number) is \(i * number)")
}
}
printTimesTables(for: 5)
There are three things in there you need to look at closely:
for number: Int
: the external name is for
, the internal name is number
, and it’s of type Int
.printTimesTables(for: 5)
.print("\(i) x \(number) is \(i * number)")
.So, Swift gives us two important ways to control parameter names: we can use _
for the external parameter name so that it doesn’t get used, or add a second name there so that we have both external and internal parameter names.
Tip: Earlier I mentioned that technically values you pass in to a function are called “arguments”, and values you receive inside the function are called parameters. This is where things get a bit muddled, because now we have argument labels and parameter names side by side, both in the function definition. Like I said, I’ll be using the term “parameter” for both, and when the distinction matters you’ll see I distinguish between them using “external parameter name” and “internal parameter name”.
SPONSORED From March 20th to 26th, you can join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer!
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.