55

I'm learning Swift lang, but I cannot pass optional callback argument into function:

func dismiss(completion: () -> Void) {
    if (completion) {
        return self.dismissViewControllerAnimated(true, completion: completion)
    }
    self.dismissModalViewControllerAnimated(true)
}

This shows me an error - Type () -> Void does not conform to protocol 'LogicValue'

Any suggestions?

user2864740
  • 60,010
  • 15
  • 145
  • 220
Kosmetika
  • 20,774
  • 37
  • 108
  • 172

3 Answers3

69

Update for Swift 3/4:

An optional is no longer a boolean expression, and the deprecated func dismissModalViewControllerAnimated(animated: Bool) is no longer available in Swift.

Simply declare the completion parameter as an optional closure, and pass it on to

func dismiss(animated flag: Bool, completion: (() -> Void)? = nil)

which takes an optional closure as well:

func dismiss(completion: (() -> Void)? = nil) {
    self.dismiss(animated: true, completion: completion)
}

Old (Swift 1.x?) answer:

Declare the completion parameter as (implicitly unwrapped) optional closure (() -> Void)!:

func dismiss(completion: (() -> Void)!) {
    if (completion) {
        return self.dismissViewControllerAnimated(true, completion: completion)
    }
    self.dismissModalViewControllerAnimated(true)
}

But note that you can call

self.dismissViewControllerAnimated(true, completion: completion)

in any case, because the completion parameter of that function is optional as well. And

func dismissModalViewControllerAnimated(animated: Bool)

is actually marked as deprecated.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • 1
    In this specific case it's not too important, but note that using implicitly unwrapped optional will cause a crash if you forget to check for nil before calling it. Whereas if you use a regular optional as in code ninja's answer, the compiler saves you from yourself and you forces you to write completion?() (which doesn't crash if completion is nil. – joel.d Mar 05 '16 at 00:10
  • Could you possibly explain why `(() -> Void)!` defines an optional closure? From what I've learnt so far, wouldn't this attempt to unwrap a nil value causing a runtime error if you passed a nil completion handler in? – Matthew Cawley Nov 19 '17 at 17:21
  • 1
    @MatthewCawley: An implicitly unwrapped optional is an optional as well, and yes, you have to test it if it might be nil, that's what `if (completion)` did. The above answer is quite old (Swift 1.x) and I must admit that I don't remember why I chose an IUO instead of an strong optional. Perhaps that was the parameter type of `func dismissViewControllerAnimated(_:completion:)` at that time. – But the code is outdated anyway and won't compile anymore, I have updated it for the current Swift. – Martin R Nov 19 '17 at 19:56
18

Just adding to Martin R's answer above..

The callback can be optional, instead of implicit parameter (with exclamation mark), use the optional operator.

func dismiss(completion: (() -> Void)?) {
    if completion != nil {
        return self.dismissViewControllerAnimated(true, completion: completion!)
    }

    self.dismissModalViewControllerAnimated(true)
}
Matej
  • 9,548
  • 8
  • 49
  • 66
  • Is your code functionally any different than Martin R's? It seems logically the same. – Bill Doughty Dec 28 '15 at 02:25
  • It is logically 'the same' although you could argue that the syntax of using the function is different. – Matej Dec 28 '15 at 09:45
  • Thanks. Just wanted to make sure I understood. – Bill Doughty Jan 01 '16 at 06:59
  • 1
    From what I can see this is not Logically the same as Martin R's answer above. This looks like it defines the completion closure as optional where as Martin R's answer looks like it explicitly unwraps the value which would cause a run time error if a nil closure was passed in. – Matthew Cawley Nov 19 '17 at 17:23
14

It's better to add = nil in the callback declaration, to avoid passing nil while calling it:

func dismiss(completion: (() -> Void)? = nil) {
    if (completion) {
        return self.dismissViewControllerAnimated(true, completion: completion)
    }
    self.dismissModalViewControllerAnimated(true) }

And you can call your function like this : dismiss()

Rémy Virin
  • 3,379
  • 23
  • 42