Guides and Sample Code

Developer

Using Swift with Cocoa and Objective-C (Swift 3.1)

iBooks
On This Page

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:

  1. - (instancetype)init;
  2. - (instancetype)initWithFrame:(CGRect)frame
  3. style:(UITableViewStyle)style;

Here are the equivalent Swift initializer declarations:

  1. init() { /* ... */ }
  2. 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:

  1. UITableView *myTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];

In Swift, you do this:

  1. let myTableView: UITableView = UITableView(frame: .zero, 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.

  1. 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:

  1. UIColor *color = [UIColor colorWithRed:0.5 green:0.0 blue:0.5 alpha:1.0];

In Swift, you call it like this:

  1. 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.

  1. if let image = UIImage(contentsOfFile: "MyImage.png") {
  2. // loaded the image successfully
  3. } else {
  4. // could not load the image
  5. }

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, and null_resettable) are imported as Swift properties with optional or nonoptional type as described in Nullability and Optionals.

  • Properties with the readonly property attribute are imported as Swift computed properties with a getter ({ get }).

  • Properties with the weak property attribute are imported as Swift properties marked with the weak keyword (weak var).

  • Properties with an ownership property attribute other than weak (that is, assign, copy, strong, or unsafe_unretained) are imported as Swift properties with the appropriate storage.

  • Properties with the class property attribute are imported as Swift type properties.

  • Atomicity property attributes (atomic and nonatomic) 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= and setter=) 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 object with the following code:

  1. myTextField.textColor = .darkGray
  2. 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:

  1. [myTableView insertSubview:mySubview atIndex:2];

In Swift, you do this:

  1. myTableView.insertSubview(mySubview, at: 2)

If you’re calling a method with no arguments, you must still include parentheses.

  1. myTableView.layoutIfNeeded()

id Compatibility

The Objective-C id type is imported by Swift as the Any type. At compile time and runtime, the compiler introduces a universal bridging conversion operation when a Swift value or object is passed into Objective-C as an id parameter. When id values are imported into Swift as Any, the runtime automatically handles bridging back to either class references or Swift value types.

  1. var x: Any = "hello" as String
  2. x as? String // String with value "hello"
  3. x as? NSString // NSString with value "hello"
  4. x = "goodbye" as NSString
  5. x as? String // String with value "goodbye"
  6. x as? NSString // NSString with value "goodbye"

Downcasting Any

When working with objects of type Any 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 Any type can refer to any type, 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:

  1. let userDefaults = UserDefaults.standard
  2. let lastRefreshDate = userDefaults.object(forKey: "LastRefreshDate") // lastRefreshDate is of type Any?
  3. if let date = lastRefreshDate as? Date {
  4. print("\(date.timeIntervalSinceReferenceDate)")
  5. }

If you are certain of the type of the object, you can use the forced downcast operator (as!) instead.

  1. let myDate = lastRefreshDate as! Date
  2. let timeInterval = myDate.timeIntervalSinceReferenceDate

However, if a forced downcast fails, a runtime error is triggered:

  1. let myDate = lastRefreshDate as! String // Error

Dynamic Method Lookup

Swift also includes an AnyObject type that represents some kind of object and has the special ability to look up any @objc method dynamically. This allows you to write maintain flexibility of untyped access to Objective-C APIs that return id values.

For example, you can assign an object of any class type to a constant or variable of AnyObject type and reassign a variable to an object of a different type. You can also call any Objective-C method and access any property on an AnyObject value without casting to a more specific class type.

  1. var myObject: AnyObject = UITableViewCell()
  2. myObject = NSDate()
  3. let futureDate = myObject.addingTimeInterval(10)
  4. 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:

  1. myObject.character(at: 5)
  2. // 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 character(at:) 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 iflet statement to conditionally unwrap the result of a method that the object may not respond to, as shown on line three.

  1. // myObject has AnyObject type and NSDate value
  2. let myCount = myObject.count
  3. // myCount has Int? type and nil value
  4. let myChar = myObject.character?(at: 5)
  5. // myChar has unichar? type and nil value
  6. if let fifthCharacter = myObject.character?(at: 5) {
  7. print("Found \(fifthCharacter) at index 5")
  8. }
  9. // conditional branch not executed

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 3.1).

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 nonoptional references, and imports it as an implicitly unwrapped optional.

  • Types declared to be nonnullable, either with a _Nonnull annotation or in an audited region, are imported by Swift as a nonoptional.

  • Types declared to be nullable with a _Nullable annotation, 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:

  1. @property (nullable) id nullableProperty;
  2. @property (nonnull) id nonNullProperty;
  3. @property id unannotatedProperty;
  4. NS_ASSUME_NONNULL_BEGIN
  5. - (id)returnsNonNullValue;
  6. - (void)takesNonNullParameter:(id)value;
  7. NS_ASSUME_NONNULL_END
  8. - (nullable id)returnsNullableValue;
  9. - (void)takesNullableParameter:(nullable id)value;
  10. - (id)returnsUnannotatedValue;
  11. - (void)takesUnannotatedParameter:(id)value;

Here’s how they’re imported by Swift:

  1. var nullableProperty: Any?
  2. var nonNullProperty: Any
  3. var unannotatedProperty: Any!
  4. func returnsNonNullValue() -> Any
  5. func takesNonNullParameter(value: Any)
  6. func returnsNullableValue() -> Any?
  7. func takesNullableParameter(value: Any?)
  8. func returnsUnannotatedValue() -> Any!
  9. func takesUnannotatedParameter(value: Any!)

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.

Bridging Optionals to Nonnullable Objects

Swift bridges optional values to nonnullable Objective-C objects according to whether the optional contains an underlying, wrapped value. If the optional is nil, Swift bridges the nil value as an NSNull instance. Otherwise, Swift bridges the optional as its unwrapped value. For example, you see this behavior when an optional is passed to an Objective-C API that takes a nonnull id and when an array of optional items ([T?]) is bridged to an NSArray.

The following code shows how String? instances bridge to Objective-C depending on their value.

  1. @implementation OptionalBridging
  2. + (void)logSomeValue:(nonnull id)valueFromSwift {
  3. if ([valueFromSwift isKindOfClass: [NSNull class]]) {
  4. os_log(OS_LOG_DEFAULT, "Received an NSNull value.");
  5. } else {
  6. os_log(OS_LOG_DEFAULT, "%s", [valueFromSwift UTF8String]);
  7. }
  8. }
  9. @end

Because the type of the valueFromSwift parameter is id, it’s imported in the Swift code below as the Swift type Any. However, because passing an optional when an Any was expected is uncommon, the optionals passed into the logSomeValue(_:) class method are explicitly cast to the Any type, which silences a compiler warning.

  1. let someValue: String? = "Bridge me, please."
  2. let nilValue: String? = nil
  3. OptionalBridging.logSomeValue(someValue as Any) // Bridge me, please.
  4. OptionalBridging.logSomeValue(nilValue as Any) // Received an NSNull value.

Protocol-Qualified Classes

Objective-C protocol-qualified classes are imported by Swift as protocol type values. For example, given the following Objective-C method that performs an operation on the specified class:

  1. - (void)doSomethingForClass:(Class<NSCoding>)codingClass;

Here’s how Swift imports it:

  1. func doSomething(for codingClass: NSCoding.Type)

Lightweight Generics

Objective-C type declarations using lightweight generic parameterization are imported by Swift with information about the type of their contents preserved. For example, given the following Objective-C property declarations:

  1. @property NSArray<NSDate *> *dates;
  2. @property NSCache<NSObject *, id<NSDiscardableContent>> *cachedData;
  3. @property NSDictionary <NSString *, NSArray<NSLocale *>> *supportedLocales;

Here’s how Swift imports them:

  1. var dates: [Date]
  2. var cachedData: NSCache<AnyObject, NSDiscardableContent>
  3. var supportedLocales: [String: [Locale]]

A parameterized class written in Objective-C is imported into Swift as a generic class with the same number of type parameters. All Objective-C generic type parameters imported by Swift have a type constraint that requires that type to be a class (T: Any). If the Objective-C generic parameterization specifies a class qualification, the imported Swift class has a constraint that requires that type to be a subclass of the specified class. If the Objective-C generic parameterization specifies a protocol qualification, the imported Swift class has a constraint that requires that type to conform to the specified protocol. For an unspecialized Objective-C type, Swift infers the generic parameterization for the imported class type constraints. For example, given the following Objective-C class and category declarations:

  1. @interface List<T: id<NSCopying>> : NSObject
  2. - (List<T> *)listByAppendingItemsInList:(List<T> *)otherList;
  3. @end
  4. @interface ListContainer : NSObject
  5. - (List<NSValue *> *)listOfValues;
  6. @end
  7. @interface ListContainer (ObjectList)
  8. - (List *)listOfObjects;
  9. @end

Here’s how they’re imported by Swift:

  1. class List<T: NSCopying> : NSObject {
  2. func listByAppendingItemsInList(otherList: List<T>) -> List<T>
  3. }
  4. class ListContainer : NSObject {
  5. func listOfValues() -> List<NSValue>
  6. }
  7. extension ListContainer {
  8. func listOfObjects() -> List<NSCopying>
  9. }

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.

  1. extension UIBezierPath {
  2. convenience init(triangleSideLength: CGFloat, origin: CGPoint) {
  3. self.init()
  4. let squareRoot = CGFloat(sqrt(3.0))
  5. let altitude = (squareRoot * triangleSideLength) / 2
  6. move(to: origin)
  7. addLine(to: CGPoint(x: origin.x + triangleSideLength, y: origin.y))
  8. addLine(to: CGPoint(x: origin.x + triangleSideLength / 2, y: origin.y + altitude))
  9. close()
  10. }
  11. }

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:

  1. extension CGRect {
  2. var area: CGFloat {
  3. return width * height
  4. }
  5. }
  6. let rect = CGRect(x: 0.0, y: 0.0, width: 10.0, height: 50.0)
  7. 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:

  1. void (^completionBlock)(NSData *) = ^(NSData *data) {
  2. // ...
  3. }

And here’s what it looks like in Swift:

  1. let completionBlock: (Data) -> Void = { data in
  2. // ...
  3. }

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:

  1. __weak typeof(self) weakSelf = self;
  2. self.block = ^{
  3. __strong typeof(self) strongSelf = weakSelf;
  4. [strongSelf doSomething];
  5. };

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:

  1. self.closure = { [unowned self] in
  2. self.doSomething()
  3. }

For more information, see Resolving Strong Reference Cycles for Closures in The Swift Programming Language (Swift 3.1).

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 imports Objective-C declarations of NSDictionary that don’t specify a class qualification for the key type as a Dictionary with the Key type AnyHashable. Similarly, NSSet declarations without a class-qualified object type are imported by Swift as a Set with the Element type AnyHashable. If an NSDictionary or NSSet declaration does parameterize its key or object type, respectively, that type is used instead. For example, given the following Objective-C declarations:

  1. @property NSDictionary *unqualifiedDictionary;
  2. @property NSDictionary<NSString *, NSDate *> *qualifiedDictionary;
  3. @property NSSet *unqualifiedSet;
  4. @property NSSet<NSString *> *qualifiedSet;

Here’s how Swift imports them:

  1. var unqualifiedDictionary: [AnyHashable: Any]
  2. var qualifiedDictionary: [String: Date]
  3. var unqualifiedSet: Set<AnyHashable>
  4. var qualifiedSet: Set<String>

The AnyHashable type is used by Swift when importing Objective-C declarations with an unspecified or id type that cannot be otherwise be imported as Any because the type needs to conform to the Hashable protocol. The AnyHashable type is implicitly converted from any Hashable type, and you can use the as? and as! operators to cast from AnyHashable to a more specific type.

For more information, see the AnyHashable API reference.

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 Int raw value type

  • Structures 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 nonoptional 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 properties become Objective-C properties with the class property attribute.

  • 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 an NSError ** parameter. If the Swift method has no parameters, AndReturnError: is appended to the Objective-C method name, otherwise error: is appended. If a Swift method does not specify a return type, the corresponding Objective-C method has a BOOL return type. If the Swift method returns a nonoptional type, the corresponding Objective-C method has an optional return type.

For example, consider the following Swift declaration:

  1. class Jukebox: NSObject {
  2. var library: Set<String>
  3. var nowPlaying: String?
  4. var isCurrentlyPlaying: Bool {
  5. return nowPlaying != nil
  6. }
  7. class var favoritesPlaylist: [String] {
  8. // return an array of song names
  9. }
  10. init(songs: String...) {
  11. self.library = Set<String>(songs)
  12. }
  13. func playSong(named name: String) throws {
  14. // play song or throw an error if unavailable
  15. }
  16. }

Here’s how it’s imported by Objective-C:

  1. @interface Jukebox : NSObject
  2. @property (nonatomic, strong, nonnull) NSSet<NSString *> *library;
  3. @property (nonatomic, copy, nullable) NSString *nowPlaying;
  4. @property (nonatomic, readonly, getter=isCurrentlyPlaying) BOOL currentlyPlaying;
  5. @property (nonatomic, class, readonly, nonnull) NSArray<NSString *> * favoritesPlaylist;
  6. - (nonnull instancetype)initWithSongs:(NSArray<NSString *> * __nonnull)songs OBJC_DESIGNATED_INITIALIZER;
  7. - (BOOL)playSong:(NSString * __nonnull)name error:(NSError * __nullable * __null_unspecified)error;
  8. @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.

  1. @objc(Color)
  2. enum Цвет: Int {
  3. @objc(Red)
  4. case Красный
  5. @objc(Black)
  6. case Черный
  7. }
  8. @objc(Squirrel)
  9. class Белка: NSObject {
  10. @objc(color)
  11. var цвет: Цвет = .Красный
  12. @objc(initWithName:)
  13. init (имя: String) {
  14. // ...
  15. }
  16. @objc(hideNuts:inTree:)
  17. func прячьОрехи(количество: Int, вДереве дерево: Дерево) {
  18. // ...
  19. }
  20. }

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 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.

Selectors

In Objective-C, a 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, and can be constructed using the #selector expression. To create a selector for a method that can be called from Objective-C, pass the name of the method, such as #selector(MyViewController.tappedButton(sender:)). To construct a selector for a property’s Objective-C getter or setter method, pass the property name prefixed by the getter: or setter: label, such as #selector(getter: MyViewController.myButton).

  1. import UIKit
  2. class MyViewController: UIViewController {
  3. let myButton = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
  4. override init?(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
  5. super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
  6. let action = #selector(MyViewController.tappedButton)
  7. myButton.addTarget(self, action: action, forControlEvents: .touchUpInside)
  8. }
  9. func tappedButton(sender: UIButton?) {
  10. print("tapped button")
  11. }
  12. required init?(coder: NSCoder) {
  13. super.init(coder: coder)
  14. }
  15. }

Unsafe Invocation of Objective-C Methods

You can invoke an Objective-C method on an Objective-C compatible object using a selector with the perform(_:) method or one of its variants. Invoking a method using a selector is inherently unsafe, because the compiler cannot make any guarantees about the result, or even guarantee whether the object responds to the selector. Therefore, using these APIs is strongly discouraged unless your code specifically relies on the dynamic method resolution provided by the Objective-C runtime. For example, using these APIs would be appropriate if you need to implement a class that uses the target-action design pattern in its interface, like NSResponder does. In most cases, it’s safer and more convenient to cast the object to AnyObject and use optional chaining with a method call as described in id Compatibility.

The methods that perform a selector synchronously, such as perform(_:), return an implicitly unwrapped optional unmanaged pointer to an AnyObject instance (Unmanaged<AnyObject>!), because the type and ownership of the value returned by performing the selector can’t be determined at compile time. In contrast, the methods that perform a selector on a specific thread or after a delay, such as perform(_:on:with:waitUntilDone:modes:) and perform(_:with:afterDelay:), don’t return a value. See Unmanaged Objects for more information.

  1. let string: NSString = "Hello, Cocoa!"
  2. let selector = #selector(NSString.lowercased(with:))
  3. let locale = Locale.current
  4. if let result = string.perform(selector, with: locale) {
  5. print(result.takeUnretainedValue())
  6. }
  7. // Prints "hello, cocoa!"

Attempting to invoke a method on an object with an unrecognized selector causes the receiver to call doesNotRecognizeSelector(_:), which by default raises an NSInvalidArgumentException exception.

  1. let array: NSArray = ["delta", "alpha", "zulu"]
  2. // Not a compile-time error because NSDictionary has this selector.
  3. let selector = #selector(NSDictionary.allKeysForObject)
  4. // Raises an exception because NSArray does not respond to this selector.
  5. array.perform(selector)

Keys and Key Paths

In Objective-C, a key is a string that identifies a specific property of an object; a key path is a string of dot-separated keys that specifies a sequence of object properties to traverse. Keys and key paths are frequently used for key-value coding (KVC), a mechanism for indirectly accessing an object’s attributes and relationships using string identifiers. Keys and key paths are also used for key-value observing (KVO), a mechanism that enables an object to be notified directly when a property of another object changes.

In Swift, you can use the #keyPath expression to generate compiler-checked keys and key paths that can be used by KVC methods like value(forKey:) and value(forKeyPath:) and KVO methods like addObserver(_:forKeyPath:options:context:). The #keyPath expression accepts chained method or property references, such as #keyPath(Person.bestFriend.name).

  1. class Person: NSObject {
  2. var name: String
  3. var friends: [Person] = []
  4. var bestFriend: Person? = nil
  5. init(name: String) {
  6. self.name = name
  7. }
  8. }
  9. let gabrielle = Person(name: "Gabrielle")
  10. let jim = Person(name: "Jim")
  11. let yuanyuan = Person(name: "Yuanyuan")
  12. gabrielle.friends = [jim, yuanyuan]
  13. gabrielle.bestFriend = yuanyuan
  14. #keyPath(Person.name)
  15. // "name"
  16. gabrielle.value(forKey: #keyPath(Person.name))
  17. // "Gabrielle"
  18. #keyPath(Person.bestFriend.name)
  19. // "bestFriend.name"
  20. gabrielle.value(forKeyPath: #keyPath(Person.bestFriend.name))
  21. // "Yuanyuan"
  22. #keyPath(Person.friends.name)
  23. // "friends.name"
  24. gabrielle.value(forKeyPath: #keyPath(Person.friends.name))
  25. // ["Yuanyuan", "Jim"]