FREE TRIAL: Accelerate your app development career with Hacking with Swift+! >>

How to customize parameter labels

Paul Hudson    @twostraws   

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 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:

  1. We write for number: Int: the external name is for, the internal name is number, and it’s of type Int.
  2. When we call the function we use the external name for the parameter: printTimesTables(for: 5).
  3. Inside the function we use the internal name for the parameter: 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”.

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for iOS devs who want to become complete senior developers — from October 18th to 24th. Learn how to apply iOS app architecture patterns through a series of lectures and practical coding sessions.

Learn more

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift (Vapor Edition) Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Server-Side Swift (Kitura Edition) Buy Beyond Code

Was this page useful? Let us know!

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.