No Trailing Closures Support for Methods with Default Parameter Values

No trailing closures support for methods with default parameter values?

It does look like a problem with trailing closures - this code works:

object.bar(b: {  () -> () in
println("foo")
})

However if the external name is removed:

func bar(a: String = "a", _ b: () -> ()) {
b()
}

this no longer works:

object.bar({  () -> () in
println("foo")
})

Moreover using a function having a string as the 2nd parameter:

func test( val1: String = "a",  val2: String) {        
}

the default parameter is correctly assigned, so this succeeds:

test("me")

which is a different behavior than using closures.

Conclusion: a method or function with parameter(s) having default value and a trailing closure does not work if at least one of the parameters with default value is not specified. Avoiding the trailing closure the function works only if the parameter has external name.

Can swift closures be set to a default value when used as a parameter in a function?

Yes, functions are just values, so you can supply them as defaults

// just to show you can do it with inline closures or regular functions
func doNothing<T>(t: T) -> Void { }

func sendBody(
body: NSData? = nil,
success: (data: NSData) -> Void = { _ in return },
failure: (data: NSData?) -> Void = doNothing
)
{ }

Alternatively, you could make them optional, that way you can detect if the caller passed one:

func sendBody(
body: NSData? = nil,
success: ((NSData) -> Void)? = nil,
failure: ((NSData?) -> Void)? = nil
)
{ success?(NSData()) }

sendBody(success: { _ in print("ah, yeah!") })

Also worth noting if you’re doing this: if the caller uses the trailing closure syntax, this will be the last closure in the argument list. So you want the last one to be the one the user is most likely to want to supply, which is probably the success closure:

func sendBody(
body: NSData? = nil,
success: ((NSData) -> Void)? = nil,
failure: ((NSData?) -> Void)? = nil
)
{
if success != nil { print("passed a success closure") }
if failure != nil { print("passed a failure closure") }
}

// this prints "passed a failure closure"
sendBody { data in
print("which closure is this?")
}

Other than this, the order in the function declaration doesn’t matter to the caller – defaulted arguments can be supplied in any order.

How to have an optional trailing closure?

I'm not sure you've got your definition of optional entirely correct. It doesn't mean you don't need to supply a value for the closure argument; it means closure can either have a value (of a closure in your case) or be nil. Therefore, if you wanted to call hello without providing a closure you would write:

hello("abc", nil)

However, you can achieve what you're after using default parameter values (I'd recommend you have a look at The Swift Programming Guide: Functions). Your function would be:

// Note the `= nil`:
func hello(message: String, closure: ((msg: String ) -> Void)? = nil) {
println("called hello with: \(message)")

closure?(msg: message)
}

// Example usage:
hello("abc")
hello("abc") { println($0) }

Swift: function with default parameter before non-default parameter

The current compiler does allow default parameters in the middle of a parameter list.

screenshot of Playground

You can call the function like this if you want to use the default value for the first parameter:

f(1)

If you want to supply a new value for the first parameter, use its external name:

f(first: 3, 1)

The documentation explains that parameters with a default value are automatically given an external name:

Swift provides an automatic external name for any defaulted parameter you define, if you do not provide an external name yourself. The automatic external name is the same as the local name, as if you had written a hash symbol before the local name in your code.

How come and when is it possible to use shorter method signatures in Swift?

That particular shorthand is possible for two reasons: default values for input parameters and trailing closures.
Have a look at the actual type signature for the function:

func async(group: DispatchGroup? = default, qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, execute work: @escaping () -> Void)

You can clearly see that all input parameters have default values except for the closure. If you omit any of these, the compiler will use the default value.

Since work is a closure, which is the last input parameter to the function, as a shorthand notation, you can move it outside the parentheses and just write it after the function call, which is called a trailing closure.

Implicit parameters in trailing closure

In

doMathOperation(4, 5, operation: multiply)

...you are being asked for a function that takes two Double parameters, and multiply is the name of just such a function, so it compiles and works.

In

doMathOperation(4, 5) { ... }

the curly braces themselves are the (body of the) function that takes two Double parameters. It makes no sense to put the name of another such function inside the curly braces.

But of course you are free to call any function you like, passing along the parameters that were passed to you; hence this works:

doMathOperation(4, 5) { multiply($0,$1) }

So, just to sum up: In the first one

doMathOperation(4, 5, operation: multiply)

there is one function, multiply, and 4 and 5 are passed to it. In the second one

doMathOperation(4, 5) { multiply($0,$1) }

there are two functions, "anonymous" (the curly braces) and multiply, and the 4 and 5 are passed into the first one and then it calls the second one.



Related Topics



Leave a reply



Submit