Using Objective-C Runtime Features in Swift
Use selectors and key paths to interact with dynamic Objective-C APIs.
Overview
Some Objective-C APIs—like target-action—accept method or property names as parameters, then use those names to dynamically call or access the methods or properties. In Swift, you use the #selector and #keyPath expressions to represent those method or property names as selectors or key paths, respectively.
Use Selectors to Arrange Calls to Objective-C Methods
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 you create them using the #selector expression.
In Swift, you create a selector for an Objective-C method by placing the name of the method within the #selector expression: #selector(MyViewController.tappedButton(_:)). To construct a selector for a property’s Objective-C getter or setter method, prefix the property name using the getter: or setter: label, like #selector(getter: MyViewController.myButton). The example below shows a selector being used as part of the target-action pattern to call a method in response to the touchUpInside event.
import UIKit
class MyViewController: UIViewController {
let myButton = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
override init(nibName nibNameOrNil: NSNib.Name?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
let action = #selector(MyViewController.tappedButton)
myButton.addTarget(self, action: action, forControlEvents: .touchUpInside)
}
@objc func tappedButton(_ sender: UIButton?) {
print("tapped button")
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
}If you need to disambiguate between overloaded functions, use parenthesized expressions along with the as operator to make the #selector expression refer unambiguously to a specific overload.
Use Key Paths to Dynamically Access Objective-C Properties
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.
You use the #keyPath string expression to create compiler-checked keys and key paths that can be used by KVC methods like value(forKey:) and value(forKeyPath:). The #keyPath string expression accepts chained method or property references. It also supports chaining through optional values within a chain, such as #keyPath(Person.bestFriend.name). Key paths created using the #keyPath string expression don’t pass type information about the properties or methods they reference to the APIs that accept key paths.
The example below defines a Person class, creates two instances of it, and uses several #keyPath string expressions to access properties and properties of those properties:
class Person: NSObject {
@objc var name: String
@objc var friends: [Person] = []
@objc var bestFriend: Person? = nil
init(name: String) {
self.name = name
}
}
let gabrielle = Person(name: "Gabrielle")
let jim = Person(name: "Jim")
let yuanyuan = Person(name: "Yuanyuan")
gabrielle.friends = [jim, yuanyuan]
gabrielle.bestFriend = yuanyuan
#keyPath(Person.name)
// "name"
gabrielle.value(forKey: #keyPath(Person.name))
// "Gabrielle"
#keyPath(Person.bestFriend.name)
// "bestFriend.name"
gabrielle.value(forKeyPath: #keyPath(Person.bestFriend.name))
// "Yuanyuan"
#keyPath(Person.friends.name)
// "friends.name"
gabrielle.value(forKeyPath: #keyPath(Person.friends.name))
// ["Yuanyuan", "Jim"]