3

Here's my Swift 2.1 code snippet. The error that's occurring is shown in the comments at the point where the error appears.

The error shows up in the debugging panel, and the app crashes. The app never prints the line in the catch, nor does it gracefully return as expected.

let audioFileURL = receivedAudio.filePathURL

guard let audioFile = try? AVAudioFile(forReading: audioFileURL) else {
    print("file setup failed")
    return
}

let audioFileFrameCount = AVAudioFrameCount(audioFile.length)

audioFileBuffer = AVAudioPCMBuffer(PCMFormat: audioFile.fileFormat, frameCapacity: audioFileFrameCount)

do {
    // ERROR: AVAudioFile.mm:263: -[AVAudioFile readIntoBuffer:frameCount:error:]: error -50
    // Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'error -50'
    // -50 = Core Audio: bad param
    try audioFile.readIntoBuffer(audioFileBuffer)
}
catch {
    print("unable to load sound file into buffer")
    return
}

From everything I've seen, my do/try/catch format should be correct.

audioFile.readIntoBuffer returns void and has the keyword throws.

Yet, the catch is never executed.

What am I missing?

UPDATE: From Apple's documentation on AVAudioFile

For:

func readIntoBuffer(_ buffer: AVAudioPCMBuffer) throws

Under Discussion:

HANDLING ERRORS IN SWIFT:

In Swift, this API is imported as an initializer and is marked with the throws keyword to indicate that it throws an error in cases of failure.

You call this method in a try expression and handle any errors in the catch clauses of a do statement, as described in Error Handling in The Swift Programming Language (Swift 2.1) and Error Handling in Using Swift with Cocoa and Objective-C (Swift 2.1).

From The Swift Programming Language (Swift 2.1): Error Handline

NOTE

Error handling in Swift resembles exception handling in other languages, with the use of the try, catch and throw keywords. Unlike exception handling in many languages—including Objective-C—error handling in Swift does not involve unwinding the call stack, a process that can be computationally expensive. As such, the performance characteristics of a throw statement are comparable to those of a return statement.

And, finally, from the same document:

Handling Errors Using Do-Catch

You use a do-catch statement to handle errors by running a block of code. If an error is thrown by the code in the do clause, it is matched against the catch clauses to determine which one of them can handle the error.

I don't have to write and throw my own errors/exceptions for them to be caught. I should be able to catch Swift's exceptions as well.

Community
  • 1
  • 1
leanne
  • 7,940
  • 48
  • 77
  • 2
    `catch` will only catch the *errors* that are explicitly thrown. It will not catch *exceptions*. What you seem to have here is an exception happening in the AVAudioFile SDK, not a Swift error, so it's not caught. – Eric Aya Feb 11 '16 at 09:19
  • I've added an update including Apple documentation that uses the terms *errors* and *exceptions* interchangeably: *Error* handling in Swift resembles *exception* handling in other languages, with the use of the try, catch and throw keywords. *It should work for both!* – leanne Feb 11 '16 at 15:47
  • In the context of Swift, "error" means an error thrown by a function, yours or not, nothing else. Exceptions in Swift are not caught *ever*. It's not the same as in Java at all, for example. I understand your opinion that "It should work for both" but it's simply not the case. You can see this as a semantic situation with using the keyword "catch" if you want, because it's the same keyword as other languages but behaves very differently. It *resembles*, but *it's not*. :) – Eric Aya Feb 11 '16 at 15:50
  • Given the wording in the Programming Guide, though, you can understand my confusion, @Eric D.. I will research this further. – leanne Feb 11 '16 at 15:59
  • Yes I understand the confusion, don't worry. :) That's why I took the time to explain that in Swift, error != exception, they are two *very* different things. The guide about Do-Try-Catch only mentions errors, not exceptions. But I understand the confusion, yes. – Eric Aya Feb 11 '16 at 16:01
  • Since comments can disappear without warning, I've made my explanations into an answer. – Eric Aya Feb 11 '16 at 16:22
  • Did you ever find out what caused the error? I'm having the same problem.. – Johannes Feb 09 '18 at 14:49
  • 1
    @Johannes: I didn't find the cause. The error means "bad parameter", but could be caused by other things not even involved with that line of code. Instead, I used a different technique to work with the audio. The project, in Swift 2.1, is on [GitHub](https://github.com/leanne63/Pitch-Perfect), if you'd like to take a look. – leanne Feb 13 '18 at 04:21

3 Answers3

6

The do - catch combination is fine. This issue is simply one that cannot be caught - and therefore never makes it to the catch block.

If the issue were catchable (defined and handled via Swift's throws functionality), the catch block would've been executed.


Some semantics: there is a long-standing argument about the differences between the terms error and exception.

Some developers consider the two terms to be distinct. In this case, the term error represents an issue that was designed to be handled. Swift's throws action would fit here. In this case, a do - catch combination would allow the issue to be caught.

An exception, for these developers, represents an unexpected, usually fatal, issue that cannot be caught and handled. (Generally, even if you could catch it, you would not be able to handle it.)

Others consider the two terms to be equivalent and interchangeable, regardless of whether the issue in question can be caught or not. (Apple's documentation seems to follow this philosophy.)

(Updated to focus on the answer rather than the semantics.)

leanne
  • 7,940
  • 48
  • 77
2

catch will only catch the errors that are explicitly thrown. It will never catch exceptions.

What you seem to have here is an exception happening in the AVAudioFile SDK, not a Swift error, so it's not caught:

ERROR: AVAudioFile.mm:263: -[AVAudioFile readIntoBuffer:frameCount:error:]: error -50
Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'error -50'
-50 = Core Audio: bad param

In the context of Swift, "error" means an error thrown by a function, nothing else. The function being yours or not does not matter.

Exceptions in Swift are not caught ever. It's not the same as in Java at all, for example. In Swift, error != exception, they are two very different things.

I understand your opinion that "It should work for both" but it's simply not the case. You can see this as a semantic situation with using the keyword "catch" if you want, because it's the same keyword as other languages but behaves very differently; it resembles, but it's not the same.

As for your exception with AVAudioFile, I don't have a solution - maybe it's a bug in this SDK? Or it's not yet properly bind to Swift and the throwing system. In this case, and if nobody else has a solution, don't hesitate to report the bug to Apple.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • 1. Again, Apple's documentation uses the terms interchangeably. Regardless, the concept here is correct (and I didn't see this answer until I had posted my own). 2. "radar" isn't an official bug reporting site, and Apple doesn't read it. It is up to an individual developer to add an issue to "radar", if they so choose. I report bugs through Apple's own bug reporting site, available to paid developers in the Member Center. – leanne Feb 11 '16 at 17:59
0

see this example

struct E: ErrorType{}

func foo(i: Int) throws {
    if i == 0 {
        throw E()
    }
    print(10 / (i - 1))
}

do {
    //try foo(1) // if you uncomment this line, the execution 
    // will crash, even though the function is declared 
    // as throwing and you use proper calling style (do / try / catch pattern)
    try foo(0)
} catch {
    print("error: ", error) // error: E()
}
user3441734
  • 16,722
  • 2
  • 40
  • 59
  • *throws* handling isn't limited to your own custom functions. Swift's documentation for AVAudioFile, as I've noted in my update, specifically states *You call this method in a try expression and handle any errors in the catch clauses of a do statement ...* - and *init(forReading fileURL: NSURL) throws* – leanne Feb 11 '16 at 15:52
  • @leanne i didn't say that! it seems, that there is a bug in swift 2.1 API to AVFoundation framework (some of possible errors is not thrown and that is why your code crash with an exception). I noted that below your answer, but somebody removed my note ... In my note i asked down voter of your question to explain his motivation, because your approach seems for me to be OK. – user3441734 Feb 11 '16 at 16:22
  • @leanne by the way, the trouble is with func readIntoBuffer(_ buffer: AVAudioPCMBuffer) throws , not with init(forReading fileURL: NSURL) throws. how is declared your audioFileBuffer (it is not clear from your snippet)? – user3441734 Feb 11 '16 at 16:36
  • Thanks, @user3441734. I hope you'll upvote my question, since you thought it was fine. Also, I have corrected the function name - the documentation applicable to that function is the same. I'll post the buffer part of the snippet in a new question asking why the problem is occurring. Also, that's weird about disappearing notes. I thought only the person making the comment or a moderator could remove comments. I'm glad you clarified here. – leanne Feb 11 '16 at 18:12