You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Support Error Handling in Swift 2.0 + Update README #220
// Note: Swift currently doesn't have exceptions.
// Only Objective-C code can raise exceptions
// that Nimble will catch.
This is not true any more. Swift 2.0 introduced Exceptions and Error handling in general – alongside guard and defer statements to make this easier. There are three things to clear out as far as I can see:
Check if Code needs update (I'm guessing 'yes' cause Nimble seems to be catching exceptions)
Update Code to support Swift native Error Handling
It definitely needs to be more clarification. But Swift's error handling is separate from Objective-C's exception handling.
// swift does not catch this exception
do{NSException(name:"hello", reason:"world", userInfo:nil).raise()}catcherror{
// ...
}
While it's good practice to throw exceptions to indicate programmer error, various systems do throw exceptions (eg - CoreData). Also testing throwing of exceptions is useful to test in a library.
Nimble also does handle unexpected swift errors. Also there's a throwError matcher if you expect an error to be thrown.
Thanks for the clarification @jeffh, but I'm not sure I understood all you wanted to say correctly. Does the throwError matcher also match against Swift exceptions? Or is what you want to say that Swift still doesn't have exceptions but instead "throws errors"?
In any case, I didn't see the throwError method before, thanks for that, I think that is what I was asking for. But I still think the note from the README I quoted is clear. At least when I read it I didn't even look for another way of catching errors thrown by the Swift native way cause I thought Nimble is outdated.
Maybe we should change it to something like:
Note: Swifts exception handling works different than Objective-C's. It doesn't catch exceptions raised by NSException. Use the matcher throwError instead if you want to test against exceptions thrown the Swift way.
Sadly, there's limited vocabulary for these definitions. Yes, Swift doesn't have exceptions, but it has syntactic sugar around errors. Hence the differing names of throwError and raiseException.
Exceptions need to find their handlers when thrown (aka - find the closest catch / finally block).
This implies an extra runtime cost when exceptions are thrown
Exceptions use extra storage for handlers + stack metadata
Which means having a CPU cache miss when exceptions occur because it's stored in distant memory addresses (0-cost exceptions implementation)
Or always incur a small overhead for being in a try / catch for cleaning local memory addresses, if that's used (naive exception implementations)
Many implementations capture stack traces when thrown
Capture stack information requires more runtime calls and address to function lookups
All of which make exceptions are slower than return values. That's why I'm afraid of using the term exception for Swift's error handling model. It's technically incorrect, even though it might be more common to say it's an exception.
In comparison, errors are more accepted as a data structure that is returned by a function. A perfect example is to see how the go programming language handles errors:
// go codefuncstuff() error {
f, err:=os.Open("filename.ext") // Open can failiferr!=nil {
// handle error: 98% time we return the error to our callerreturnerr
}
// do more stuff
}
Obviously this is annoying to write an if case for every error. Especially since the common case is for a function to return its error back to the caller. So swift has syntax that converts code to what go programmers would normally write in functions:
// swift code
func stuff()throws{letf=tryopen("filename.ext")
// do more stuff
}
Which is much more concise. When handling errors, swift uses syntax similar to how developers are familiar with exceptions:
do{trystuff()}catchlet error{
// log error
}
But that can be a simple conversion to an if statement in compiled code (like go's):
leterror=trystuff() // fake: pretending `try` returns the error
if error !=nil{
// log error
}
Of course, it's useful to quote a more authoritative source about swift's implementation. A note under the "Handling Errors" section of the Swift Language Guide that says:
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.
This is the reason why Swift always refers them to errors, and not exceptions.
All that as a long-winded way to say:
Objective-C has errors (NSError) and exceptions (NSException)
Swift only has errors (ErrorType)
And with the above explanation, you can say:
Errors don't suffer the performance cost of exceptions
@jeffh Thank you very much for that very detailed explanation. I realize now that I had fallen into the trap to assume I'm dealing with real exception handling when using do, try and catch – I simply had never heard of "catching an error" before, so I assumed the catch must be there for exceptions. Sorry for that misconception on my side. From my perspective, this issue can be closed now.
But in case you care about people who could fall into the same trap as I did, you might want to alter the README to state something like this (providing your great explanation above):
Note: Swift currently doesn't have exceptions (see also #220). Only Objective-C code can raise exceptions that Nimble will catch.
Activity
jeffh commentedon Dec 8, 2015
Thanks for filing an issue @dschee.
It definitely needs to be more clarification. But Swift's error handling is separate from Objective-C's exception handling.
While it's good practice to throw exceptions to indicate programmer error, various systems do throw exceptions (eg - CoreData). Also testing throwing of exceptions is useful to test in a library.
Nimble also does handle unexpected swift errors. Also there's a
throwErrormatcher if you expect an error to be thrown.Jeehut commentedon Jan 17, 2016
Thanks for the clarification @jeffh, but I'm not sure I understood all you wanted to say correctly. Does the throwError matcher also match against Swift exceptions? Or is what you want to say that Swift still doesn't have exceptions but instead "throws errors"?
In any case, I didn't see the
throwErrormethod before, thanks for that, I think that is what I was asking for. But I still think the note from the README I quoted is clear. At least when I read it I didn't even look for another way of catching errors thrown by the Swift native way cause I thought Nimble is outdated.Maybe we should change it to something like:
Or do I still misunderstand?
jeffh commentedon Jan 18, 2016
Sadly, there's limited vocabulary for these definitions. Yes, Swift doesn't have exceptions, but it has syntactic sugar around errors. Hence the differing names of
throwErrorandraiseException.Exceptions are generally associated to its common implementations:
All of which make exceptions are slower than return values. That's why I'm afraid of using the term
exceptionfor Swift's error handling model. It's technically incorrect, even though it might be more common to say it's an exception.In comparison, errors are more accepted as a data structure that is returned by a function. A perfect example is to see how the go programming language handles errors:
Obviously this is annoying to write an
ifcase for every error. Especially since the common case is for a function to return its error back to the caller. So swift has syntax that converts code to what go programmers would normally write in functions:Which is much more concise. When handling errors, swift uses syntax similar to how developers are familiar with exceptions:
But that can be a simple conversion to an
ifstatement in compiled code (like go's):Of course, it's useful to quote a more authoritative source about swift's implementation. A note under the "Handling Errors" section of the Swift Language Guide that says:
This is the reason why Swift always refers them to errors, and not exceptions.
All that as a long-winded way to say:
NSError) and exceptions (NSException)ErrorType)And with the above explanation, you can say:
I hope that was a bit clearer 😉.
Jeehut commentedon Jan 18, 2016
@jeffh Thank you very much for that very detailed explanation. I realize now that I had fallen into the trap to assume I'm dealing with real exception handling when using
do,tryandcatch– I simply had never heard of "catching an error" before, so I assumed thecatchmust be there for exceptions. Sorry for that misconception on my side. From my perspective, this issue can be closed now.But in case you care about people who could fall into the same trap as I did, you might want to alter the README to state something like this (providing your great explanation above):
Added reference to #220 in README
jeffh commentedon Jan 19, 2016
Done, thanks for the feedback @dschee. 👍
Added reference to Quick#220 in README
Merge pull request Quick#220 from mkauppila/tests/before-each-objc