Search code examples
swiftobjective-c-runtime

Why aren't default parameters for functions respected when called via Selector in Swift?


Methods called via Selector don't respect default parameter values.

EXAMPLE

If I have a button wired up to call a function via a selector:

func setupButton() {
    self.button.addTarget(self, action: #selector(printValue), for: .touchUpInside)
}

@objc func printValue(value:Int = 7)
{
    if value == 7 {
       print("received default")
    } else {
       print("default unused")
    }
}

When calling this method via code "received default" is printed, but when I press the button and call the method via selector "default unused" is printed because 7 wasn't set as the value.


Why is this happening?


Solution

  • Default parameters are inserted by the Swift compiler when you call the function.
    So this is a compile-time thing.

    When manually calling the function via a selector, you use the Objective-C runtime, which has no idea about your default parameters.
    This is a runtime thing.

    Moreover, default parameters don't exist in Objective-C.

    You can think of Swift default parameters as a compile-time convenience.
    But once you run your code, they're basically gone.

    EDIT

    If you're looking for a workaround, I can suggest having two different functions, without using default parameters:

    @objc func printValue()
    {
        printValue(value: 7)
    }
    
    @objc func printValue(value:Int)
    {}
    

    This way, you can call it without arguments through the Objective-C runtime. Note that when using #selector, you'll need to cast to resolve ambiguity:

    self.button?.addTarget(self, action: #selector((printValue as () -> Void)), for: .touchUpInside)