Search code examples
swiftfunctionclosuresoption-type

Swift optional escaping closure parameter


Given:

typealias Action = () -> ()

var action: Action = { }

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

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

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

doStuffAgain()

Is there any way to make the completion parameter (and action) of type Action? and also keep @escaping ?

Changing the type gives the following error:

@escaping attribute only applies to function types

Removing the @escaping attribute, the code compiles and runs, but doesn't seem to be correct since the completion closure is escaping the scope of the function.


Solution

  • 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.