9

Trying to use dispatch_async in which i need a throwable call, but Swift's new error handling, and method calls are confusing me, if anyone could show me how to do this correctly, or point me in the right direction, I would greatly appreciate it.

Code:

func focusAndExposeAtPoint(point: CGPoint) {
    dispatch_async(sessionQueue) {
        var device: AVCaptureDevice = self.videoDeviceInput.device

        do {

            try device.lockForConfiguration()
            if device.focusPointOfInterestSupported && device.isFocusModeSupported(AVCaptureFocusMode.AutoFocus) {
                device.focusPointOfInterest = point
                device.focusMode = AVCaptureFocusMode.AutoFocus
            }

            if device.exposurePointOfInterestSupported && device.isExposureModeSupported(AVCaptureExposureMode.AutoExpose) {
                device.exposurePointOfInterest = point
                device.exposureMode = AVCaptureExposureMode.AutoExpose
            }

            device.unlockForConfiguration()
        } catch let error as NSError {
            print(error)
        }
    }
}

Warning:

: Invalid conversion from throwing function of type '() throws -> _' to non-throwing function type '@convention(block) () -> Void'

justin shores
  • 687
  • 7
  • 24

1 Answers1

11

FINAL EDIT: This bug is fixed in Swift 2.0 final (Xcode 7 final).

Change

} catch let error as NSError {

to

} catch {

The effect is exactly the same — you can still print(error) — but the code will compile and you'll be on your way.

EDIT Here's why I think (as I said in a comment) that what you've found is a bug. This compiles just fine:

func test() {
    do {
        throw NSError(domain: "howdy", code: 1, userInfo:nil)
    } catch let error as NSError {
        print(error)
    }
}

The compiler does not complain, and in particular does not force you to write func test() throws — thus proving that the compiler thinks this catch is exhaustive.

But this does not compile:

func test() {
    dispatch_async(dispatch_get_main_queue()) {
        do {
            throw NSError(domain: "howdy", code: 1, userInfo:nil)
        } catch let error as NSError {
            print(error)
        }
    }
}

But it's the exact same do/catch blocks! So why doesn't it compile here? I think it's because the compiler is somehow confused by the surrounding GCD block (hence all the stuff in the error message about the @convention(block) function).

So, what I'm saying is, either they should both compile or they should both fail to compile. The fact that one does and the other doesn't is, I think, a bug in the compiler, and I have submitted a bug report on precisely that basis.

EDIT 2: Here's another pair that illustrates the bug (this comes from @fqdn's comment). This does not compile:

func test() {
    dispatch_async(dispatch_get_main_queue()) {
        do {
            throw NSError(domain: "howdy", code: 1, userInfo:nil)
        } catch is NSError {

        }
    }
}

But this does compile even though it is exactly the same thing:

func test() {
    func inner() {
        do {
            throw NSError(domain: "howdy", code: 1, userInfo:nil)
        } catch is NSError {

        }
    }
    dispatch_async(dispatch_get_main_queue(), inner)
}

That inconsistency is the bug.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • I believe what you've found is a bug, though I'm not entirely sure. Just in case, I'm filing it. – matt Jul 28 '15 at 01:58
  • 1
    @matt it's not a bug, the catch needs to be exhaustive in order for the closure not to throw (which it is not, 'let error as NSError' is failable) - see my answer here -> http://stackoverflow.com/questions/31599615/how-to-throw-errors-in-a-closure-in-swift/31613855#31613855 – fqdn Jul 28 '15 at 05:26
  • 1
    @fqdn I don't agree, because if you put his same do/catch structure in an ordinary function, say `func test() { do...catch...}`, it compiles fine. Why? Because it _is_ exhaustive! If it were not, you'd have to say `func test() throws`, and you do not have to. – matt Jul 28 '15 at 05:36
  • @fqdn Edited my answer to provide actual examples showing why I think this is a bug. – matt Jul 28 '15 at 05:51
  • great update! thank you for the example, that helps illustrate the point - some more food for your bug! -> if you pass the name of your first function `test` as the closure argument to `dispatch_async(_:_:)` the compiler doesn't complain :) e.g. `func test2() {dispatch_async(dispatch_get_main_queue(), test)}` - crazy... I'd be interested in following this, got a bug #? – fqdn Jul 28 '15 at 14:57
  • @fqdn Oooooh, nice example. I'm stealing it and adding it to my bug report! It's radar number 22023685 but I don't think that info does you any good; you cannot see into Apple's bugbase. Your best bet is to file separately. The more the better! – matt Jul 28 '15 at 15:03
  • @fqdn I added your example to my answer. – matt Jul 28 '15 at 15:08
  • @matt: This seems to have improved with Xcode 7 (final). All your code examples compile. `catch is NSError` gives a warning that it is always true. `catch let error as NSError ` is now considered exhaustive (and Swift automatically bridges between the ErrorType type and the NSError class). – Martin R Sep 23 '15 at 14:54
  • @MartinR Thanks, I'll add a note to the answer that this now fixed. – matt Sep 23 '15 at 16:00