Swift Optional Escaping Closure Parameter

Swift optional escaping closure parameter

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.

Swift optional escaping closure

Clarification:

For understanding the case, implementing the following code would be useful:

typealias completion = () -> ()

enum CompletionHandler {
case success
case failure

static var handler: completion {
get { return { } }
set { }
}
}

func doSomething(handlerParameter: completion) {
let chObject = CompletionHandler.handler = handlerParameter
}

At the first look, this code seems to be legal, but it's not! you would get compile-time error complaining:

error: assigning non-escaping
parameter 'handlerParameter' to an @escaping closure

let chObject = CompletionHandler.handler = handlerParameter

with a note that:

note: parameter 'handlerParameter' is implicitly non-escaping func
doSomething(handlerParameter: completion) {

Why is that? the assumption is that the code snippet has nothing to do with the @escaping...

Actually, since Swift 3 has been released, the closure will be "escaped" if it's declared in enum, struct or class by default.

As a reference, there are bugs reported related to this issue:

  • Optional closure type is always considered @escaping.
  • @escaping failing on optional blocks.

Although they might not 100% related to this case, the assignee comments are clearly describe the case:

First comment:

The actual issue here is that optional closures are implicitly
@escaping right now.

Second comment:

That is unfortunately the case for Swift 3. Here are the semantics for
escaping in Swift 3:

1) Closures in function parameter position are
non-escaping by default

2) All other closures are escaping

Thus, all generic type argument closures, such as Array and Optional, are escaping.

Obviously, Optional is enum.

Also -as mentioned above-, the same behavior would be applicable for the classes and structs:

Class Case:

typealias completion = () -> ()

class CompletionHandler {
var handler: () -> ()

init(handler: () -> ()) {
self.handler = handler
}
}

func doSomething(handlerParameter: completion) {
let chObject = CompletionHandler(handler: handlerParameter)
}

Struct Case:

typealias completion = () -> ()

struct CompletionHandler {
var handler: completion
}

func doSomething(handlerParameter: completion) {
let chObject = CompletionHandler(handler: handlerParameter)
}

The two above code snippets would leads to the same output (compile-time error).

For fixing the case, you would need to let the function signature to be:

func doSomething( handlerParameter: @escaping completion)


Back to the Main Question:

Since you are expecting that you have to let the completion:(()->())? to be escaped, that would automatically done -as described above-.

Is optional closure always escaping? Should we use [weak self] or [unowned self] on it?

According to the compiler (which has the only "opinion" that matters for something like this), an optional closure parameter is always implicitly @escaping. Test this yourself by writing a function that takes an optional closure and mark it as @escaping. The compiler will inform you that it's already escaping.

As to whether you should capture self as weak or unowned, that depends. First I'd stay away from capturing self as unowned, unless you want your program to crash if you're wrong about the lifetime of self - and you might. I'd rather my program crash than silently do the wrong thing.

But that leaves whether to capture as weak. My opinion is yes, if you're in any doubt capturing a strong reference creating a reference cycle that would prevent it from properly deinitializing. But I wouldn't go so far as to say always or never do something. If you are able to reason about your object's life time so that you know the closure will be run and disposed of before the object should normally be deinitialized, then just capture as a strong reference. Also capture by strong reference if you purposefully want to extend the life of your object for the sake of the closure and you know that the closure will be executed and disposed of.

Also there is the case where you know that despite being implicitly @escaping that it doesn't actually escape. Usually that's for a function defined in your code base so you can look at the implementation, but really any time the closure is just a customization point for work that has to be done before the function you're calling returns, you can infer that it doesn't actually escape. That's not the case for completion handlers, but it might be for other things.

That's my opinion. I like to think it's an informed one, but others might disagree.

How to create a closure to use with @escaping

You're missing a return type on the closure (ie inside the last )):

typealias sessionDidReceiveMessageReplyHandlerHandler = (WCSession, [String : Any], @escaping ([String : Any]) -> Void) -> Void

Passing optional closure to the View

Change your variable let to var.

var action: (() -> Void)? = nil

Allow Multiple optional parameter in @escaping in swift

In answer to your question, you call it with a result when successful, passing nil for the error parameter, e.g. in success scenario:

onComplete(result, nil)

And if there was some error, you’d call it supplying the error code, but no value:

onComplete(nil, error)

Nowadays, we would generally use Result type. See https://www.swiftbysundell.com/basics/result/ for practical discussion.

We favor Result over the “two optional parameters” pattern because it makes it explicit to the caller that either it will be successful with a value or a failure with an error, one or the other, but not both.

func fetchData(with someString: String, completion: @escaping (Result<[String], Error>) -> Void)) {
...

doingSomethingAsynchronous {
...

// if failed

if let error = error {
completion(.failure(error))
return
}

// if successful

completion(.success(value))
}
}

And use it like so:

fetchData(with: string) { result in
switch result {
case .failure(let error):
print(error)

case .success(let value):
// do something with `value`
}
}

Now, this is how the two optional parameter pattern tends to work in practice (it is one or the other), but Result formalizes the contract, saves you from unwrapping optionals, etc.

Swift: Escaping closure How to pass values to previous ViewController after success

You could add the dictionary to your success closure like this:

func addToCart(vc:UIViewController, param:[String:String], completionHandler:@escaping (_ success:Bool, _ errorC : Error?, _ stock_flag : Bool?, result: [String:Any]?) -> Void)
{ ... }

Afterwards you can just use it in the closure.

CartViewModel().addToCart(vc: self, param:params ) { (isDone, error, stock_flag, result) in 
// Use result here
}

swift Parameter is implicitly non-escaping

The error is shown because 'b' and 'f' do have different signatures.

'b' is an escaping closure 'f' is not.

when doing:

let a = f

you are copying 'f'. The signature stays the same "nonescaping () -> ()".

Declaring a closure in function definition gives you implicit 'nonescaping' in body gives you implicit 'escaping'.

Decorating your closure in the header with @escaping will solve the problem.

func test(f: @escaping () -> ()) {

If you want to know more:
https://medium.com/swiftcommmunity/what-do-mean-escaping-and-nonescaping-closures-in-swift-d404d721f39d

Swift optional completion handler

Completion handlers are similar to function return values, but not the same. For example, compare the following functions:

/// 1. With return value
func createList(name: String) -> Response { }
/// 2. With completion handler
func createList(name: String, completion: @escaping (Response) -> Void) { }

In the first function, you'd get the return value instantly.

let response = barback.createList(name: name)
if response.status = 200 {
/// trigger some UI component
}

However, if you try the same for the second function, you'll get the Missing argument for parameter 'completion' in call error. That's because, well, you defined a completion: argument label. However, you didn't supply it, as matt commented.

Think of completion handlers as "passing in" a chunk of code into the function, as a parameter. You need to supply that chunk of code. And from within that chunk of code, you can access your Response.

                                           /// pass in chunk of code here
barback.createList(name: name, completion: { response in
/// access `response` from within block of code
if response.status = 200 {
/// trigger some UI component
}
})

Note how you just say barback.createList, not let result = barback.createList. That's because in the second function, with the completion handler, doesn't have a return value (-> Response).

Swift also has a nice feature called trailing closure syntax, which lets you omit the argument label completion:.

barback.createList(name: name) { response in
/// access `response` from within block of code
if response.status = 200 {
/// trigger some UI component
}
}

You can also refer to response, the closure's first argument, by using $0 (which was what I did in my comment). But whether you use $0 or supply a custom name like response is up to you, sometimes $0 is just easier to type out.

barback.createList(name: name) {
/// access $0 (`response`) from within block of code
if $0.status = 200 {
/// trigger some UI component
}
}


Related Topics



Leave a reply



Submit