Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSKeyedUnarchiver error handling - prevent crash in Swift

Since Swift currently doesn't have try-catch, how am I supposed to prevent crashes with bad data in this line of code?

var myObject = NSKeyedUnarchiver.unarchiveObjectWithData(data) as MyClass

UPDATE

I created a very simple case in a playground for demonstration. Assume we don't know what's in data, how can I catch the SIGABRT on the second line? Is there no way to check to make sure it is possible to unarchive an NSData object before calling unarchiveObjectWithData?

var data = "foo bar".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!
if let unarc = NSKeyedUnarchiver.unarchiveObjectWithData(data) { // Execution was interrupted: signal SIGABRT
}
like image 209
ccwasden Avatar asked Sep 16 '14 03:09

ccwasden


1 Answers

I think your best bet for now, until Apple updates the implementation of NSKeyedUnarchiver to not use exceptions or adds exception support to Swift, you are going to have to use an Objective-C wrapper to try-catch.

You can see an example of a wrapper here: https://medium.com/swift-programming/adding-try-catch-to-swift-71ab27bcb5b8

Essentially, you can introduce a single Objective-C function or class that will allow you to use a try-catch block from Swift. I like implementing the above example as an initializer to make it cleaner in Swift:

// In Objective-C
// ----------------------

@interface try: NSObject

- (id)initWithTry:(void(^)())try catch:(void(^)(NSException *exception))catch finally:(void(^)())finally;

@end

@implementation try

- (id)initWithTry:(void(^)())try catch:(void(^)(NSException *exception))catch finally:(void(^)())finally
{
    self = [super init];
    if (self) {
        @try {
            try ? try() : nil;
        }
        @catch (NSException *exception) {
            catch ? catch(exception) : nil;
        }
        @finally {
            finally ? finally() : nil;
        }
    }
    return self;
}

@end

// From Swift (make sure you import the objc header in your bridging header
// ----------------------

var data = "foo bar".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!
try(
    try: { () -> Void in
        if let unarc: AnyObject = NSKeyedUnarchiver.unarchiveObjectWithData(data) { // Execution was interrupted: signal SIGABRT
            println(unarc)
        }
    },
    catch: { exception in
        println("Failed to parse data: \(exception)")
    },
    finally: nil
)
like image 120
drewag Avatar answered Sep 19 '22 05:09

drewag