Swift: A basic step-by-step use of a Swift class from an Objective-C file (Xcode 6 GM)


Step-by-Step Instructions

1. create an Objective-C project called ObjCTest

2. create a new Swift file (doesn’t matter what you call it)

3. accept the Bridging Header when offered

4. create a simple class in the Swift file like so:
class CanYouSeeMe: NSObject {
    
    var a:String
    init (str:String){
        self.a = str
    }
}
Remembering to import Foundation at the head of the file.

5. import Swift header into Objective-C ViewController.m file, e.g. #import "ObjCTest-Swift.h" if project name is ObjCTest. (The header name is your project name with "-Swift.h" appended.)

6. add the following code to your viewDidLoad (ViewController.m):
CanYouSeeMe *cysm = [[CanYouSeeMe alloc] initWithStr:@"Hello"];
 NSLog(@"%@",cysm.a);
7. add advance declaration to Objective-C header file @class CanYouSeeMe. As explained by Apple, “To avoid cyclical references, don’t import Swift into an Objective-C header file. Instead, you can forward declare a Swift class to use it in an Objective-C header. Note that you cannot subclass a Swift class in Objective-C.”

Code files

In the end you’ll have three files. Swift file
import Foundation

class CanYouSeeMe: NSObject {
    
    var a:String
    init (str:String){
        self.a = str
    }
}
Objective-C .m file:
#import "ViewController.h"
#import "ObjCTest-Swift.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    CanYouSeeMe *cysm = [[CanYouSeeMe alloc] initWithStr:@"Hello"];
    NSLog(@"%@",cysm.a);

}

@end
Objective-C .h file:
#import <UIKit/UIKit.h>

@class CanYouSeeMe;
@interface ViewController : UIViewController
@end

Using the @objc attribute

The above class is visible to the Objective-C class because it subclasses NSObject. If it didn’t subclass NSObject then it would need the @objc modifier to be attached. For example we might have a Swift file like this:
import Foundation

@objc class CanYouSeeMe {
    
    class var hi:String {return "Hi there"}
}
and an Objective-C file like this:
#import "ViewController.h"
#import "ObjCTest-Swift.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"%@",CanYouSeeMe.hi);
    
}

@end

Further use of the @objc attribute

As well as exposing code to Objective-C files, the @objc attribute can also change the way code looks to Objective-C to make it more characteristic of the language. We've already seen in the code above how initWith automatically prefixes init calls with arguments. But we can manually change method names as well. For example here is a Swift file:
import Foundation

class CanYouSeeMe:NSObject {
    
    @objc(sayHelloBackWithBackUsingMyName:) func sayHello(name:String)->String {
        return "Hello \(name)"
    }
}
and here is the accompanying Objective-C .m file:
#import "ViewController.h"
#import "ObjCTest-Swift.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    CanYouSeeMe *cysm = [[CanYouSeeMe alloc] init];
    NSLog(@"%@", [cysm sayHelloBackWithBackUsingMyName:@"Bert"]);
}

@end
When the sayHello method is called from Swift it would be done so with just sayHello("Bert") but from Objective-C we use the much more descriptive method name.

Note: the @objc attribute can also be used to allow protocols to define optional methods and properties. See this post for details.

Conclusion

This post has covered the absolute basics of accessing Swift classes from Objective-C files. And this should be read more as a post on what can be done rather than what should be done. There are a whole list of restrictions that Apple supplies on what cannot be done when accessing Swift from Objective-C and unless you have to do this I would highly recommend going the other way and importing and accessing Objective-C from Swift or migrating entirely to Swift.

Accessing Swift from Objective-C should not be a long-term model for your code because it closes so many opportunities for working with Swift types.

Comments