Поймать NSException в Swift

Следующий код в Swift вызывает исключение NSInvalidArgumentException:

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

Как я могу поймать исключение? Насколько я понимаю, try / catch в Swift – это ошибки, возникающие в Swift, а не для NSExceptions, созданных из таких объектов, как NSTask (что, я думаю, написано в ObjC). Я новичок в Swift, может быть, мне не хватает чего-то очевидного …

Изменить : вот радар для ошибки (специально для NSTask): openradar.appspot.com/22837476

    Вот код, который преобразует NSExceptions в Swift 2 ошибки.

    Теперь вы можете использовать

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

    ObjC.h:

     #import  @interface ObjC : NSObject + (BOOL)catchException:(void(^)())tryBlock error:(__autoreleasing NSError **)error; @end 

    ObjC.m

     #import "ObjC.h" @implementation ObjC + (BOOL)catchException:(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 

    Не забудьте добавить это в свой «* -Bridging-Header.h»:

     #import "ObjC.h" 

    Я предлагаю сделать функцию C, которая поймает исключение и вернет NSError. И затем, используйте эту функцию.

    Функция может выглядеть так:

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

    И с небольшой помощью, вам просто нужно позвонить:

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

    Примечание. Я действительно не тестировал это, я не мог найти быстрый и простой способ иметь Objective-C и Swift на игровой площадке.

    TL; DR: используйте Carthage, чтобы включить https://github.com/eggheadgames/SwiftTryCatch или CocoaPods, чтобы включить https://github.com/ravero/SwiftTryCatch .

    Тогда вы можете использовать такой код, не опасаясь, что он разрушит ваше приложение:

     import Foundation import SwiftTryCatch class SafeArchiver { class func unarchiveObjectWithFile(filename: String) -> AnyObject? { var data : AnyObject? = nil if NSFileManager.defaultManager().fileExistsAtPath(filename) { SwiftTryCatch.tryBlock({ data = NSKeyedUnarchiver.unarchiveObjectWithFile(filename) }, catchBlock: { (error) in Logger.logException("SafeArchiver.unarchiveObjectWithFile") }, finallyBlock: { }) } return data } class func archiveRootObject(data: AnyObject, toFile : String) -> Bool { var result: Bool = false SwiftTryCatch.tryBlock({ result = NSKeyedArchiver.archiveRootObject(data, toFile: toFile) }, catchBlock: { (error) in Logger.logException("SafeArchiver.archiveRootObject") }, finallyBlock: { }) return result } } 

    Принятый ответ от @BPCorp работает по назначению, но, как мы выяснили, все становится немного интересным, если вы попытаетесь включить этот код Objective C в структуру Swift большинства и затем запустить тесты. У нас были проблемы с отсутствием функции classа ( Ошибка: использование неразрешенного идентификатора ). Поэтому, по этой причине, и просто общая простота использования, мы упаковали его как библиотеку Carthage для общего использования.

    Как ни странно, мы могли бы использовать структуру Swift + ObjC в других местах без каких-либо проблем, это были только модульные тесты для платформы, которые боролись.

    PR запросили! (Было бы неплохо иметь его комбо CocoaPod & Carthage build, а также иметь некоторые тесты).

    Как отмечается в комментариях, этот API генерирует исключения для ситуаций, которые могут быть восстановлены в противном случае, является ошибкой. Загрузите его и запросите альтернативу, основанную на NSError. В основном текущее положение дел является анахронизмом, так как NSTask восходит к тому, что Apple стандартизовала исключения из ошибок только для программистов.

    Тем временем, хотя вы можете использовать один из механизмов из других ответов для исключения исключений в ObjC и передать их Swift, имейте в виду, что это не очень безопасно. Механизм разворачивания стека за исключениями ObjC (и C ++) является хрупким и принципиально несовместимым с ARC. Это часть того, почему Apple использует исключения только для ошибок программиста – идея состоит в том, что вы можете (теоретически, по крайней мере) разобрать все случаи исключения в своем приложении во время разработки и не иметь никаких исключений, возникающих в вашем производственном коде. ( NSError ошибки или NSError s, с другой стороны, могут указывать восстановимые ситуационные или пользовательские ошибки.)

    Более безопасное решение заключается в том, чтобы предвидеть вероятные условия, которые могли бы заставить API генерировать исключения и обрабатывать их перед вызовом API. Если вы индексируете NSArray , NSArray проверьте его count . Если вы устанавливаете launchPath на NSTask на что-то, что может не существовать или может не выполняться, используйте NSFileManager чтобы проверить, что перед запуском задачи.

    Давайте будем гением компьютера.