Suppose the following situation in Objective-C: an array of blocks.
So I want to run a block, I do:
myBlock block = blocks[0]();
Now imagine that this line runs inside a try:
myBlock block = blocks[0];
#try {
block();
} #catch {
// catch an error
}
Now imagine that I want add a line inside a block to force the catch.
What I did in objective-C was to add this line inside a block
[NSException raise:#"Failed" format:#"Failed", nil];
Now I want to do that in Swift
let myClousure = closures[0]
do {
myClosure!()
} catch {
}
The question is: what line should I add to a closure to force the same behavior, or in other words, to make the closure fail and the catch to be triggered?
Sorry if the question is stupid but I am new to Swift.
In Objective-C, you use #try/#catch blocks to handle ObjC exceptions such as the one you are raising. In Swift, you use do/catch blocks to catch Swift errors that are explicitly thrown from a "throwing" function. Only functions (and closures) marked with the "throws" keyword are allowed to throw an error, and if you call a function that can throw, you must handle the error.
If you have an array of closures, the type signature of the closure must specify that it can throw, e.g.
let blocks: [() throws -> Void] = // get some blocks
At this point, you can (and indeed must) handle any errors thrown by calling these blocks:
let block = blocks[0]
do {
try block()
} catch let error {
print(error)
}
Related
I want to run cleanup code after a certain block of code completes, regardless of exceptions. This is not a closeable resource and I cannot use try-with-resources (or Kotlin's use).
In Java, I could do the following:
try {
// ... Run some code
} catch(Exception ex) {
// ... Handle exception
} finally {
// ... Cleanup code
}
Is the following Kotlin code equivalent?
runCatching {
// ... Run some code
}.also {
// ... Cleanup code
}.onFailure {
// ... Handle exception
}
Edit: added boilerplate exception handling - my concern is with ensuring the cleanup code runs, and maintainability.
There is one important difference, where the code inside runCatching contains an early return. A finally block will be executed even after a return, whereas also has no such magic.
This code, when run, will print nothing:
fun test1()
runCatching {
return
}.also {
println("test1")
}
}
This code, when run, will print "test2":
fun test2() {
try {
return
} finally {
println("test2")
}
}
There is one big difference between both code samples. try...finally propagates exceptions while runCatching().also() catches/consumes them. To make it similar you would have to throw the result at the end:
runCatching {
// ... Run some code
}.also {
// ... Cleanup code
}.getOrThrow()
But still, it is not really 1:1 equivalent. It catches all exceptions just to rethrow them. For this reason, it is probably less performant than simple try...finally.
Also, I think this is less clear for the reader. try...finally is a standard way of dealing with exceptions. By using runCatching() just to immediately rethrow, you actually confuse people reading this code later.
Your question sounded a little like you believed Kotlin does not have try...finally and you need to search for alternatives. If this is the case, then of course Kotlin has try...finally and I think you should use it instead of runCatching().
As per Kotlin's doc for runCatching:
Calls the specified function block and returns its encapsulated result if invocation was successful, catching any Throwable exception that was thrown from the block function execution and encapsulating it as a failure.
Even if finally always runs after a try block and also always runs after a runCatching, they do not serve the same purpose.
finally doesn't receive any argument and cannot operate on the values of the try block, while also receives the Result of the runCatching block.
TLDR; .runCatching{}.also{} is a more advanced try{}finally{}
There is also a difference in what is the result of evaluating the expression.
Consider the following code:
fun main() {
val foo = try {
throw Exception("try")
} catch(e: Exception) {
"catch"
} finally {
"finally"
}
val bar = runCatching{
throw Exception("runCatching")
}.also{
"also"
}.onFailure {
"onFailure"
}
println(foo)
println(bar)
}
The output will be:
catch
Failure(java.lang.Exception: runCatching)
https://pl.kotl.in/a0aByS5l1
EDIT:
An interesting article that points out some differences as well:
https://medium.com/#mattia23r/a-take-on-functional-error-handling-in-kotlin-515b67b4212b
Now let’s give a second look at the implementation of runCatching in the gist above. What does it do? It catches everything.
In this case, it goes even further: it catches all Throwables. For those not knowing, Throwable is everything that can go after a throw keyword; it has two descendants: Exceptions and Errors. We haven’t mentioned Errors so far; Errors usually represent something wrong that happened at a lower level than your business logic, something that can’t usually be recovered with a simple catch.
As per kotest docs: https://github.com/kotest/kotest/blob/master/doc/nondeterministic.md
You can tell eventually to ignore specific exceptions and any others will immediately fail the test.
I want to pass multiple exceptions to eventually that I know would be thrown by my block so that I can explicitly skip them.
Right now I only see a way to pass one, how do I pass more than one exception to eventually to skip it in case the block throws those exceptions?
You may use superclass for all your exceptions like
eventually(200.milliseconds, exceptionClass = RuntimeException::class) {
throw IllegalStateException()
}
or wrap exceptions
eventually(200.milliseconds, exceptionClass = IllegalStateException::class) {
runCatching { throw UnknownError() }
.onFailure { throw IllegalStateException(it) }
}
In 4.4.3 there are no features with collection of Exception
I'm trying to use the inline function use with a FileInputStream instead of the classic try/catch IOException so that
try {
val is = FileInputStream(file)
// file handling...
}
catch (e: IOException) {
e.printStackTrace()
}
becomes
FileInputStream(file).use { fis ->
// do stuff with file
}
My question is, why use the function use if it stills throws exception? Do I have to wrap use in a try/catch? This seems ridiculous.
From Kotlin documentation:
Executes the given block function on this resource and then closes it
down correctly whether an exception is thrown or not.
When you use an object that implements the Closeable interface, you need to call the close() method when you are done with it, so it releases any system resources associated with the object.
You need to be careful and close it even when an exception is thrown. In this kind of situation that is error prone, cause you might not know or forget to handle it properly, it is better to automate this pattern. That's exactly what the use function does.
Your try-catch does not close the resource so you are comparing apples to oranges. If you close the resource in finally block:
val is = FileInputStream(file)
try {
...
}
catch (e: IOException) {
...
}
finally {
is.close()
}
is definitely more verbose than use which handles closing the resource.
Excuses to my silly question, i wish to use a catch for specific exception, NSInvalidArgumentException, so, i have the next code in my OSX project:
#try
{
...
}
#catch(NSInvalidArgumentException* exception )
{
...
}
but xcode say me: "unknown type name 'NSInvalidArgumentException'", so i i was importing
import "Foundation/Foundation.h" or
import "Foundation/NSException.h"
but nothing happen, somebody known in what package or library is NSInvalidArgumentException? or which is my error? or is strictly necessary catch all exception using the superclass NSException? in the developer documentation do not show that so be it.
best regards.
NSInvalidArgumentException is not an exception type. It is a string that will be returned in the name property for an exception. So, you should catch your exception, and the name property does not match, you can re-#throw the exceptions you're not going to handle, e.g.:
#try {
// code that generates exception
}
#catch (NSException *exception) {
if ([exception.name isEqualToString:NSInvalidArgumentException])
{
// handle it
}
else
{
#throw;
}
}
See the Exception Programming Topics for more information.
I must confess that I share CodaFi's concern that this is not an appropriate use of exceptions. It's much better to program defensively, validate your parameters before you call Cocoa methods, and simply ensure that you don't generate exceptions in the first place. If you refer to the Dealing with Errors section of the Programming with Objective-C guide, exceptions are intended for "programmer errors" and they say:
You should not use a try-catch block in place of standard programming checks for Objective-C methods.
somebody known in what package or library is NSInvalidArgumentException?
It is declared in the Foundation Framework, in NSException.h. As CodaFi wrote in the comment, it is not a type, it is a string constant, declared as FOUNDATION_EXPORT NSString * const NSInvalidArgumentException;
So importing more headers won't fix your problem, because #catch(NSInvalidArgumentException* exception ) is like writing #catch(#"A string constant"* exception ), you have an object, where a type is expected.
Having said that, don't use exceptions for flow control. Have a look at the last part of this answer on SO
Hey, I'm using a ScriptDebugDelegate to notify me when an exception occurs in the exceptionWasRaised function.
However, I'd like to know how I could ignore the exception, ie. just skip the line of code that'd normally fire an exception.
Thanks
ASH
If you know the line where the exception occurs you could use a try/catch with no code inside the catch block
#try {
//my code that throws exception
} #catch ( NSException *e ) {
}
You can even filter it by using a specific exception type that you want like for CustomException:
#try {
//my code that throws exception
} #catch ( MyCustomException *e ) {
}
What will cause any MyCustomException to be ignored, and all other to be fired.
NOTE: It is important to use that only for testing and debugging proposes as empty catches are always wrong.