This question already has answers here:
How do I catch "Index out of range" in Swift?
(6 answers)
Closed 5 years ago.
How to handle errors for methods or code that does not explicitly throw?
Wrapping it a do / catch block results in a compiler warning:
"'catch' block is unreachable because no errors are thrown in 'do' block"
Coming from C# / JAVA background this is an oddity to say the least. As a developer I should be able to safeguard and wrap any code block in do/catch block. Just because a method is not explicitly marked with "throw" does not mean that errors will not occur.
What you are asking is not possible in Swift, as Swift has no facility to handle runtime errors such as out-of-bounds, access violations or failed forced unwrapping during runtime. Your application will terminate if any of these serious programming errors will occur.
Some pointers:
Q: How to handle EXC_BAD_ACCESS? A: Not possible
Q: How to handle failed forced unwrap? A: Not possible
Q: How to handle array out of bounds? A: Not possible
Long story short: Don't short-cut error handling in Swift. Play it safe, always.
Workaround: If you absolutely must catch runtime-errors, you must use process boundaries to guard. Run another program/process and communicate using pipes, sockets, etc.
I suspect that you'd like to catch errors which is not explicitly marked with "throws".
This makes no sense.
You cannot catch other than errors which is explicitly marked with "throws".
So, this warning is valid.
For this example, if executed, fatal error: Index out of range will occur.
This is runtime error, and you cannot catch it.
For this example, you should check elements size like this, instead of doing try-catch error handling:
Faced with an exception thrown from a method which cannot throw. Found out this exception was thrown from objective-c part of API. So you should catch it in old style way using objective-c.
Firstly create objective-c class which takes several blocks in init method - for try, catch and finally.
#import <Foundation/Foundation.h>
/**
Simple class for catching Objective-c-style exceptions
*/
#interface ObjcTry : NSObject
/**
* Initializeer
*
* #param tryBlock
* #param catchBlock
* #param finallyBlock
*
* #return object
*/
- (_Nonnull id)initWithTry:(nonnull void(^)(void))tryBlock catch:(nonnull void(^)( NSException * _Nonnull exception))catchBlock finally:(nullable void(^)(void))finallyBlock;
#end
In .m file:
#import "ObjcTry.h"
#implementation ObjcTry
- (_Nonnull id)initWithTry:(nonnull void(^)(void))tryBlock catch:(nonnull void(^)( NSException * _Nonnull exception))catchBlock finally:(nullable void(^)(void))finallyBlock
{
self = [super init];
if (self) {
#try {
tryBlock ? tryBlock() : nil;
}
#catch (NSException *exception) {
catchBlock ? catchBlock(exception) : nil;
}
#finally {
finallyBlock ? finallyBlock() : nil;
}
}
return self;
}
#end
Second, add its headers to the Bridging Header file.
#import "ObjcTry.h"
And use it in your swift code like that:
var list: [MyModel]!
_ = ObjcTry(withTry: {
// this method throws but not marked so, you cannot even catch this kind of exception using swift method.
if let items = NSKeyedUnarchiver.unarchiveObject(with: data) as? [MyModel] {
list = items
}
}, catch: { (exception: NSException) in
print("Could not deserialize models.")
}, finally: nil)
There is a difference between ERRORS and EXCEPTIONS. Swift deals only with errors which are explicitly THROWN and has no native capability for dealing with EXCEPTIONS. As others have commented ERRORS must be thrown and you can't catch what isn't thrown.
By contrast Objective-C #try-#catch deals with exceptions, not errors,. Some objc methods may cause exceptions but do not declare them in any way to the compiler. e.g. FileHandle.write. Such exceptions are more closely aligned to Java's RuntimeException which also does not need to be declared.
There are some situations such as file handling where it would be nice to handle exceptions cleanly in Swift and it is possible by using an Objective-C wrapper. See http://stackoverflow.com/questions/34956002/how-to-properly-handle-nsfilehandle-exceptions-in-swift-2-0
Code reproduced here:
#ifndef ExceptionCatcher_h
#define ExceptionCatcher_h
#import <Foundation/Foundation.h>
NS_INLINE NSException * _Nullable tryBlock(void(^_Nonnull tryBlock)(void)) {
#try {
tryBlock();
}
#catch (NSException *exception) {
return exception;
}
return nil;
}
#endif /* ExceptionCatcher_h */
Then calling it from Swift:
let exception = tryBlock {
// execute dangerous code, e.g. write to a file handle
filehandle.write(data)
}
if exception != nil {
// deal with exception which is of type NSException
}
As others mentioned, you should not catch these errors, you should fix them, but in case you want to execute more code before the program terminates, use NSSetUncaughtExceptionHandler in AppDelegate in applicationdidFinishLaunchingWithOptions function.
The function description:
Changes the top-level error handler.
Sets the top-level error-handling
function where you can perform last-minute logging before the program
terminates.
You simply cannot. The whole do-try-catch or do-catch statement is meant to be used to catch unhandled errors and...
I mean there is no point in catching error if no error occurs in first place... I see no scenario why would you want to do such thing, you make only compiler angry for no reason.
It's the same scenario if you safely unwrap optional with if let or guard let statements
guard let smth = smthOpt?.moreSpecific else { return }
//Compiler gives warning - unused variable smth. You wouldn't declare the variable and then not use it, or you would?
Simply Do-Catch is not meant to be used for safe use and I don't see any reason why to use it when not dealing with risky operations which need to catch...
for further understanding, see:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html
Related
NSSetUncaughtExceptionHandler catches only Objective-C exceptions. I need to catch only swift exceptions. Is it possible?
NSSetUncaughtExceptionHandler catches this one. let arr = NSArray() let x = arr[4]
I also want to catch this crash. let number: Int? = nil let val = number!
As I already mentioned in my comment above
Swift does not deal with exception, it deals with error, hence when you use try catch block of swift for let x = arr[4] you cant catch the exceptions (Its array out of bound exception not an error).
As Vadian also pointed out Swift encourages you to catch and solve those exceptions rather than catching them at run time.
That being said, there might be scenarios when you have to deal with exceptions explicitly, example you are dealing with third party framework which is written in Objective-C and might throw an exception or you might be dealing with certain iOS API which still runs in Objective-C runtime and throws exception in such cases, its handy to have a have method of your own in objective C and pass swift statements to it using block / closure and execute them inside Objective-C try catch block,
How can you do it?
Add an Objective-C file to your swift project, it will ask you should it create bridging header as well, say yes, update your .h and .m files as shown
ExceptionCatcher.h
#import <Foundation/Foundation.h>
#interface ExceptionCatcher : NSObject
+ (void)catchExceptionIfAny: (void(^)(void)) executeCodeBlock withCompletion: (void (^)(NSException *)) completion;
#end
ExceptionCatcher.m
#import <Foundation/Foundation.h>
#import "ExceptionCatcher.h"
#implementation ExceptionCatcher : NSObject
+ (void)catchExceptionIfAny: (void(^)(void)) executeCodeBlock withCompletion: (void (^)(NSException *)) completion {
#try {
executeCodeBlock();
}
#catch (NSException *exception) {
NSLog(#"%#", exception.description);
completion(exception);
}
}
#end
Finally, update your bridging header file with
#import "ExceptionCatcher.h"
And come to swift file and use it as
let x = NSArray()
ExceptionCatcher.catchExceptionIfAny({[weak self] in
let a = x[4]
debugPrint(a)
}) { (exception) in
if let e = exception {
debugPrint(e.description)
}
}
As you can see obviously x[4] should end up with array out of bound exception, now instead of crashing exception will be captured and returned to your completion block.
The code above works because x is declared as NSArray which is extended form NSObject if you use x = [1,2,3] you will be creating an instance of Swift Array and obviously swift array wont throw exception. So be very careful when you use this method.
This is not a work around to catch all sort of Exceptions and should be used sparingly (only in cases when avoiding it is impossible) This is an overkill and unnecessary for this usecase however
This question already has answers here:
Catch an exception for invalid user input in swift
(3 answers)
Closed 5 years ago.
I want to validate user created expressions (like "2+2", "5+7" or more complex). I use NSExpression class to parse and calculate this expressions. This is my Playground code:
import UIKit
let string = "2+2"
var ex:NSExpression?
do {
ex = NSExpression(format: string)
}
catch {
print("String is not valid expression")
}
if let result = ex?.expressionValue(with: nil, context: nil) as! NSNumber? {
print("result is \(result)")
}
When I use valid expression ("2+2") - I get the result. But sometime user can provide wrong string ("2+" for example). With this string my app crashes with this:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unable to parse the format string "2+ == 1"'
I don't understand how I can catch this exception and why code above don't do this. Now I use Objective C class (with same logic), calling this method from my swift code, and in that class I really can catch such exception:
+(NSNumber *)solveExpression:(NSString *)string
{
id value;
#try {
NSExpression *ex = [NSExpression expressionWithFormat:string];
value = [ex expressionValueWithObject:nil context:nil];
}
#catch (NSException *e) { }
return value;
}
This works and I can get correct parse state (nil means problems with expression) and result (NSNumber), but I really want to understand how to do all this things correct and entirely in Swift.
NSInvalidArgumentException is not a catchable error in the sense of Java exceptions. Apple doesn't guarantee that your program will be in a correct state when you catch this exception and chances are that something will go wrong.
You should probably use some other mechanism to check if the string is valid before you pass it to the method.
This is what the book Using Swift with Cocoa and Objective-C has to say:
Although Swift error handling resembles exception handling in Objective-C, it is entirely separate functionality. If an Objective-C method throws an exception during runtime, Swift triggers a runtime error. There is no way to recover from Objective-C exceptions directly in Swift. Any exception handling behavior must be implemented in Objective-C code used by Swift.
[My bold]
Having just skimmed the reference for NSExpression I can't see a simple way around the issue. The quote above recommends writing a a bit of Objective-C code to do it. The simplest way is probably to create a C function:
Declaration:
extern NSExpression* _Nullable makeExpression(NSString* format _Nonnull);
Definition
NSExpression* _Nullable makeExpression(NSString* format _Nonnull)
{
NSExpression* ret = nil;
#try
{
// create and assign the expression to ret
}
#catch(NSException* e)
{
// ignore
}
return ret;
}
The function returns nil for expressions that are in error.
You could probably add an NSError** parameter to be used when there is a failure. You could also probably make this a method in a category on NSExpression and then the return nil for error/fill in NSError pattern would probably be imported to Swift as a Swift throwing method.
I should say, by the way, that an Objective-C exception is not really guaranteed to leave your program in a consistent state. Fingers crossed it is OK in this case.
I come from a .NET background, where error handling can be as simple as wrapping a set of statements in a try-catch. For example:
try
{
statement1
statement2
}
catch (ex)
{
log(ex.Message)
}
I'm trying to add error handling in my Swift project, and all the articles I have read so far seem to indicate that error handling requires more work in Swift. In the example above, it seems I would need to know exactly which statement throws an error, and then add a "try" before it. Is it not possible to simply wrap a block of code in a try-catch, and inspect the error that gets thrown?
Nope, you can't just wrap a block of code with try-catch.
First of all, cause not every line of code could produce exceptions. Only functions marked as "throws" could produce exceptions.
For example, you have some function:
deserialise(json: JSON) -> MyObjectType throws
And let this functions throws exceptions of type MyErrorType
That's how you should use it:
....
do {
let deserialisedObject = try deserialise(jsonObject)
... // do whatever you want with deserialised object here
} catch let error as MyErrorType {
... // do whatever you want with error here
}
...
So, as you see, exceptions in swift is not the same thing as exceptions in C++(or other regular language) for .NET
You can use try catch in swift just like you do in .net with this little change in structure,
before that create a enum of exceptions which you want to catch like follows
//exceptions to catch, change as per your need
enum someActionError: Error {
case invalidSelection
case outOfStock
}
//use
do {
try statement1
} catch someActionError.invalidSelection {
print("Invalid Selection.")
} catch someActionError.outOfStock {
print("Out of Stock.")
}
Coming from Android, wrapping a block of code in a try..catch statement is straightforward.
try{
//A whole lot of code
} catch (Exception e) {
//Exception is a parameter
}
I wanted to do the same thing in Swift, but it doesn't seem to be quite this easy.
If I understand correctly, you need place a try in front of something that you think will go wrong.
Is there anything similar to the Android - functionality I described in the Swift toolbox?
Error handling in Swift 2 takes two things into consideration:
Whether the method can throw an error (marked with throws keyword in its declaration).
Whether the method that can throw is called from throwing scope (e.g. another methods that can throw) or not.
For non-throwing methods it's simple: you do not need to put any try or do/catch.
Methods that throw must always be prefixed by try, regardless of scope. So, that's a part of the answer to your question.
In case if scope is throwing, you do not have to use do/catch (but you can). In this case all errors thrown in current scope that are not handled by it will be passed upstream to the parent throwing scope.
If the scope is non-throwing, then you must handle all errors that can possibly be thrown by the methods that are called from said scope. Therefore, you must use do/catch.
Depending on your particular case, you might have to put do/catch around as well. If your current scope is non-throwing you will have to.
There are 3 types of do/catch.
I. Catch all
do {
methodThadDoesNotThrow()
try methodThatThrows()
}
catch {
// This will catch all errors that can be possibly thrown.
// You get implicit local-scope variable `error` that holds thrown error.
}
II. Catch concrete error type
Errors that can be thrown must implement ErrorType protocol. This is the only pre-requisite. They do not really have to be NSError or its descendants (this type is just a legacy from Objective-C). Therefore, if you know the concrete error type that you expect you can do something like:
do {
methodThadDoesNotThrow()
try methodThatThrows()
}
catch let error as CustomErrorType {
// This will catch all `CustomErrorType` errors.
// You get explicit local-scope variable `error` of `CustomErrorType` type.
}
III. Catch concrete error instance
Typically error types are declared as enums. For example:
enum CustomErrorType {
case SomethingStrange(String)
case SomethingMildlyHorrible
case Apocalypse
}
If you want to catch some specific case of CustomErrorType, you can do:
do {
methodThadDoesNotThrow()
try methodThatThrows()
}
catch CustomErrorType.SomethingStrange(let message) {
// This will catch only `CustomErrorType.SomethingStrange` errors.
// You get explicit local-scope variable `message` that
// will come from the value associated with the error enum-case.
}
There is, of course, the case of:
IV. Combine all together
do {
methodThadDoesNotThrow()
try methodThatThrows()
}
catch CustomErrorType.SomethingStrange(let message) {
// This will catch `CustomErrorType.SomethingStrange` error.
}
catch let error as CustomErrorType {
// This will catch the rest of `CustomErrorType` errors.
}
catch {
// Finally, this will catch all the rest of the errors
// (anything that is not `CustomErrorType` in this case).
}
I do not know how do I handle runtime error in swift. I need to check for a parsing error and do something about it. Can anyone help me please?
I have code something like this:
var:SomeObject = parse("some string")
I need to handle any generic error that occurs in runtime.
Thanks!
If the function is yours, then in case of a failure, you can make that function return a nil value.
That way your line of code
var anObj:SomeObject = parse("some string")
would become something like this
var:SomeObject? = parse("some string")
Notice the ? sign. It means that the value in it is Optional. In simple words, it could be some actual value, or it could be nil.
After this function, you should perform a check like
If anObj != nil
{
//do something
}
else
{
//the parse didn't go right, handle the erorr here.
}
Swift 2 adds additional safety to your error checking. You use the throws keyword to specify which functions and methods could throw an error. Then you have the do, try, and catch keywords for when you call something that could throw:
// 1
enum ParseError: ErrorType {
case InvalidValue
}
// 2
func parseWithError(value:String) throws {
if value.count > 0 {
// yeaaa!
} else {
// 3
throw ParseError.InvalidValue
}
}
func parse(value:String) {
// 4
do {
try parseWithError(value)
} catch {
print("Could not parse! :[")
return
}
}
There are a few things to highlight here:
To create an error to throw, simply create an enum that derives from ErrorType.
You need to use the throws keyword to mark any function that can throw an error.
This throws an error, which will be caught in section 4.
Instead of try blocks, which might be familiar from other languages, you wrap any code that can throw an error in a do block. Then, you add the try keyword to each function call that could throw an error.
For more read here or this is the official documentation of Error Handling