75

Using the latest Xcode 9 beta, I'm seemingly completely unable to access properties on Swift classes. Even odder, I can access the class itself to instantiate it or whatever, but completely unable to access properties on it.

So if I have this Swift class:

import UIKit

class TestViewController: UIViewController {
    var foobar = true
}

And I try to do this:

TestViewController *testViewController = [[TestViewController alloc] init]; // success
testViewController.foobar; // error

What exactly am I doing wrong? New project with Xcode 9.

Paulo Mattos
  • 18,845
  • 10
  • 77
  • 85
Doug Smith
  • 29,668
  • 57
  • 204
  • 388
  • Compare [How can I deal with @objc inference deprecation with #selector() in Swift 4?](https://stackoverflow.com/q/44390378/2976878) – Hamish Aug 13 '17 at 09:08

6 Answers6

184

The rules for exposing Swift code to Objective-C have changed in Swift 4. Try this instead:

@objc var foobar = true

As an optimization, @objc inference have been reduced in Swift 4. For instance, a property within an NSObject-derived class, such as your TestViewController, will no longer infer @objc by default (as it did in Swift 3).

Alternatively, you could also expose all members to Objective-C at once using @objcMembers:

@objcMembers class TestViewController: UIViewController {
    ...
}

This new design is fully detailed in the corresponding Swift Evolution proposal.

Paulo Mattos
  • 18,845
  • 10
  • 77
  • 85
  • 4
    I am still facing issue to access Int & Bool properties from Swift to objective-C ... and getting this error : **Property cannot be marked @objc because its type cannot be represented in Objective-C** , Any suggetion ? .. I have used header "MyProject-Swift.h – shaqir saiyed Feb 23 '18 at 12:10
  • 1
    **Only String Properties are accessible** – shaqir saiyed Feb 26 '18 at 11:36
  • 4
    Stupid upgrade. Now I have to add @objc pre-processor on every variable and method declaration if using it on Objective C class – Scofield Tran Apr 14 '18 at 11:10
  • @shaqirsaiyed I can access Booleans as well. – Lehlohonolo_Isaac Aug 21 '18 at 13:42
  • The alternate didn't work for me. I had to prefix @objc to the class and all it's member fields – Vijay Kumar Kanta Dec 29 '18 at 03:10
  • Very interesting. I have it working with both versions. – javi_swift Jun 03 '19 at 11:27
  • 1
    Thank you SO MUCH! This has been driving me nuts all morning, but your post clarified several points and gave the solution in the form of @objcMembers that works perfectly in my case, which is a class with a variety of String, Int, Float and CLLocationDegrees properties. – Erik van der Neut Sep 22 '19 at 03:21
15

Swift 3.2/4.0 / XCode 9.1

You you set swift3.2 in project settings ( //:configuration = Debug SWIFT_VERSION = 3.2 )

you can use your code,(using the correct import file in objc, see below). If You set project to swift 4.0 ( //:configuration = Debug SWIFT_VERSION = 4.0 )

You must prepend @objc for every property.

So:

Swift 3.2:

// MyClass.swift

@objc class MyClass: NSObject{

    var s1: String?
    @objc var s2 : String?
}


//

//  ViewController.m

import "MixingObjCAndSwift-Swift.h"
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];        
    MyClass * mc = [MyClass new];
    NSString * s1 = mc.s1;
    NSString * s2 = mc.s2;
}

works.

Swift 4.0:

// MyClass.swift

@objc class MyClass: NSObject{

    var s1: String?
    @objc var s2 : String?
}


.....  
- (void)viewDidLoad {
    [super viewDidLoad];        
    MyClass * mc = [MyClass new];
    NSString * s1 = mc.s1;
    NSString * s2 = mc.s2;
}

does NOT works: compiler fails:

/Users....ViewController.m:24:21: Property 's1' not found on object of type 'MyClass *'

as s1 is not prepended with @objc.

You must write:

@objc class MyClass: NSObject{

    @objc var s1: String?
    @objc var s2 : String?
}

(As a side-note: in C/C++/ObJC file, put always system/general *h files before your "local" class headers.)

Swift 4

just add @objcMembers before class @objcMembers class MyClassObject: NSObject { var s1: String! var s2: String!

} Swift evolutionenter link description here

ingconti
  • 10,876
  • 3
  • 61
  • 48
13

As for Swift 5.1 I found that "@objc" is not enough, but also make it public "@objc public":

@objc public class SomeClass: NSObject {
    @objc public let amount: NSNumber
...
protspace
  • 2,047
  • 1
  • 25
  • 30
  • It doesn't work for me for String? property with explicit `set` and `get` blocks. Anybody faced this as well? – Dan Mar 22 '20 at 08:00
  • @Dan I believe optional is a Struct, this means that it can't be exposured to Obj-C runtime – protspace Mar 23 '20 at 09:10
3
  1. When you add a swift file in your Objective-C project, Xcode will prompt to add Objective-C bridging header file, so allow the header file to be created.
  2. In your Objective-C implementation file where you want to access the TestViewController property foobar. Use the following import syntax and replace the ProjectName with your project.

#import "ProjectName-Swift.h"

Objective-C implementation file:

#import "ViewController.h"
#import "ProjectName-Swift.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    TestViewController *testViewController = [[TestViewController alloc] init]; // success
    BOOL prop = testViewController.foobar;
    NSLog(@"Property: %d", prop);
}

@end

For more details go through the Apple Documents

Rahul Kumar
  • 3,009
  • 2
  • 16
  • 22
2

Xcode 10.2 | Swift 4 Adding @objcMembers before class solved my problem.

@objcMembers class MyClass:NSObject {
   var s1: String!
   var s2: NSMutableArray!
}
morten.c
  • 3,414
  • 5
  • 40
  • 45
0

I also encountered with this problem in swift 3.0

class declaration was like that

class ClassA {
//statements
}

above class was not accessible in Objective C code and it was also not registered in -Swift.h

once I inherited from NSObject like this:

class ClassA : NSObject {
//statements
} 

the class was accessible in Objective c and got registered with -Swift.h

This solution is useful when you don't want to avoid inheritance from NSobject class.

Prabhat Kasera
  • 1,129
  • 11
  • 28