57

A pretty handy feature of Swift functions is that function parameters can have default values:

func someFunction(parameterWithDefault: Int = 42) {
    //if no arguments are passed to the function call,
    //value of parameterWithDefault is 42
}

If a parameter is a closure, is there a way to make it have a default value? See the example below:

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

Is there a way to not force the developer to pass a value for success or failure when calling sendBody?

Eric
  • 16,003
  • 15
  • 87
  • 139
  • Good question. Unfortunately I don't know the answer. Hoping one of the Swift experts on the board can weigh in. – Duncan C Jul 01 '15 at 10:47
  • See the various answers to [How does one make an optional closure in swift?](http://stackoverflow.com/questions/24395853/how-does-one-make-an-optional-closure-in-swift). – Martin R Jul 01 '15 at 11:20

4 Answers4

54

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.

Paul Schröder
  • 1,440
  • 1
  • 10
  • 19
Airspeed Velocity
  • 40,491
  • 8
  • 113
  • 118
5

You could do something like this,

let defaultSuccess: NSData -> Void = {
    (data: NSData) in

}

let defaultFailure: NSData? -> Void = {
    (data: NSData?) in
}

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

Then, you may be able to call either one of these methods. Notice sendBody which is called with default parameters.

sendBody()
sendBody(body: , success: , failure: )

You can also call with all the variants like passing just one of the argument in the above method, for that you have to call it with named parameter.

sendBody()
sendBody(body:)

sendBody(failure: )
sendBody(success:)

sendBody(body: , success: , failure: )
Sandeep
  • 20,908
  • 7
  • 66
  • 106
5

How to set a default value for a function parameter. Works with Swift 4 and 5, Xcode 13 and 14.

func someFunction(age: Int, doSomething: @escaping () -> Void = {}){
  //do work here
  
  doSomething()
}

Then you can do this

someFunction(age: 18) {
  print("hello")
}

someFunction(age: 19)

You may or may not need to use the @escaping keyword. Xcode will warn you when it builds if you need it.

Basically if your function changes outside variables, you need to use @escaping. See Swift @escaping and Completion Handler for that.

spnkr
  • 952
  • 9
  • 18
2

My preferred way to specify public facing closures - in particular completion closures which you might want to store somewhere for later - is to define a typealias for them, like this:

public typealias FooCompletion = (String) -> Void

Then in the public facing function you can easily make it optional like this:

var onCompletion: FooCompletion? = nil

public func foo(completion: FooCompletion? = nil) {
    // Store completion for later
    onCompletion = completion
}

The completion parameter is optional, so it's allowed to be nil, and the default value is nil, meaning the caller doesn't have to specify it. Also, because you use the type in more than one place, if you need to change its definition during development there's only one place to do so. It's easy to call too:

private func someBackgroundThing() {
    var completionString = "done"
    ...
    onCompletion?(completionString)
}
jhabbott
  • 18,461
  • 9
  • 58
  • 95