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
Here is some code, that converts NSExceptions to Swift 2 errors.
Now you can use
ObjC.h:
ObjC.m
Don't forget to add this to your "*-Bridging-Header.h":
As specified in the documentation, this is an easy and lightweight way to do it:
As noted in comments, that this API throws exceptions for otherwise-recoverable failure conditions is a bug. File it, and request an NSError-based alternative. Mostly the current state of affairs is an anachronism, as
NSTask
dates to back before Apple standardized on having exceptions be for programmer errors only.In the meantime, while you could use one of the mechanisms from other answers to catch exceptions in ObjC and pass them to Swift, be aware that doing so isn't very safe. The stack-unwinding mechanism behind ObjC (and C++) exceptions is fragile and fundamentally incompatible with ARC. This is part of why Apple uses exceptions only for programmer errors — the idea being that you can (theoretically, at least) sort out all the exception cases in your app during development, and have no exceptions occurring in your production code. (Swift errors or
NSError
s, on the other hand, can indicate recoverable situational or user errors.)The safer solution is to foresee the likely conditions that could cause an API to throw exceptions and handle them before calling the API. If you're indexing into an
NSArray
, check itscount
first. If you're setting thelaunchPath
on anNSTask
to something that might not exist or might not be executable, useNSFileManager
to check that before you launch the task.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:
And with a little bridging help, you'll just have to call:
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.
TL;DR: Use Carthage to include https://github.com/eggheadgames/SwiftTryCatch or CocoaPods to include https://github.com/ravero/SwiftTryCatch.
Then you can use code like this without fear it will crash your app:
The accepted answer by @BPCorp works as intended, but as we discovered, things get a little interesting if you try to incorporate this Objective C code into a majority Swift framework and then run tests. We had problems with the class function not being found (Error: Use of unresolved identifier). So, for that reason, and just general ease of use, we packaged it up as a Carthage library for general use.
Strangely, we could use the Swift + ObjC framework elsewhere with no problems, it was just the unit tests for the framework that were struggling.
PRs requested! (It would be nice to have it a combo CocoaPod & Carthage build, as well as have some tests).