dispatch_async() with throwables swift 2 Xcode 7
Asked Answered
L

1

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'

Lathing answered 28/7, 2015 at 1:43 Comment(0)
N
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.

Neap answered 28/7, 2015 at 1:50 Comment(9)
I believe what you've found is a bug, though I'm not entirely sure. Just in case, I'm filing it.Neap
@Neap 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 -> #31600115Hocuspocus
@Hocuspocus 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.Neap
@Hocuspocus Edited my answer to provide actual examples showing why I think this is a bug.Neap
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 #?Hocuspocus
@Hocuspocus 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!Neap
@Hocuspocus I added your example to my answer.Neap
@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).Piece
@MartinR Thanks, I'll add a note to the answer that this now fixed.Neap

© 2022 - 2024 — McMap. All rights reserved.