How to map my Objective-C errors like Swift 2 does for NSURLError, NSCocoaError, AVError, etc. as described in WWDC 2015 video 401?

| | August 4, 2015

@13:20 into the WWDC video “Swift handling of Cocoa errors” (https://developer.apple.com/videos/wwdc/2015/?id=401) Apple shows how they have exposed their errors from Cocoa (at least some classes) as something like NSURLError.someError. These errors can then be used nicely with Swift 2 catch syntax.

This part of the video shows that this (for errors from certain Cocoa classes) is now possible:

func preflight() -> Bool {
  do {
    try url.checkResourceIsReachable()
    resetState()
    return true
  } catch NSURLError.FileDoesNotExist {
    return true // still okay
  } catch {
    return false
  }
}

Note the fact that Cocoa error NSURLErrorFileDoesNotExist is somehow
mapped or usable in Swift as NSURLError.FileDoesNotExist (in the catch
expression)

I’d like to have my SDK errors be able to be used in the same way.

But when I try to return an NSError from my Objective-C code, and then catch that error on the Switch 2 side, the catch block for the particular error is not invoked and the only catch block that is the ending catch-all one.

Here’s how my errors are defined in Objective-C TestClass.h:

typedef NS_ENUM(NSInteger, TestClassError) {
    TestClassErrorOne,
    TestClassErrorTwo,
    TestClassErrorThree,
    TestClassErrorFour,
    TestClassErrorFive,
};

Here’s how I return one of these errors in my Objective-C TestClass.m:

- (BOOL)method2AndReturnError:(NSError **)err {
    // ...

    if (err) {
        *err = [NSError errorWithDomain:@"TestClassError"
                                  code:TestClassErrorTwo
                              userInfo:nil];
        NSLog(@"set error: %@", *err);
    }
    return NO;
}

The error logged here as set in the NSError is:

[...] set error: Error Domain=TestClassError Code=1 
    "The operation couldn’t be completed. (TestClassError error 1.)"

Here’s how I try to catch the error in Swift 2:

    // why are the TestClassError.xxx catch blocks not catching?
    do {
        try test.method2()
    } catch TestClassError.One {
        print("error one!")
    } catch TestClassError.Two {
        print("error two!")
    } catch TestClassError.Three {
        print("error three!")
    } catch TestClassError.Four {
        print("error four!")
    } catch TestClassError.Five {
        print("error five!")
    } catch let error as NSError {
        print("error not caught!")
        print(error)
    }

And here’s how the error gets logged at the end of this (from the Swift code):

Error Domain=TestClassError Code=1 "The operation couldn’t be completed. 
    (TestClassError error 1.)"

Offhandedly I think everything looks good but the catch blocks are not “catching” the errors in the form TestClassError.Two

UPDATE 1 (2015-08-04):

An Apple developer evangelist tells me:

Right now we don’t have a recommended way of how 3rd party libraries
can do this, but I know the engineering team is interested in some
sample cases. Can you file a bug report with a few more details on how
exactly you’d want this to work?

Leave a Reply