I'm working on a swift project that need to interact with an existing objective-c api. I've run into a bit of a roadblock with one of the functions though. In the objective-c header file (OrderItem.h) I have this function definition:
+ (NSString *_Nullable)getOptional:(NSString *_Nonnull)foo error:(NSError *_Nullable *_Nullable)error;
In particular, take notice of the last parameter; because it is an error pointer calls to this method in swift will need to be wrapped in an error hander (do .. catch).
Here is the corresponding .m file:
+ (NSString *)getOptional:(NSString *)foo error:(NSError *__autoreleasing *)error
{
    if([foo isEqualToString:@"abc"])
    {
        return @"abc item";
    }
    else
    {
        if([foo isEqualToString:@"xyz"])
        {
            *error = [[NSError alloc] init];
        }
        return nil;
    }
}
In my swift file I then added this code:
func testGetOptional()
{
    do
    {
        var result:NSString? = try OrderItem.getOptional("abc");
        XCTAssertNotNil(result);
        result = try OrderItem.getOptional("123");
        XCTAssertNil(result);
    }
    catch let error as NSError
    {
        XCTFail("OrderItem lookup should not have thrown an error. Error was " + error.localizedDescription);
    }
}
Nothing especially complicated; neither of the two calls to getOptional should actually result in an error. When I run that function however, the '123' case is blowing up and causing the test to fail. When I took a closer look, it seems that the bridged version of my objective-c is defining the return type as Nonnull (-> OrderItem) even though I explicitly defined it as Nullable in the objective-c. Even more strange, if I declare this same function without the final 'error' parameter then the bridged version will have the correct return type of Nullable (-> OrderItem?).
Can someone shed some light on what is going on here? More importantly, is there some way to work around this issue?
In Cocoa there is the error pattern that a method that can fail will have a return value and an indirect error parameter. If it is a method returning a object reference, the pattern is
set the reference the indirect error argument points to,
return nil
So it looks like this
+(NSString *) getOptional:( NSString *) foo error:(NSError *__autoreleasing *)error
{
   …
   // Error case
   *error = [NSError …];
   return nil;
   …
 }
In Swift errors are translated into a do… catch construct. Therefore a return value of nil signaling an error is never used from the Swift point of view, because the execution is caught. Therefore it is non-nullable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With