objective-cswifttypesobjective-c-swift-bridge

Cannot access property on Swift type from Objective-C


I am trying to access a Swift class's Double? property from Objective-C.

class BusinessDetailViewController: UIViewController {

    var lat : Double?
    var lon : Double?

    // Other elements...
}

In another view controller, I am trying to access lat like following:

#import "i5km-Swift.h"
@interface ViewController ()

@property (strong, nonatomic) BusinessDetailViewController *businessDetailViewController;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.businessDetailViewController = [[BusinessDetailViewController alloc] initWithNibName:@"BusinessDetailViewController" bundle:nil];
    self.businessDetailViewController.lat = businessArray[1]; /* THIS GIVES ME AN ERROR */
}

and I am getting

Property 'lat' not found on object of type 'BusinessDetailViewController *'

Why can't I access this property? What am I missing?


Solution

  • Optional values of non-Objective-C types aren't bridged into Objective-C. That is, the first three properties of TestClass below would be available in Objective-C, but the fourth wouldn't:

    class TestClass: NSObject {
        var nsNumberVar: NSNumber = 0      // obj-c type, ok
        var nsNumberOpt: NSNumber?         // optional obj-c type, ok
        var doubleVar: Double = 0          // bridged Swift-native type, ok
        var doubleOpt: Double?             // not bridged, inaccessible
    }
    

    In your Objective-C code, you'd access those first three properties like this:

    TestClass *optTest = [[TestClass alloc] init];
    optTest.nsNumberOpt = @1.0;
    optTest.nsNumberVar = @2.0;
    optTest.doubleVar = 3.0;
    

    In your case, you can either convert lat and long to be non-Optional or switch them to be instances of NSNumber.


    Note that you need to be careful about your Objective-C code if you take the second approach (switching lat and lon to non-optional properties of type NSNumber) -- while the Swift compiler will prevent you from assigning nil to non-optional properties, the Objective-C compiler has no qualms about allowing it, letting nil values sneak into your Swift code with no chance of catching them at runtime. Consider this method on TestClass:

    extension TestClass {
        func badIdea() {
            // print the string value if it exists, or 'nil' otherwise
            println(nsNumberOpt?.stringValue ?? "nil")
    
            // non-optional: must have a value, right?
            println(nsNumberVar.stringValue)
        }
    }
    

    This works fine if invoked with values in both of the properties, but if nsNumberVar is set to nil from the Objective-C code, this will crash at runtime. Note that there is no way to check whether or not nsNumberVar is nil before using it!

    TestClass *optTest = [[TestClass alloc] init];
    optTest.nsNumberOpt = @1.0;
    optTest.nsNumberVar = @2.0;
    [optTest badIdea];
    // prints 1, 2
    
    optTest.nsNumberOpt = nil;
    optTest.nsNumberVar = nil;
    [optTest badIdea];
    // prints nil, then crashes with an EXC_BAD_ACCESS exception