Swift interoperability
There are two main points that Apple thought of when introducing Swift:
- The usage of the Cocoa framework and established Cocoa patterns
- Easy to adopt and migrate
Apple understood that and took it very seriously while working on Swift. They made Swift work seamlessly with Objective-C and Cocoa. You can use all Objective-C code in Swift, and you can even use Swift in Objective-C.
It's very crucial to be able to use the Cocoa framework. All of the code that is written in Objective-C is available for use in Swift, both Apple frameworks and third-party libraries as well.
Using Objective-C in Swift
All the Cocoa frameworks written in Objective-C are available in Swift by default. You just need to import them and then use them. Swift doesn't have header files; instead, you need to use a module name. You can also include your own Swift frameworks in the same way:
import Foundation import UIKit import Alamofire // Custom framework
Setup
To include your own Objective-C source files, you need to do a small setup first. The process is a bit different for the application target and framework target. The main idea is the same—to import the Objective-C header files.
The application target
For the application target, you need to create a bridging header. A bridging header is a plain Objective-C header file in which you specify the Objective-C import
statements.
Xcode will show a popup, offering to create, and set up a bridging header for you when you add the Objective-C file to a Swift project, or vice versa for the first time. This is the best and the most convenient way to add it.
If you decline the Xcode help, you can create a bridging header yourself anytime. To do that, you need to follow these steps:
- Add a new header file to the project.
- Go to Target | Build Settings.
- Search for
Objective-C Bridging Header
and specify the path to the bridging header file created in step 1.
Once you set up bridging header, the next step is to add import
statements to it:
Bridging.h
// // Use this file to import your target's public headers that you // would like to expose to Swift. #import "MyClass.h"
The framework target
For the framework target, you simply need to import the .h
Objective-C header files to the framework's umbrella header. The Objective-C header files must be marked as public. The umbrella header is the header in which you specify your publicly available API. Usually, it looks like this—the ExampleFramework.h
umbrella header:
#import <UIKit/UIKit.h>
//! Project version number for MySwiftKit.
FOUNDATION_EXPORT double MySwiftKitVersionNumber;
//! Project version string for MySwiftKit.
FOUNDATION_EXPORT const unsigned char MySwiftKitVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <MySwiftKit/PublicHeader.h>
#import <SimpleFramework/MyClass.h>
Calling Objective-C code
Once you are done with the setup, you can use all Objective-C APIs in Swift. You can create instances, call methods, inherit from Objective-C classes, conform to protocols, and do other things that you can do in Objective-C. In this example, we will use the Foundation
classes, but the rules are the same for third-party code as well:
import UIKit import Foundation let date = NSDate() date.timeIntervalSinceNow UIColor.blackColor() UIColor(red: 0.5, green: 1, blue: 1, alpha: 1) class MyView: UIView { //custom implementation }
Tip
Inherit from Objective-C classes only if you need it. This can have a negative impact on performance.
There is free bridging between Swift types and Objective-C Foundation types. Automatic bridging happens on assignment and when you pass it as an argument to a function:
let array = [1, 2, 3] func takeArray(array: NSArray) { } var objcArray: NSArray = array takeArray(array)
Converting from Objective-C to a Swift type requires explicit type casting. There are two types of casting: downcasting and upcasting. Casting is usually an unsafe operation, which could fail, and that's why it returns an optional type:
//Upcasting or safe casting let otherArray: [AnyObject] = objcArray as [AnyObject] //Downcasting, unsafe casting if let safeNums = objcArray as? [Int] { safeNums[0] + 10 //11 } let string: NSString = "Hi" let str: String = string as String
The String
type has gone one step even further. You can invoke the Objective-C foundation methods on the Swift String
type without any type casting:
var name: String = "Name" name.stringByAppendingString(": Sara")
Swift made a small improvement to Objective-C code so that it looks more Swift-style. The biggest change is made to instance creation and the style of the initialization code. The init
, the initWith
, and other factory methods are transformed into Swift initializers:
//Objective-C - (instancetype)initWithFrame:(CGRect)frame; + (UIColor *)colorWithWhite:(CGFloat)white alpha:(CGFloat)alpha; // Swift init(frame: CGRect) init(white: CGFloat, alpha: CGFloat)
The other change is made to NS_ENUM
and NS_OPTIONS
. They become native Swift types: enum
and RawOptionSetType
.
As you can see, the API looks a bit different. Because Swift strives for cleanliness, it removes word duplications from the API nomenclature. The other method calls, properties, and names, are the same as they were in Objective-C, so it should be easy to find and understand them.
What is happening behind the scenes is that Swift is generating special interface files to interact with Objective-C. You can see these Swift interface files by holding down the command key and clicking on the type, NSDate
and UIColor
in our example.
Using Swift in Objective-C
It is also possible to use Swift in Objective-C. It makes Swift very easy to adapt to an existing project. You can start by adding one Swift file, and move more functionality to Swift over time.
The setup process is much easier than that for including Objective-C in Swift. All you need to do is import Swift's autogenerated header to Objective-C. The naming convention of the files for application targets is ProductModuleName + -Swift.h
, and for frameworks, it is <ProductName/ProductModuleName + -Swift.h>
.
Take a look at the following examples:
#import "SwiftApp-Swift.h" #import <MySwiftKit/MySwiftKit-Swift.h>
You can inspect the content of that autogenerated file by holding down the command key and clicking on it. By default, Swift classes aren't exposed for use in Objective-C. There are two ways of making Swift classes available in Objective-C:
- Mark the Swift class, protocol, or enumeration with the
@objc
attribute.You can mark classes, methods, protocols, and enumerations with the
@objc
attribute. The@objc
attribute also accepts the alternative name that is used for Objective-C. When you expose a Swift class by marking it with the@objc
attribute, it has to inherit from the Objective-C class, and the enumeration must have a rawInt
value:@objc(KOKPerson) class Person: NSObject { @objc(isMan) func man() -> Bool { ... } } @objc enum Options: Int { case One case Two }
Now, the
KOKPerson
class with theisMan
method is available for use in Objective-C. - Inherit from an Objective-C class,
NSObject
for example:When you inherit from an Objective-C class, your Swift class automatically becomes available in Objective-C. You don't need to perform any extra steps in such cases. You can also mark it with the
@objc
attribute and provide an alternative name:class Person: NSObject { }
Features of Swift that are not available in Objective-C
There are some features of Swift that are not available in Objective-C, so if you plan to use Swift code from Objective-C, you should avoid using them. Here is the complete list of these features:
- Structures
- Generics
- Tuples
- Enumerations
- Type aliases
- Top-level functions
- Curried functions
- Global variables
- Swift-style variadic parameters
- Nested types