Swift - Optional Void

Swift - Optional Void

This is simply because you are using Optional Chaining. The method returns Void, but it is possible for the whole chain to return nil before the method is ever called.

Essentially, a return value of Void will mean the call was actually made (self and client both have values) while a nil result will mean that one of those were nil.

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
}
}

Passing optional closure to the View

Change your variable let to var.

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

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.

Why its impossible to cast `(()-Void)` to `(()-Void)?`

When a closure is wrapped inside an optional, that closure "escapes". If you don't know what "escapes" means, read this answer of mine.

The non-optional closure in the second getData, however, is not @escaping. So you are trying to make a non-escaping closure escape! The error message is kind of confusing here, but if you pass .some(callback) rather than callback directly, it becomes clear what's happening:

Converting non-escaping parameter 'callback' to generic parameter 'Wrapped' may allow it to escape

So you should mark callback in your second getData as @escaping:

func getData(id: CLong, callback: @escaping ((Dictionary<String, Any>) -> Void) ) {

Optional try for function with no return value

There is no reason for the compiler to complain. The return type
of

func throwingVoidFunction() throws { ... }

is Void and therefore the type of the expression

try? throwingVoidFunction()

is Optional<Void>, and its value is nil (== Optional<Void>.none) if an error was thrown while evaluating the expression,
and Optional<Void>.some() otherwise.

You can ignore the return value or test it against nil. An
example is given in An elegant way to ignore any errors thrown by a method:

let fileURL = URL(fileURLWithPath: "/path/to/file")
let fm = FileManager.default
try? fm.removeItem(at: fileURL)

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.

Swift optional promotion vs generic overload resolution

Specificity seems to always trump variance conversions, according to my experiments. For example:

func bar<T>(_ x: [Int], _ y: T) { print("A") }
func bar<T: P>(_ x: [Any], _ y: T) { print("B") }

bar([1], Y()) // A

bar is more specific, but requires a variance conversion from [Int] to [Any].

For why you can convert from (Y?) -> Void to (P) -> Void, see this. Note that Y is a subtype of Y?, by compiler magic.

Since it is so consistent, this behaviour seems to be by design. Since you can't really make Y not a subtype of Y?, you don't have a lot of choices if you want to get the desired behaviour.

I have this work around, and I admit it's really ugly - make your own Optional type. Let's call it Maybe<T>:

enum Maybe<T> {
case some(T)
case none

// implement all the Optional methods if you want
}

Now, your (Maybe<Y>) -> Void won't be converted to (P) -> Void. Normally I wouldn't recommend this, but since you said:

in the real-world code where I encountered this, the closure has multiple params, any of them can be optional, so this would lead to a combinatorial explosion.

I thought reinventing Optional might be worth it.



Related Topics



Leave a reply



Submit