[swift-evolution] Type-safe selectors

Brent Royal-Gordon brent at architechies.com
Sat Dec 5 01:43:16 CST 2015

>> Why couldn't this be covered by overloads? It's unlikely we'd be able
>> to import selectors from Objective-C to specific types because of this
>> and other reasons, so we'd still need an untyped Selector type, but we
>> could easily provide well-typed overloads in Swift overlays that
>> convert from typed selectors to untyped.
> This can only be covered by overloads when the selector is passed to a
> function. So that would work for target/action in iOS, where you use
> addTarget(_:action:forControlEvents:). But it won't work for OS X, where
> target/action is exposed as two independent properties, because you
> can't overload properties.

So? I mean, it’s sad that OS X can’t force you to use a selector that matches the target. But you’re still going to get some typing benefits.

Suppose NSControl is bridged to Swift with these properties:

	var target: NSObjectProtocol?
	var action: Selector

And you set it up like so:

	frobButton.target = controller
	frobButton.action = MyViewController.frobnicate

Swift can’t prove that myViewController is of type MyViewController. But this code still proves that:

1. controller is an NSObjectProtocol, and thus a plausible target.
2. frobnicate is a real method on MyViewController (it’s not misspelled, it’s not on the window controller or something).
3. frobnicate is an @objc method.
4. frobnicate will be called if target is a MyViewController (i.e. the user doesn’t have to understand how Objective-C selectors map to Swift methods or worry about whether that particular method has an @objc(replacementName:) attribute).

Now let’s give action a more precise definition:

	var action: (@convention(selector) NSObjectProtocol -> AnyObject -> Void)?

(This doesn’t support no-sender actions, but technically, I don’t think OS X is supposed to allow them. They coincidentally work because the ABI is loosey-goosey in the right places.)

To the above four guarantees, this definition adds a fifth:

5. frobnicate takes one parameter which can accept an AnyObject.

Do these guarantees mean that you can’t ever mismatch the target and action? No. But do they eliminate several other common target/action bugs? Yes! That seems worth doing to me.

And that’s assuming you don’t take any artistic license in the bridging. There’s no reason the Swift overlay can’t do as Michel Fortin suggested and hide the target and action setters, replacing them with a method which provides the same features and guarantees as UIKit’s overloaded calls:

	func setTarget<T: NSObjectProtocol>(target: T?, action: (@convention(selector) T -> AnyObject -> Void)?)

Brent Royal-Gordon

More information about the swift-evolution mailing list