Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Catching NSException in Swift

The following code in Swift raises NSInvalidArgumentException exception:

task = NSTask() task.launchPath = "/SomeWrongPath" task.launch() 

How can I catch the exception? As I understand, try/catch in Swift is for errors thrown within Swift, not for NSExceptions raised from objects like NSTask (which I guess is written in ObjC). I'm new to Swift so may be I'm missing something obvious...

Edit: here's a radar for the bug (specifically for NSTask): openradar.appspot.com/22837476

like image 325
silyevsk Avatar asked Sep 24 '15 10:09

silyevsk


People also ask

What is NSException in Swift?

An object that represents a special condition that interrupts the normal flow of program execution.

Do catch in IOS Swift?

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's matched against the catch clauses to determine which one of them can handle the error.

How do you handle try catch in Swift?

The try/catch syntax was added in Swift 2.0 to make exception handling clearer and safer. It's made up of three parts: do starts a block of code that might fail, catch is where execution gets transferred if any errors occur, and any function calls that might fail need to be called using try .

Do catch vs try catch Swift?

do – This keyword starts the block of code that contains the method that can potentially throw an error. try – You must use this keyword in front of the method that throws. Think of it like this: “You're trying to execute the method.


2 Answers

Here is some code, that converts NSExceptions to Swift 2 errors.

Now you can use

do {     try ObjC.catchException {         /* calls that might throw an NSException */     } } catch {     print("An error ocurred: \(error)") } 

ObjC.h:

#import <Foundation/Foundation.h>  @interface ObjC : NSObject  + (BOOL)catchException:(void(^)(void))tryBlock error:(__autoreleasing NSError **)error;  @end 

ObjC.m

#import "ObjC.h"  @implementation ObjC   + (BOOL)catchException:(void(^)(void))tryBlock error:(__autoreleasing NSError **)error {     @try {         tryBlock();         return YES;     }     @catch (NSException *exception) {         *error = [[NSError alloc] initWithDomain:exception.name code:0 userInfo:exception.userInfo];         return NO;     } }  @end 

Don't forget to add this to your "*-Bridging-Header.h":

#import "ObjC.h" 
like image 173
freytag Avatar answered Sep 25 '22 13:09

freytag


What I suggest is to make an C function that will catch the exception and return a NSError instead. And then, use this function.

The function could look like this:

NSError *tryCatch(void(^tryBlock)(), NSError *(^convertNSException)(NSException *)) {     NSError *error = nil;     @try {         tryBlock();     }     @catch (NSException *exception) {         error = convertNSException(exception);     }     @finally {         return error;     } } 

And with a little bridging help, you'll just have to call:

if let error = tryCatch(task.launch, myConvertFunction) {     print("An exception happened!", error.localizedDescription)     // Do stuff } // Continue task 

Note: I didn't really test it, I couldn't find a quick and easy way to have Objective-C and Swift in a Playground.

like image 20
BPCorp Avatar answered Sep 26 '22 13:09

BPCorp