Interacting with Objective-C APIs
Interoperability is the ability to interface between Swift and Objective-C in either direction, letting you access and use pieces of code written in one language in a file of the other language. As you begin to integrate Swift into your app development workflow, it’s a good idea to understand how you can leverage interoperability to redefine, improve, and enhance the way you write Cocoa apps.
One important aspect of interoperability is that it lets you work with Objective-C APIs when writing Swift code. After you import an Objective-C framework, you can instantiate classes from it and interact with them using native Swift syntax.
Initialization
To instantiate an Objective-C class in Swift, you call one of its initializers using Swift initializer syntax.
Objective-C initializers begin with init, or initWith: if the initializer takes one or more arguments. When an Objective-C initializer is imported by Swift, the init prefix becomes an init keyword to indicate that the method is a Swift initializer. If the initializer takes an argument, the With is removed and the rest of the selector is divided up into named parameters accordingly.
Consider the following Objective-C initializer declarations:
- (instancetype)init;- (instancetype)initWithFrame:(CGRect)framestyle:(UITableViewStyle)style;
Here are the equivalent Swift initializer declarations:
init() { /* ... */ }init(frame: CGRect, style: UITableViewStyle) { /* ... */ }
The differences between Objective-C and Swift syntax are all the more apparent when instantiating objects.
In Objective-C, you do this:
UITableView *myTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
In Swift, you do this:
let myTableView: UITableView = UITableView(frame: CGRectZero, style: .Grouped)
Notice that you don’t need to call alloc; Swift handles this for you. Notice also that “init” doesn’t appear anywhere when calling the Swift-style initializer.
You can provide an explicit type when assigning to a constant or variable, or you can omit the type and have Swift infer the type automatically from the initializer.
let myTextField = UITextField(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 40.0))
These UITableView and UITextField objects are the same objects that you’d instantiate in Objective-C. You can use them in the same way you would in Objective-C, accessing any properties and calling any methods defined on their respective types.
Class Factory Methods and Convenience Initializers
For consistency and simplicity, Objective-C class factory methods are imported as convenience initializers in Swift. This allows them to be used with the same syntax as initializers.
For example, whereas in Objective-C you would call this factory method like this:
UIColor *color = [UIColor colorWithRed:0.5 green:0.0 blue:0.5 alpha:1.0];
In Swift, you call it like this:
let color = UIColor(red: 0.5, green: 0.0, blue: 0.5, alpha: 1.0)
Failable Initialization
In Objective-C, initializers directly return the object they initialize. To inform the caller when initialization has failed, an Objective-C initializer can return nil. In Swift, this pattern is built into a language feature called failable initialization.
Many Objective-C initializers in system frameworks have been audited to indicate whether initialization can fail. You can indicate whether initializers in your own Objective-C classes can fail using nullability annotations, as described in Nullability and Optionals. Objective-C initializers that indicate whether they’re failable are imported as either init(...)—if initialization cannot fail—or init?(...)—if initialization can fail. Otherwise, Objective-C initializers are imported as init!(...).
For example, the UIImage(contentsOfFile:) initializer can fail to initialize a UIImage object if an image file doesn’t exist at the provided path. You can use optional binding to unwrap the result of a failable initializer if initialization is successful.
if let image = UIImage(contentsOfFile: "MyImage.png") {// loaded the image successfully} else {// could not load the image}
Accessing Properties
Objective-C property declarations using the @property syntax are imported as Swift properties in the following way:
Properties with the nullability property attributes (
nonnull,nullable, andnull_resettable) are imported as Swift properties with optional or non-optional type as described in Nullability and Optionals.Properties with the
readonlyproperty attribute are imported as Swift computed properties with a getter ({ get }).Properties with the
weakproperty attribute are imported as Swift properties marked with theweakkeyword (weak var).Properties with an ownership property attribute other than
weak(that is,assign,copy,strong, orunsafe_unretained) are imported as Swift properties with the appropriate storage.Atomicity property attributes (
atomicandnonatomic) are not reflected in the corresponding Swift property declaration, but the atomicity guarantees of the Objective-C implementation still hold when the imported property is accessed from Swift.Accessor property attributes (
getter=andsetter=) are ignored by Swift.
You access properties on Objective-C objects in Swift using dot syntax, using the name of the property without parentheses.
For example, you can set the textColor and text properties of a UITextField with the following code:
myTextField.textColor = UIColor.darkGrayColor()myTextField.text = "Hello world"
Objective-C methods that return a value and take no arguments can be called like an Objective-C property using dot syntax. However, these are imported by Swift as instance methods, as only Objective-C @property declarations are imported by Swift as properties. Methods are imported and called as described in Working with Methods.
Working with Methods
You can call Objective-C methods from Swift using dot syntax.
When Objective-C methods are imported into Swift, the first part of the Objective-C selector becomes the base method name and appears before the parentheses. The first argument appears immediately inside the parentheses, without a name. The rest of the selector pieces correspond to argument names and appear inside the parentheses. All selector pieces are required at the call site.
For example, whereas in Objective-C you would do this:
[myTableView insertSubview:mySubview atIndex:2];
In Swift, you do this:
myTableView.insertSubview(mySubview, atIndex: 2)
If you’re calling a method with no arguments, you must still include parentheses.
myTableView.layoutIfNeeded()
id Compatibility
Swift includes an AnyObject type that represents some kind of object. This is similar to Objective-C’s id type. Swift imports id as AnyObject, which allows you to write type-safe Swift code while maintaining the flexibility of an untyped object.
For example, as with id, you can assign an object of any class type to a constant or variable of AnyObject type. You can also reassign a variable to an object of a different type.
var myObject: AnyObject = UITableViewCell()myObject = NSDate()
You can call any Objective-C method and access any property on an AnyObject value without casting to a more specific class type. This includes Objective-C compatible methods and properties marked with the @objc attribute.
let futureDate = myObject.dateByAddingTimeInterval(10)let timeSinceNow = myObject.timeIntervalSinceNow
Unrecognized Selectors and Optional Chaining
Because the specific type of an AnyObject value is not known until runtime, it is possible to inadvertently write unsafe code. In Swift as well as Objective-C, attempting to call a method that does not exist triggers an unrecognized selector error.
For example, the following code compiles without a compiler warning, but triggers an error at runtime:
myObject.characterAtIndex(5)// crash, myObject doesn't respond to that method
Swift uses optionals to guard against such unsafe behavior. When you call a method on a value of AnyObject type, that method call behaves like an implicitly unwrapped optional. You can use the same optional chaining syntax you would use for optional methods in protocols to optionally invoke a method on AnyObject.
For example, in the code listing below, the first and second lines are not executed because the count property and the characterAtIndex: method do not exist on an NSDate object. The myCount constant is inferred to be an optional Int, and is set to nil. You can also use an if–let statement to conditionally unwrap the result of a method that the object may not respond to, as shown on line three.
// myObject has AnyObject type and NSDate valuelet myCount = myObject.count// myCount has Int? type and nil valuelet myChar = myObject.characterAtIndex?(5)// myChar has unichar? type and nil valueif let fifthCharacter = myObject.characterAtIndex?(5) {print("Found \(fifthCharacter) at index 5")}// conditional branch not executed
Downcasting AnyObject
When working with objects of type AnyObject where the underlying type is known or could be reasonably determined, it is often useful to downcast those objects to a more specific type. However, because the AnyObject type can refer to any type of object, a downcast to a more specific type is not guaranteed to succeed.
You can use the conditional type cast operator (as?), which returns an optional value of the type you are trying to downcast to:
let userDefaults = NSUserDefaults.standardUserDefaults()let lastRefreshDate: AnyObject? = userDefaults.objectForKey("LastRefreshDate")if let date = lastRefreshDate as? NSDate {print("\(date.timeIntervalSinceReferenceDate)")}
If you are certain of the type of the object, you can use the forced downcast operator (as!) instead.
let myDate = lastRefreshDate as! NSDatelet timeInterval = myDate.timeIntervalSinceReferenceDate
However, if a forced downcast fails, a runtime error is triggered:
let myDate = lastRefreshDate as! NSString // Error
Nullability and Optionals
In Objective-C, you work with references to objects using raw pointers that could be NULL (referred to as nil in Objective-C). In Swift, all values—including structures and object references—are guaranteed to be non–null. Instead, you represent a value that could be missing by wrapping the type of the value in an optional type. When you need to indicate that a value is missing, you use the value nil. For more information about optionals, see Optionals in The Swift Programming Language (Swift 2.2).
Objective-C can use nullability annotations to designate whether a parameter type, property type, or return type, can have a NULL or nil value. Individual type declarations can be audited using the _Nullable and _Nonnull annotations, individual property declarations can be audited using the nullable, nonnull and null_resettable property attributes, or entire regions can be audited for nullability using the NS_ASSUME_NONNULL_BEGIN and NS_ASSUME_NONNULL_END macros. If no nullability information is provided for a type, Swift cannot distinguish between optional and non-optional references, and imports it as an implicitly unwrapped optional.
Types declared to be nonnullable, either with a
_Nonnullannotation or in an audited region, are imported by Swift as a non-optional.Types declared to be nullable with a
_Nullableannotation, are imported by Swift as an optional.Types declared without a nullability annotation are imported by Swift as an implicitly unwrapped optional.
For example, consider the following Objective-C declarations:
@property (nullable) id nullableProperty;@property (nonnull) id nonNullProperty;@property id unannotatedProperty;NS_ASSUME_NONNULL_BEGIN- (id)returnsNonNullValue;- (void)takesNonNullParameter:(id)value;NS_ASSUME_NONNULL_END- (nullable id)returnsNullableValue;- (void)takesNullableParameter:(nullable id)value;- (id)returnsUnannotatedValue;- (void)takesUnannotatedParameter:(id)value;
Here’s how they’re imported by Swift:
var nullableProperty: AnyObject?var nonNullProperty: AnyObjectvar unannotatedProperty: AnyObject!func returnsNonNullValue() -> AnyObjectfunc takesNonNullParameter(value: AnyObject)func returnsNullableValue() -> AnyObject?func takesNullableParameter(value: AnyObject?)func returnsUnannotatedValue() -> AnyObject!func takesUnannotatedParameter(value: AnyObject!)
Most of the Objective-C system frameworks, including Foundation, already provide nullability annotations, allowing you to work with values in an idiomatic and type-safe manner.
Lightweight Generics
Objective-C declarations of NSArray, NSSet and NSDictionary types using lightweight generic parameterization are imported by Swift with information about the type of their contents preserved.
For example, consider the following Objective-C property declarations:
@property NSArray<NSDate *> *dates;@property NSSet<NSString *> *words;@property NSDictionary<NSURL *, NSData *> *cachedData;
Here’s how Swift imports them:
var dates: [NSDate]var words: Set<String>var cachedData: [NSURL: NSData]
Extensions
A Swift extension is similar to an Objective-C category. Extensions expand the behavior of existing classes, structures, and enumerations, including those defined in Objective-C. You can define an extension on a type from either a system framework or one of your own custom types. Simply import the appropriate module, and refer to the class, structure, or enumeration by the same name that you would use in Objective-C.
For example, you can extend the UIBezierPath class to create a simple Bézier path with an equilateral triangle, based on a provided side length and starting point.
extension UIBezierPath {convenience init(triangleSideLength: CGFloat, origin: CGPoint) {self.init()let squareRoot = CGFloat(sqrt(3.0))let altitude = (squareRoot * triangleSideLength) / 2moveToPoint(origin)addLineToPoint(CGPoint(x: origin.x + triangleSideLength, y: origin.y))addLineToPoint(CGPoint(x: origin.x + triangleSideLength / 2, y: origin.y + altitude))closePath()}}
You can use extensions to add properties (including class and static properties). However, these properties must be computed; extensions can’t add stored properties to classes, structures, or enumerations.
This example extends the CGRect structure to contain a computed area property:
extension CGRect {var area: CGFloat {return width * height}}let rect = CGRect(x: 0.0, y: 0.0, width: 10.0, height: 50.0)let area = rect.area
You can also use extensions to add protocol conformance to a class without subclassing it. If the protocol is defined in Swift, you can also add conformance to it to structures or enumerations, whether defined in Swift or Objective-C.
You cannot use extensions to override existing methods or properties on Objective-C types.
Closures
Objective-C blocks are automatically imported as Swift closures with Objective-C block calling convention, denoted by the @convention(block) attribute. For example, here is an Objective-C block variable:
void (^completionBlock)(NSData *, NSError *) = ^(NSData *data, NSError *error) {// ...}
And here’s what it looks like in Swift:
let completionBlock: (NSData, NSError) -> Void = { data, error in// ...}
Swift closures and Objective-C blocks are compatible, so you can pass Swift closures to Objective-C methods that expect blocks. Swift closures and functions have the same type, so you can even pass the name of a Swift function.
Closures have similar capture semantics as blocks but differ in one key way: Variables are mutable rather than copied. In other words, the behavior of __block in Objective-C is the default behavior for variables in Swift.
Avoiding Strong Reference Cycles When Capturing self
In Objective-C, if you need to capture self in a block, it’s important to consider the memory management implications.
Blocks maintain strong references to any captured objects, including self. If self maintains a strong reference to the block, such as a copying property, this would create a strong reference cycle. To avoid this, you can instead have the block capture a weak reference to self:
__weak typeof(self) weakSelf = self;self.block = ^{__strong typeof(self) strongSelf = weakSelf;[strongSelf doSomething];};
Like blocks in Objective-C, closures in Swift also maintain strong references to any captured objects, including self. To prevent a strong reference cycle, you can specify self to be unowned in a closure’s capture list:
self.closure = { [unowned self] inself.doSomething()}
For more information, see Resolving Strong Reference Cycles for Closures in The Swift Programming Language (Swift 2.2).
Object Comparison
There are two distinct types of comparison you can make between two objects in Swift. The first, equality (==), compares the contents of the objects. The second, identity (===), determines whether or not the constants or variables refer to the same object instance.
Swift provides default implementations of the == and === operators and adopts the Equatable protocol for objects that derive from the NSObject class. The default implementation of the == operator invokes the isEqual: method, and the default implementation of the === operator checks pointer equality. You should not override the equality or identity operators for types imported from Objective-C.
The base implementation of the isEqual: provided by the NSObject class is equivalent to an identity check by pointer equality. You can override isEqual: in a subclass to have Swift and Objective-C APIs determine equality based on the contents of objects rather than their identities. For more information about implementing comparison logic, see Object comparison in Cocoa Core Competencies.
Hashing
Swift adopts the Hashable protocol and provides a default implementation of the required hashValue property for objects that derive from the NSObject class. The default implementation of the hashValue property invokes the hash property.
Subclasses of NSObject that provide their own implementation of the isEquals: method should also provide their own implementation of the hash property.
Swift Type Compatibility
When you create a Swift class that descends from an Objective-C class, the class and its members—properties, methods, subscripts, and initializers—that are compatible with Objective-C are automatically available from Objective-C. This excludes Swift-only features, such as those listed here:
Generics
Tuples
Enumerations defined in Swift without
Intraw value typeStructures defined in Swift
Top-level functions defined in Swift
Global variables defined in Swift
Typealiases defined in Swift
Swift-style variadics
Nested types
Curried functions
Swift APIs are translated into Objective-C similar to how Objective-C APIs are translated into Swift, but in reverse:
Swift optional types are annotated as
__nullable.Swift non-optional types are annotated as
__nonnull.Swift constant stored properties and computed properties become read-only Objective-C properties.
Swift variable stored properties become read-write Objective-C properties.
Swift type methods become Objective-C class methods.
Swift initializers and instance methods become Objective-C instance methods.
Swift methods that throw errors become Objective-C methods with a trailing
NSError **parameter andAndReturnError:appended to their name. If a Swift method does not specify a return type, the corresponding Objective-C method has aBOOLreturn type.
For example, consider the following Swift declaration:
class Jukebox: NSObject {var library: Set<String>var nowPlaying: String?var isCurrentlyPlaying: Bool {return nowPlaying != nil}init(songs: String...) {self.library = Set<String>(songs)}func playSong(named name: String) throws {// play song or throw an error if unavailable}}
Here’s how it’s imported by Objective-C:
@interface Jukebox : NSObject@property (nonatomic, copy) NSSet<NSString *> * __nonnull library;@property (nonatomic, copy) NSString * __nullable nowPlaying;@property (nonatomic, readonly) BOOL isCurrentlyPlaying;- (nonnull instancetype)initWithSongs:(NSArray<NSString *> * __nonnull)songs OBJC_DESIGNATED_INITIALIZER;- (BOOL)playSong:(NSString * __nonnull)name error:(NSError * __nullable * __null_unspecified)error;@end
Configuring Swift Interfaces in Objective-C
In some cases, you need finer grained control over how your Swift API is exposed to Objective-C. You can use the @objc(name) attribute to change the name of a class, property, method, enumeration type, or enumeration case declaration in your interface as it’s exposed to Objective-C code.
For example, if the name of your Swift class contains a character that isn’t supported by Objective-C, you can provide an alternative name to use in Objective-C. If you provide an Objective-C name for a Swift function, use Objective-C selector syntax. Remember to add a colon (:) wherever a parameter follows a selector piece.
@objc(Color)enum Цвет: Int {@objc(Red)case Красный@objc(Black)case Черный}@objc(Squirrel)class Белка: NSObject {@objc(color)var цвет: Цвет = .Красный@objc(initWithName:)init (имя: String) {// ...}@objc(hideNuts:inTree:)func прячьОрехи(количество: Int, вДереве дерево: Дерево) {// ...}}
When you use the @objc(name) attribute on a Swift class, the class is made available in Objective-C without any namespacing. As a result, this attribute can also be useful when migrating an archivable Objective-C class to Swift. Because archived objects store the name of their class in the archive, you should use the @objc(name) attribute to specify the same name as your Objective-C class so that older archives can be unarchived by your new Swift class.
Requiring Dynamic Dispatch
When Swift APIs are imported by the Objective-C runtime, there are no guarantees of dynamic dispatch for properties, methods, subscripts, or initializers. The Swift compiler may still devirtualize or inline member access to optimize the performance of your code, bypassing the Objective-C runtime.
You can use the dynamic modifier to require that access to members be dynamically dispatched through the Objective-C runtime. Requiring dynamic dispatch is rarely necessary. However, it is necessary when using using APIs like key–value observing or the method_exchangeImplementations function in the Objective-C runtime, which dynamically replace the implementation of a method at runtime. If the Swift compiler inlined the implementation of the method or devirtualized access to it, the new implementation would not be used.
Objective-C Selectors
An Objective-C selector is a type that refers to the name of an Objective-C method. In Swift, Objective-C selectors are represented by the Selector structure. You can construct a selector using the #selector expression, such as let mySelector = #selector(MyViewController.tappedButton), with a direct reference to an Objective-C method as the subexpression. An Objective-C method reference can be parenthesized, and it can use the as operator to disambiguate between overloaded functions, such as let anotherSelector = #selector(((UIView.insertSubview(_:at:)) as (UIView) -> (UIView, Int) -> Void)).
import UIKitclass MyViewController: UIViewController {let myButton = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))override init?(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)let action = #selector(MyViewController.tappedButton)myButton.addTarget(self, action: action, forControlEvents: .TouchUpInside)}func tappedButton(sender: UIButton!) {print("tapped button")}required init?(coder: NSCoder) {super.init(coder: coder)}}
Sending Messages with performSelector
You can send messages to Objective-C compatible objects using the performSelector(_:) method and its variants.
The performSelector APIs that send messages on a specific thread or after a delay do not return a value. The performSelector APIs that execute synchronously return implicitly unwrapped optional unmanaged instance (Unmanaged<AnyObject>!), because the type and ownership of the value returned by performing the selector cannot be determined at compile time. See Unmanaged Objects for more information.
let string: NSString = "Hello, Cocoa!"let selector = #selector(NSString.lowercaseStringWithLocale(_:))let locale = NSLocale.currentLocale()if let result = string.performSelector(selector, withObject: locale) {print(result.takeUnretainedValue())}// Prints "hello, cocoa!"
Sending an unrecognized selector to an object causes the receiver to call doesNotRecognizeSelector(_:), which by default raises an NSInvalidArgumentException exception.
let array: NSArray = ["delta", "alpha", "zulu"]// Not a compile-time error because NSDictionary has this selector.let selector = #selector(NSDictionary.allKeysForObject)// Raises an exception because NSArray does not respond to this selector.array.performSelector(selector)
Sending a message directly to objects in the Objective-C runtime is inherently unsafe, as the compiler cannot make any guarantees about the result of the message send, or whether the message will resolve in the first place. As such, use of the performSelector APIs is discouraged unless your code specifically relies on the dynamic method resolution provided by the Objective-C runtime. Otherwise, it is safer and more convenient to optionally chain method calls to a type cast to AnyObject, as described in id Compatibility.
Copyright © 2016 Apple Inc. All rights reserved. Terms of Use | Privacy Policy | Updated: 2016-03-21
