When are argument labels required in Swift?

SwiftSyntax

Swift Problem Overview


In answering this question it came about that argument labels were required for a call to init. This is normal in Swift.

class Foo {
    init(one: Int, two: String) { }
}

let foo = Foo(42, "Hello world") // Missing argument labels 'one:two:' in call

However, stranger forces are at play:

extension Foo {
    func run(one: String, two: [Int]) { }
}

foo.run(one: "Goodbye", two: []) // Extraneous argument label 'one:' in call

To use an argument label here it would have to be declared explicitly.

I haven't seen something very thorough explaining all of this in the documentation. For which varieties of class/instance/global functions are argument labels required? Are Obj-C methods always exported and imported with argument labels?

Swift Solutions


Solution 1 - Swift

All init methods require parameter names:

var view = NSView(frame: NSRect(x: 10, y: 10, width: 50, height: 50))
class Foo {
    init(one: Int, two: String) { }
}

let foo = Foo(one: 42, two: "Hello world")

All methods called on an object use parameter names for everything but the first parameter:

extension Foo {
    func run(one: String, two: [Int]) { }
}
foo.run("Goodbye", two: [])

All including class functions in Swift and objective-c follow the same pattern. You also can explicitly add external names.

extension Foo{
class func baz(one: Int, two: String){}
class func other(exOne one: Int,  exTwo two: String){}
}
Foo.baz(10, two:"str")
Foo.other(exOne: 20, exTwo:"str")

Swift functions that are not a class function don't require parameter names, but you still can explicitly add them:

func bar(one: Int, two: String){}
bar(1, "hello")

As Bryan said, it's to make Swift method calls make sense when called on objective-c methods that have parameter names in the method signature. Init methods include the first parameter because Swift changes the init methods from objective-c from initWith:... to Class() so the first parameter name is no longer included in the method name.

Solution 2 - Swift

As of Swift 3.0 this has changed again: all methods, functions, and initializers require argument labels for all parameters, unless you have explicitly opted out using the external name _. This means methods such as addChildViewController(_:) are now written like this:

func addChildViewController(_ childController: UIViewController)

This was proposed and approved as part of the Swift Evolution process, and was implemented in SR-961.

Solution 3 - Swift

Swift 3.0

In Swift 3.0, slated to be released in late 2016, the default behavior is simple:

  • All parameters to all methods have external labels by default.

You can find these rules most concisely in the Swift API Design Guidelines. This newest behavior was proposed in SE-0056, "establish consistent label behavior across all parameters including first labels," and implemented in SR-961. The default behavior may be changed as described below, in "Overriding the Default Behavior."

Swift 2.2

In Swift 2.2, the language's defaults for the presence of external argument labels have changed and are now simpler. The default behavior can be summarized as follows:

  • First parameters to methods and functions should not have external argument labels.
  • Other parameters to methods and functions should have external argument labels.
  • All parameters to initializers should have external argument labels.

The default behavior may be changed as described below, in "Overriding the Default Behavior."

An Example

These rules are best demonstrated with an example:

func printAnimal(animal: String, legCount: Int) {
    let legNoun = legCount == 1 ? "leg" : "legs"
    print("\(animal) has \(legCount) \(legNoun)")
}

struct Player {
    let name: String
    let lives: Int

    init(name: String, lives: Int) {
        self.name = name
        self.lives = lives
    }

    func printCurrentScore(currentScore: Int, highScore: Int) {
        print("\(name)'s score is \(currentScore). Their high score is \(highScore)")
    }
}


// SWIFT 3.0
// In Swift 3.0, all argument labels must be included
printAnimal(animal: "Dog", legCount: 4)
let p = Player(name: "Riley", lives: 3)
p.printCurrentScore(currentScore: 50, highScore: 110)

// SWIFT 2.2
// In Swift 2.2, argument labels must be included or omitted in exactly the following way
// given the definition of the various objects.
printAnimal("Dog", legCount: 4)
let p = Player(name: "Riley", lives: 3)
p.printCurrentScore(50, highScore: 110)

// In Swift 2.2, none of the following will work
printAnimal(animal: "Dog", legCount: 4)  // Extraneous argument label 'animal:' in call
let q = Player("Riley", lives: 3)  // Missing argument label 'name:' in call
p.printCurrentScore(50, 110)  // Missing argument label 'highScore:' in call

Overriding the Default Behavior

For any parameter to any method or function, you may deviate from the language's default, though the style guide rightly warns you not to do so unless there's a good reason.

To add an external parameter label where there would normally not be one – only applicable in Swift 2.2, since Swift 3.0 defaults to assigning external labels to every parameter – or to change an external parameter label – applicable to both versions – write the desired external parameter label before the local parameter label:

func printAnimal(theAnimal animal: String, legCount: Int) {
    let legNoun = legCount == 1 ? "leg" : "legs"
    print("\(animal) has \(legCount) \(legNoun)")
}

printAnimal(theAnimal: "Dog", legCount: 4)

To remove an external parameter label where there normally would be one, use the special external parameter label _:

func printAnimal(animal: String, _ legCount: Int) {
    let legNoun = legCount == 1 ? "leg" : "legs"
    print("\(animal) has \(legCount) \(legNoun)")
}

// SWIFT 3.0
printAnimal(theAnimal: "Dog", 4)

// SWIFT 2.2
printAnimal("Dog", 4)

These "default overrides" will work for any method or function, including initializers.

Solution 4 - Swift

Here's what I've been able to gather through reading the (fairly sparse) documentation, and through plain experimentation:

  • Init methods always need their labels. Init methods like labels, as they make it clear what init method, exactly, you want to call. Otherwise, this:

     FooBar(foos: 5)
    

    And this:

     FooBar(bars: 5)
    

    Would look exactly the same:

     FooBar(5)
    

    Nowhere else is this the case - init methods are the only place in Swift where they all have the same name, but potentially different arguments. Which is why...

  • Functions, methods, etc (anything that isn't an init method) have the first label omitted - this is for style and to cut down on boring repetitiveness. Instead of

     aDictionary.removeValueForKey(key: "four")
    

    We have this:

     aDictionary.removeValueForKey("four")
    

    And still have fairly un-ambiguous and easy-to-read arguments to functions with two parameters. So instead of

     anArray.insert("zebras", 9)
    

    We have a much more understandable-on-reading form:

     anArray.insert("zebras", atIndex: 9)
    

Which looks much better. When I was at WWDC, this was touted as a feature of Swift: Java-style modern, short arguments, without sacrificing readability. This also eases the transition from Objective-C, as Bryan Chen's answer shows.

Solution 5 - Swift

You can make a parameter label required for calling a method using # before the label.

E.g.:

func addLocation(latitude : Double, longitude : Double) { /*...*/ }
addLocation(125.0, -34.1) // Not clear

Can be improved like so:

func addLocation(#latitude : Double, #longitude : Double) { /*...*/ }
addLocation(latitude: 125.0, longitude: -34.1) // Better

(From WWDC 2014 - 416 - Building Modern Frameworks, 15 mins in)

Solution 6 - Swift

It is only make ObjC methods looks nice in Swift.

Documentation

> ## Instance Methods > ### Local and External Parameter Names for Methods > > Specifically, Swift gives the first parameter name in a method a local > parameter name by default, and gives the second and subsequent > parameter names both local and external parameter names by default. > This convention matches the typical naming and calling convention you > will be familiar with from writing Objective-C methods, and makes for > expressive method calls without the need to qualify your parameter > names. > >... > >The default behavior described above mean that method definitions in Swift are written with the same grammatical style as Objective-C, and are called in a natural, expressive way. > > ## Customizing Initialization > ### Local and External Parameter Names >However, initializers do not have an identifying function name before their parentheses in the way that functions and methods do. Therefore, the names and types of an initializer’s parameters play a particularly important role in identifying which initializer should be called. Because of this, Swift provides an automatic external name for every parameter in an initializer if you don’t provide an external name yourself.

For example for this ObjC class

@interface Counter : NSObject

@property int count;

- (void)incrementBy:(int)amount numberOfTimes:(int)numberOfTimes;

@end

and it written in Swift

class Counter {
    var count: Int = 0
    func incrementBy(amount: Int, numberOfTimes: Int) {
        count += amount * numberOfTimes
    }
}

to call ObjC version

[counter incrementBy:10 numberOfTimes:2];

and Swift version

counter.incrementBy(10, numberOfTimes:2)

you can see they are almost the same

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjtbandesView Question on Stackoverflow
Solution 1 - SwiftConnorView Answer on Stackoverflow
Solution 2 - SwiftTwoStrawsView Answer on Stackoverflow
Solution 3 - SwiftravronView Answer on Stackoverflow
Solution 4 - SwiftUndoView Answer on Stackoverflow
Solution 5 - SwiftRobertView Answer on Stackoverflow
Solution 6 - SwiftBryan ChenView Answer on Stackoverflow