Swift 2.0 interop with Objective C not working as expected?

I'm porting a Swift 1.2 framework to 2.0. After fixing the 3 million compiler errors courtesy of the new Swift 2.0 error handling scheme, I was finally able to link my test app (written in Objective C) to use the updated framework.


Xcode version 7.0 beta 3 (7A121l)


However, I ran into a problem. Some Swift functions are no longer generated into the automatically generated Objective C header (MyFramwork-Swift.h) used by the Objective C test app.


Here is an example of a function that is not exposed: (My actual framework function returned an enum, but I tried to simplify to illustrate the problem more clearly).


    public func calculateImportantValueDangerously() throws -> Int
    {
        return 42
    }


Note that other functions like the following actually do get exposed as expected (and can be called):


    public func doSomething()
    {
    }

    public func doSomethingDangerous() throws
    {
    }

    public func calculateMeaninglessValue() -> Int
    {
        return -1
    }


Here's the Objective C side:


    MyClass *newInstance = [[MyClass alloc] init];
    [newInstance doSomething];

    NSError *error = nil;
    [newInstance doSomethingDangerousAndReturnError:&error];

    long meaninglessValue = [newInstance calculateMeaninglessValue];
    NSLog(@"%ld", meaninglessValue);

    long importantValue = [newInstance calculateImportantValueDangerouslyAndReturnError:&error]; <-COMPILE ERROR; SELECTOR NOT AVAILABLE
    NSLog(@"%ld", importantValue);


From watching this video, I had expected that it should "just work":


https://developer.apple.com/videos/wwdc/2015/?id=401


...but it seems like we can't currently use functions that both throw and return a value.


Is this a bug, or not implemented feature? My apologies if I missed something in the release notes somewhere.


Any advice appreciated.

I believe you have to put @objc before the Swift function name so it will be found in Objective-C.


I don't know about using both throws and returns, I apologize.

   long importantValue = [newInstance calculateImportantValueDangerouslyAndReturnError];

This line is missing the error parameter. It should be:

  NSError *error;
   long importantValue = [newInstance calculateImportantValueDangerouslyAndReturnError:&error];

I fixed my question to include the error parameter. But this does not resolve the issue. Seems there is a limitation on returning primitives and throwing functions. http://stackoverflow.com/questions/31416725/swift-2-0-interop-with-objective-c-not-working-as-expected

I added @objc. Although this is not needed to expose public functions, it does produce a more direct error message stating the problem: "Throwing method cannot be marked @objc because it returns a value of type 'Int'; return 'Void' or a type that bridges to an Objective-C class". Uggh

Swift tries to add success/failure informaiton to the return type for the bridged Objective-C code.


Void -> BOOL (returns NO on error)

Reference type (Objective-C class) non-optional -> nullable Objective-C class (returns nil on error)


So, `throws` methods returning primitive types or optional types cannot be bridged to Objective-C.

Thanks for the explanation. This seems to be a bad design and very non-obvious. And definitely doesn't match the "it just works" story portrayed in the referenced WWDC presentation. I guess I should just file a bug.

The problem here is that Cocoa conventions require the caller to first test the function result and then look at the returned NSError. There's an obvious interpretation of the first step for

BOOL
(
NO
indicates an error) and pointers (
nil
indicates an error) but not for integer types. In some contexts 0 indicates an error, in some it's -1, and in some it's
NSNotFound
.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
Swift 2.0 interop with Objective C not working as expected?
 
 
Q