Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift optional escaping closure parameter

People also ask

What is escaping closure in Swift?

An escaping closure is a closure that's called after the function it was passed to returns. In other words, it outlives the function it was passed to. A non-escaping closure is a closure that's called within the function it was passed into, i.e. before it returns.

How do you make a optional closure in Swift?

You should enclose the optional closure in parentheses. This will properly scope the ? operator. func then(onFulfilled: ()->(), onReject: (()->())?){ if let callableRjector = onReject { // do stuff! } }

What is difference between escaping and non-escaping closures?

Escaping closures are different from non-escaping closures since we can preserve escaping closures to execute them later on. Meanwhile, the function body can execute and returns the compiler back. The scope of the escaping closure exists and has existence in memory as well until it gets executed.

Why do you need to declare closures escaping?

Storing Closure Outside the Function Essentially the closure is escaping from the function scope. Look at the example below. Here we are storing the closure in the variable holder , which we can later on use and execute the closure. So we must make the closure parameter @escaping in order to remove the compiler error.


from: swift-users mailing list

Basically, @escaping is valid only on closures in function parameter position. The noescape-by-default rule only applies to these closures at function parameter position, otherwise they are escaping. Aggregates, such as enums with associated values (e.g. Optional), tuples, structs, etc., if they have closures, follow the default rules for closures that are not at function parameter position, i.e. they are escaping.

So optional function parameter is @escaping by default.
@noeascape only apply to function parameter by default.


There is a SR-2552 reporting that @escaping is not recognizing function type alias. that's why the error @escaping attribute only applies to function types. you can workaround by expanding the function type in the function signature:

typealias Action = () -> ()

var action: Action? = { }

func doStuff(stuff: String, completion: (@escaping ()->())?) {
    print(stuff)
    action = completion
    completion?()
}

func doStuffAgain() {
    print("again")
    action?()
}

doStuff(stuff: "do stuff") {
    print("swift 3!")
}

doStuffAgain()

EDIT 1::

I was actually under a xcode 8 beta version where the bug SR-2552 was not resolved yet. fixing that bug, introduced a new one(the one you're facing) that is still open. see SR-2444.

The workaround @Michael Ilseman pointed as a temporary solution is remove the @escaping attribute from optional function type, that keep the function as escaping.

func doStuff(stuff: String, completion: Action?) {...}

EDIT 2::

The SR-2444 has been closed stating explicitly that closures in parameters positions are not escaping and need them to be marked with @escaping to make them escaping, but the optional parameters are implicitly escaping, since ((Int)->())? is a synonyms of Optional<(Int)->()>, optional closures are escaping.


I ran into a similar problem because mixing @escaping and non-@escaping is very confusing, especially if you need to pass the closures around.

I ended up assigning a no-op default value to the closure parameter via = { _ in }, which I think makes more sense:

func doStuff(stuff: String = "do stuff",
        completion: @escaping (_ some: String) -> Void = { _ in }) {
     completion(stuff)
}

doStuff(stuff: "bla") {
    stuff in
    print(stuff)
}

doStuff() {
    stuff in
    print(stuff)
}

I got it working in Swift 3 without any warnings only this way:

func doStuff(stuff: String, completion: (()->())? ) {
    print(stuff)
    action = completion
    completion?()
}