[swift-evolution] [Proposal draft] Referencing the Objective-C selector of a method

Douglas Gregor dgregor at apple.com
Mon Jan 11 15:08:14 CST 2016


Here’s a follow-up to the idea I posted for a simple expression to produce the selector for a given method, revised based on the discussion there. The proposal is over at

	https://github.com/DougGregor/swift-evolution/blob/objc-selectors/proposals/0000-objc-selectors.md

Referencing the Objective-C selector of a method

Proposal: SE-NNNN <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-objc-selectors.md>
Author(s): Doug Gregor <https://github.com/DougGregor>
Status: Awaiting review
Review manager: TBD
 <https://github.com/DougGregor/swift-evolution/blob/objc-selectors/proposals/0000-objc-selectors.md#introduction>Introduction

In Swift 2, Objective-C selectors are written as string literals (e.g., "insertSubview:aboveSubview:") in the type context of a Selector. This proposal seeks to replace this error-prone approach with Selector initialization syntax that refers to a specific method via its Swift name.

Swift-evolution thread: here <http://thread.gmane.org/gmane.comp.lang.swift.evolution/1384/focus=1403>
 <https://github.com/DougGregor/swift-evolution/blob/objc-selectors/proposals/0000-objc-selectors.md#motivation>Motivation

The use of string literals for selector names is extremely error-prone: there is no checking that the string is even a well-formed selector, much less that it refers to any known method, or a method of the intended class. Moreover, with the effort to perform automatic renaming of Objective-C APIs <https://github.com/apple/swift-evolution/blob/master/proposals/0005-objective-c-name-translation.md>, the link between Swift name and Objective-C selector is non-obvious. By providing explicit "create a selector" syntax based on the Swift name of a method, we eliminate the need for developers to reason about the actual Objective-C selectors being used.

 <https://github.com/DougGregor/swift-evolution/blob/objc-selectors/proposals/0000-objc-selectors.md#proposed-solution>Proposed solution

Introduce Selector initialization syntax that allows one to build a selector from a reference to a method, e.g.,

control.sendAction(Selector(MyApplication.doSomething), to: target, forEvent: event)
where “doSomething” is a method of MyApplication, which might even have a completely-unrelated name in Objective-C:

extension MyApplication {
  @objc(jumpUpAndDown:)
  func doSomething(sender: AnyObject?) { … }
}
By naming the Swift method and having the Selector initializer do the work to form the Objective-C selector, we free the developer from having to do the naming translation manually and get static checking that the method exists and is exposed to Objective-C.

This proposal composes with the Naming Functions with Argument Labels proposal <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006262.html>, which lets us name methods along with their argument labels, e.g.:

let sel = Selector(UIView.insertSubview(_:at:)) // produces the Selector "insertSubview:atIndex:"
With the introduction of the Selector syntax, we should deprecate the use of string literals to form selectors. Ideally, we could perform the deprecation in Swift 2.2 and remove the syntax entirely from Swift 3.

Additionally, we should introduce specific migrator support to translate string-literals-as-selectors into method references. Doing this well is non-trivial, requiring the compiler/migrator to find all of the declarations with a particular Objective-C selector and determine which one to reference. However, it should be feasible, and we can migrator other references to a specific, string-based initialization syntax (e.g., Selector("insertSubview:atIndex:")).

 <https://github.com/DougGregor/swift-evolution/blob/objc-selectors/proposals/0000-objc-selectors.md#detailed-design>Detailed design

The proposed Selector initializer "almost" has the signature:

extension Selector {
  init<T, U>(_ fn: (T) -> U)
}
with some additional semantic restrictions that require that input be a reference to an objc method. Specifically, the input expression must be a direct reference to an Objective-C method, possibly parenthesized and possible with an "as" cast (which can be used to disambiguate same-named Swift methods). For example, here is a "highly general" example:

let sel = Selector(((UIKit.UIView.insertSubview(_:at:)) as (UIView) -> (UIView, Int) -> Void))
The actual implementation will introduce some magic in the type checker to only support references to methods within the Selector initialization syntax.

 <https://github.com/DougGregor/swift-evolution/blob/objc-selectors/proposals/0000-objc-selectors.md#impact-on-existing-code>Impact on existing code

The introduction of the Selector initialization syntax has no impact on existing code. However, deprecating and removing the string-literal-as-selector syntax is a source-breaking change. We can migrate the uses to either the new Selectorinitialization syntax or to explicit initialization of a Selector from a string.

 <https://github.com/DougGregor/swift-evolution/blob/objc-selectors/proposals/0000-objc-selectors.md#alternatives-considered>Alternatives considered

The primary alternative is type-safe selectors <https://lists.swift.org/pipermail/swift-evolution/2015-December/000233.html>, which would introduce a new "selector" calling convetion to capture the type of an @objc method, including its selector. One major benefit of type-safe selectors is that they can carry type information, improving type safety. From that discussion, referencing MyClass.observeNotification would produce a value of type:

@convention(selector) (MyClass) -> (NSNotification) -> Void
Objective-C APIs that accept selectors could provide type information (e.g., via Objective-C attributes or new syntax for a typed SEL), improving type safety for selector-based APIs. Personally, I feel that type-safe selectors are a well-designed feature that isn't worth doing: one would probably not use them outside of interoperability with existing Objective-C APIs, because closures are generally preferable (in both Swift and Objective-C). The cost of adding this feature to both Swift and Clang is significant, and we would also need adoption across a significant number of Objective-C APIs to make it worthwhile. On iOS, we are talking about a relatively small number of APIs (100-ish), and many of those have blocks/closure-based variants that are preferred anyway. Therefore, we should implement the simpler feature in this proposal rather than the far more complicated (but admittedly more type-safe) alternative approach.

	- Doug

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160111/a996d8ab/attachment.html>


More information about the swift-evolution mailing list