108

I'm trying to declare an argument in Swift that takes an optional closure. The function I have declared looks like this:

class Promise {

 func then(onFulfilled: ()->(), onReject: ()->()?){       
    if let callableRjector = onReject {
      // do stuff! 
    }
 }

}

But Swift complains that "Bound value in a conditional must be an Optional type" where the "if let" is declared.

Forge
  • 6,538
  • 6
  • 44
  • 64
Marcosc
  • 3,101
  • 4
  • 17
  • 16

5 Answers5

131

You should enclose the optional closure in parentheses. This will properly scope the ? operator.

func then(onFulfilled: ()->(), onReject: (()->())?){       
    if let callableRjector = onReject {
      // do stuff! 
    }
 }
Cezar
  • 55,636
  • 19
  • 86
  • 87
  • Do you know what the rationale is for having to enclose it in parenthesis? – Marcosc Jun 24 '14 at 20:56
  • 5
    Probably to remove ambiguity. If the optional closure were to have a return value, it could get confusing as to what `()->Int?` means. – Cezar Jun 24 '14 at 20:58
  • 3
    Also, from the Swift book: “When declaring an optional type, be sure to use parentheses to properly scope the ? operator. As an example, to declare an optional array of integers, write the type annotation as (Int[])?; writing Int[]? produces an error.” – Cezar Jun 24 '14 at 20:59
  • @Cezar Could you please explain a bit why and where to use "Optional closure", I am curious to know this. – iLearner Apr 06 '19 at 14:17
  • 1
    @Cezar Not on a mac at the moment so my syntax may be slightly off, but remember the `?` is really just sugar for `Optional`, so you could also write ` func then(onFulfilled: ()->(), onReject: Optional<()->()>) { ` then you would not need the extra `()`, though IMO the `()?` is prettier. Also you can make it even prettier with a typealias like `typealias RejectHandler = () -> ()` `func then(onFulfilled: ()->(), onReject: RejectHandler?) {` –  Jul 22 '20 at 02:28
75

To make the code even shorter we can use nil as default value for onReject parameter and optional chaining ?() when calling it:

func then(onFulfilled: ()->(), onReject: (()->())? = nil) {
  onReject?()
}

This way we can omit onReject parameter when we call then function.

then({ /* on fulfilled */ })

We can also use trailing closure syntax to pass onReject parameter into then function:

then({ /* on fulfilled */ }) {
  // ... on reject
}

Here is a blog post about it.

Evgenii
  • 36,389
  • 27
  • 134
  • 170
41

Since I assume, that this "optional" closure should simply do nothing, you could use a parameter with an empty closure as default value:

func then(onFulfilled: ()->(), onReject: ()->() = {}){       
    // now you can call your closures
    onFulfilled()
    onReject()
}

this function can now be called with or without the onReject callback

then({ ... })
then({ ... }, onReject: { ... })

No need for Swift's awesome Optionals? here!

DiegoFrings
  • 3,043
  • 3
  • 26
  • 30
8

Maybe it's a cleaner way. Specially when the closure has complicated parameters.

typealias SimpleCallBack = () -> ()

class Promise {

func then(onFulfilled: SimpleCallBack, onReject: SimpleCallBack?){       
    if let callableRjector = onReject {
        // do stuff! 
    }
}

}
Seifolahi
  • 919
  • 3
  • 11
  • 26
1

As an alternative to creating a TypeAlias or using well-placed parentheses, there's always Swift's Optional type itself. It can be used just like your typical Java (or in this case Swift) Generic Array syntax Optional<() -> ()> OR in context:

func callAClosure(firstClosure: () -> (), secondClosure: Optional<() -> ()> {
     if let secondClosure = secondClosure {
          secondClosure()    
     }
     else { firstClosure() }
}

I find this to be fairly clean. As a bonus, in the context of SwiftUI where generics can be common, like struct CommonList<T: View>: View then you don't have to create a typealias that only gets used once (commonly the init function for that struct). Instead, you make one simple optional closure parameter, and you're all done!

Hopefully this helps anyone that runs into the issue and happy coding!

Nick C
  • 141
  • 1
  • 5