Optional chaining in Swift Closure where return type has to be Void
Optional chaining wraps whatever the result of the right side is inside an optional. So if run()
returned T
, then x?.run()
returns T?
. Since run()
returns Void
(a.k.a. ()
), that means the whole optional chaining expression has type Void?
(or ()?
).
When a closure has only one line, the contents of that line is implicitly returned. So if you only have that one line, it is as if you wrote return weakSelf.rscript?.run()
. So you are returning type Void?
, but dispatch_async
needs a function that returns Void
. So they don't match.
One solution is to add another line that explicitly returns nothing:
dispatch_after(time, dispatch_get_main_queue()) {
weakSelf.rscript?.run()
return
}
Swift optional chaining doesn't work in closure
This is NOT a bug. It's simply your closure type which is wrong.
The correct type should return an optional Void
to reflect the optional chaining:
let clos = { ()->()? in currentBottle?.jiggle() }
The problem in details:
- You declare your closure as a closure that returns
Void
(namely->()
). - But, do remember that, as like every time you use optional chaining, the return type of the whole expression is of optional type. Because your closure can either return
Void
ifcurrentBottle
do exists… ornil
if it doesn't!
So the correct syntax is to make your closure return a Void?
(or ()?
) instead of a simple Void
class BottleLayer {
func jiggle() { println("Jiggle Jiggle") }
}
var currentBottle: BottleLayer?
currentBottle?.jiggle() // OK
let clos = { Void->Void? in currentBottle?.jiggle() } // Also OK
let clos = { () -> ()? in currentBottle?.jiggle() } // Still OK (Void and () are synonyms)
Note: if you had let Swift infer the correct type for you instead of explicitly forcing it, it would have fixed the issue for you:
// Even better: type automatically inferred as ()->()? — also known as Void->Void?
let clos = { currentBottle?.jiggle() }
[EDIT]
Additional trick: directly assign the optional chaining to a variable
You can even assign the function directly to a variable, like so:
let clos2 = currentBottle?.jiggle // no parenthesis, we don't want to call the function, just refer to it
Note that the type of clos2
(which is not explicitly specified here and is thus inferred automatically by Swift) in this case is not Void->Void?
— namely a function that returns either nil
or Void
) as in the previous case — but is (Void->Void)?
, which is the type for "an optional function of type Void->Void
".
This means that clos2
itself is "either nil
or is a function returning Void
". To use it, you could once again use optional chaining, simply like that:
clos2?()
This will evaluate to nil
and do nothing if clos2
is itself nil
(likely because currentBottle
is itself nil)… and execute the closure — thus the currentBottle!.jiggle()
code — and return Void
if clos2
is non-nil (likely because currentBottle
itself is non-nil).
The return type of clos2?()
itself is indeed Void?
, as it returns either nil or Void.
Doing the distinction between Void
and Void?
may seem pointless (after all, the jiggle
function does not return anything in either case), but it let you do powerful stuff like testing the Void?
in an if
statement to check if the call actually did happen (and returned Void
namely nothing) or didn't happen (and return nil
):
if clos2?() { println("The jiggle function got called after all!") }
[EDIT2] As you (@matt) pointed out yourself, this other alternative has one other major difference: it evaluates currentBottle?.jiggle
at the time that expression got affected to clos2
. So if currentBottle
is nil
at that time, clos2
will be nil
… even if currentBottle
got a non-nil value later.
Conversely, clos
is affected to the closure itself, and the optional chaining is only evaluated each time clos
is called, so it will evaluate to nil
if currentBottle
is nil… but will be evaluated to non-nil and will call jiggle()
if we call clos()
at a later time at which point currentBottle
became non-nil.
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
.
Cannot convert the expression's type' with optional in closure body
An expression containing a single expression is inferred to return that result, hence:
let closure: (number: Int) -> Void = { (number) -> Void in
optional?.updateValue(number, forKey: "key")
}
is equivalent to:
let closure: (number: Int) -> Void = { (number) -> Void in
return optional?.updateValue(number, forKey: "key")
}
You now have conflicting return types between Void
and Int?
(remember, updateValue
returns the old value)
Splitting it up with an explicit return clarifies the inferred typing.
let closure: (number: Int) -> Void = { (number) -> Void in
optional?.updateValue(number, forKey: "key")
return
}
How does one make an 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!
}
}
How to execute a closure on an optional type without unwrapping it?
I would just map over it
let oi: Int? = 1
let oiplus = oi.map{ $0 + 1 }.map { String($0) }
oiplus.map { print($0) }
Arguably not nice to map for producing a side-effect, but there's no foreach
for Optional
in Swift.
This also produces an unused result
warning in Swift 2.
If the semantic (and the warning) bug you, you can always define your own foreach
for Optional
. It's pretty straightforward:
extension Optional {
func forEach(f: Wrapped -> Void) {
switch self {
case .None: ()
case let .Some(w): f(w)
}
}
}
and then
let oi: Int? = 1
let oiplus = oi.map{ $0 + 1 }.map { String($0) }
oiplus.forEach { print($0) }
which resembles scala as much as possible.
Swift: closure as parameter reports error
Problem is that closures have auto return, when there are no explicit return. In this case the return value is Void?
as there is optional chaining involved. You can fix this by returning as last statement:
form.testClosure {
form.delegate?.formDidFinish(form)
return
}
or make testClosure return Void?
class Form {
var delegate: FormDelegate?
func testClosure(sender: () -> Void?) {
}
}
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
}
}
PromiseKit 3.0 chaining
The promise returned by then(_:)
matches the return value of the closure.
func sample() -> Promise<AnyObject> {
return Promise(1)
.then { _ -> Void in
debugPrint("foo")
}.then { _ -> Void in
debugPrint("foo")
}
}
Let me rework your method.
func sample() -> Promise<AnyObject> {
let p1: Promise<AnyObject> = Promise(1)
let p2: Promise<Void> = p1.then { _ -> Void in
debugPrint("foo")
}
let p3: Promise<Void> = p2.then { _ -> Void in
debugPrint("foo")
}
return p3
}
You can now see the expected return type of Promise<AnyObject>
doesn't match actual return type of Promise<Void>
.
If you want to have a method return Promise<AnyObject>
, then the last promise in the promise chain must return AnyObject
.
func sample() -> Promise<AnyObject> {
return firstly { _ -> Void in
debugPrint("foo")
}.then { _ -> Void in
debugPrint("foo")
}.then { _ -> AnyObject in
1
}
}
Related Topics
Dtassetproviderservice Could Not Start Dtxconnection with Simulator
Differencebetween a Property and a Variable in Swift
Exc_Bad_Access When Building Nspredicate
What Is Export * in Module.Modulemap File Inside Each Framework
iOS Aes Encryption - Fail to Encrypt
Swift - Saving Highscore Using Nsuserdefaults
Force Landscape Viewcontroller in iOS 7
Trigger Local Notifications Automatically Daily on Dynamic Time Given in Arrays Objective C iOS
Optional Chaining in Swift Closure Where Return Type Has to Be Void
How to Detect One Button in Tableview Cell
Uicollectionview Remove Top Padding
How to Put the Image on the Right Side of the Text in a Uibutton
What Is the Life Cycle of an iPhone Application
Uicollectionview Set Number of Columns
Remove Empty Space Before Cells in Uitableview