Jak szybko wykonać opcjonalne zamknięcie?

93

Próbuję zadeklarować argument w języku Swift, który przyjmuje opcjonalne zamknięcie. Zadeklarowana przeze mnie funkcja wygląda następująco:

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.

Marcosc
źródło
Consider using only one closure with parameters.
catanore

Odpowiedzi:

113

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
źródło
Do you know what the rationale is for having to enclose it in parenthesis?
Marcosc
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
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
@Cezar Could you please explain a bit why and where to use "Optional closure", I am curious to know this.
iLearner
@Cezar Not on a mac at the moment so my syntax may be slightly off, but remember the ? is really just sugar for Optional<T>, 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?) {
Andrew Carter
63

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
źródło
34

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
źródło
This is nice solution!
Roland T.
6

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
źródło