68

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?

Nicolapps
  • 819
  • 13
  • 29
jtbandes
  • 115,675
  • 35
  • 233
  • 266

6 Answers6

45

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.

Connor Pearson
  • 63,902
  • 28
  • 145
  • 142
  • Your answer seems to be the most complete here. Can you add some references to external documentation? – jtbandes Jul 19 '14 at 05:32
  • I wanted to force including the name of my parameter `ascending` so that the call site would be a little more explicit. So I made the method signature `sortByDate(ascending ascending: Bool)` as indicated by this answer. Xcode suggested I could simplify this by changing `ascending ascending` to `#ascending` which I did. I think we can say that Swift is _clearly_ a "modern programming language" since it embraces hashtags (which have always been 'pound signs' to me). Now I want to add a `#stfu` parameter somewhere in my code. ;-) – mharper Mar 27 '15 at 17:58
  • 2
    A helpful quote from The Swift Programming Language: *"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."* – jtbandes Apr 21 '15 at 17:17
  • Might also want to add that you can force the use of the first argument label with `#` like this: `func myFunc( #parameter: String) {}` and it will have to be called like this: `myFunc(parameter:"")`, `myFunc("")` would show error. like @Robert shows in his answer – SirRupertIII Jun 12 '15 at 22:21
  • To update the previous comment, "'#' has been removed from Swift. double up 'parameter' to make the argument label the same as the parameter name" `func myFunc(parameter parameter: String) {}` – Mark Mckelvie Dec 29 '15 at 19:44
  • @jtbandes Thanks for the reference, it make a whole lot more sense, because `init` carries a very generic meaning, which makes it difficult to read... Swift in general has leaned toward making code **more readable**, in ObjC. In ObjC you had `+` for class methods in Swift it's `static` in ObjC you had to manually do lazy instanstition, in Swift you just use the `lazy` keyword, in ObjC you had to do casting using `(NSMutableArray*)` in Swift you use the keyword `as` again making it all more readible – mfaani May 31 '16 at 14:06
35

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.

jtbandes
  • 115,675
  • 35
  • 233
  • 266
TwoStraws
  • 12,862
  • 3
  • 57
  • 71
  • 2
    I was going to post a new question on this but perhaps you could answer: why would one want to omit an argument label? As I see it, it just seems arbitrary when and when not to omit argument labels. – tfantina Dec 24 '16 at 01:44
  • Would `add(childController: UIViewController)` be bad? How should we decide whether a parameter description should be part of the function name or an argument label? – Declan McKenna Apr 14 '17 at 08:21
  • @tfantina in my case, I have a class that writes to a log file. It's much easier to do Log.write("an error message") than Log.write(text:"an error message") over and over again. Compare it to print() function. The argument label clutters the syntax (IMO) in this case and seems redundant – bergy Jan 08 '18 at 02:07
33

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.

ravron
  • 11,014
  • 2
  • 39
  • 66
  • 1
    Thank you so much! Now that you've described the rules, is there any chance we can get a "why"? Just, why? Is it only me, or do these syntax rules make absolutely no sense whatsoever? – Nostalg.io Jan 03 '16 at 06:30
  • 2
    Well, the real why can only come from the core Swift team, who implemented these guidelines. Some thoughts: these rules make it fairly obvious how to convert method names from Objective-C to Swift. Also, if it matters, they're less convoluted than in Swift 2.0 and below (previously, the rules for methods and functions differed subtly). – ravron Jan 03 '16 at 06:48
  • this is incredibly stupid. in Swift 2.2 calling `printAnimal(animal:"Dog", legCount: 4)` will cause an error. why not make this consistent apple? – gondo Apr 17 '16 at 16:57
  • @gondo: You'll be happy to see that in Swift 3.0, the rules are changing. See the note at the end of the edited answer. – ravron May 19 '16 at 17:09
  • In Swift 3 first parameter has external argument label. announced in [WWDC16 API design guidelines](https://developer.apple.com/videos/play/wwdc2016/403/) – Gerald Jun 23 '16 at 09:03
  • @Gerald Yup! Did you see the note at the end of my answer? – ravron Jun 23 '16 at 15:21
  • @RileyAvron oops. Maybe shift the note up? After all anyone can start labelling their first parameter regardless of the version they are using. New programmers will not need to change their habit when swift 3 is fully released – Gerald Jun 24 '16 at 02:14
  • @Gerald good suggestion. I've worked the answer to feature Swift 3.0 most prominently. – ravron Aug 03 '16 at 17:25
12

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.

Community
  • 1
  • 1
Undo
  • 25,519
  • 37
  • 106
  • 129
  • 2
    Actually, functions don't have any external labels at all (by default). – radex Jul 18 '14 at 08:24
  • +1 Good answer - the only problem with the 'first label omitted feature' is that methods like this: `func insertLocation(lat : Double, lon : Double) { ...` are then invoked like so: `insertLocation(2.5, lon: 168.3)`. The fix would be to call it `insertLocationWithLat` but then it its longer than it needs to be. Is there any way to disable the first label omitted feature on methods? – Robert Nov 02 '14 at 14:05
  • > Is there any way to disable the first label omitted feature on methods? -> Yes use `#` see my answer – Robert Nov 19 '14 at 14:51
8

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)

Robert
  • 37,670
  • 37
  • 171
  • 213
4

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

Community
  • 1
  • 1
Bryan Chen
  • 45,816
  • 18
  • 112
  • 143
  • 2
    This doesn't seem like an accurate comparison. Wouldn't the usual objective-C implementation include the first argument in the name - `incrementByAmount:(int)amount numberOfTimes:(int)numberOfTimes`? I don't see much swift written as `incrementByAmount(amount: Int, numberOfTimes: Int)` – Ben Packard Feb 21 '15 at 04:22