Swift language features must attempt to satisfy two sometimes contradictory objectives: they must explicitly maintain the safety of the type system while also providing a concise, developer friendly syntax. Swift’s approach to ensuring safe code via static analysis is deeply embedded into it’s design and idioms. But by their nature, safety features like type checking and optionals can result in more verbose code than willy-nilly Objective-C or other dynamically typed languages. Where conflicts between these two objectives arise, Swift (rightly, IMO) nearly always chooses the safety option1. However, In one feature, Swift fails on both criteria.

I’m looking at you Selector.

Selector is frequently used in wiring up UIButton2 and UIGestureRecognizer actions, and this is the syntax…

button.addTarget(self, action: Selector("buttonPressed:"), forControlEvents: .TouchUpInside)

A big red flag is that Selector is created with a String argument. As of Xcode 7, this string does not autocomplete (making it slow and error prone to type) and is not checked by the compiler to see if a method on the target matches the signature described by the Selector. The latter means that the following code will happily compile…

button.addTarget(self, action: Selector("whyDoesThisEvenCompile:"), forControlEvents: .TouchUpInside)

Tapping the button results in…

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[SomeApp.ViewController whyDoesThisEvenCompile:]: unrecognized selector sent to instance 0x145e07fa0' 

It is neither convenient nor safe. It just kinda sucks. On top of this, you don’t even get the good stuff from Objective-C. You can’t even invoke Selectors in Swift. They’re a glossed over backward-compatibility feature.

To be safe, Selectors need more type information. I suggest something like…

//NOT REAL SWIFT CODE
let selector = Selector(ViewController.Type, @buttonPressed:)
viewController.performSelector(selector, someObject)

@ is a thing I made up, but it kinda fits the idea of “messaging” an instance. I’m no expert in compilers, but it seems this could be statically checked3. ViewController has a method called buttonPressed that takes an AnyObject parameter, and someObject is an instance of AnyObject. This example alone would eliminate a whole class of errors. A nice extension of this concept is a Target consisting of an object and a Selector, which would replace the target-action idiom.

//NOT REAL SWIFT CODE
button.addTarget(Target(self, @buttonPressed:), forControlEvents: .TouchUpInside)

Going further, now that perform selector is back (and in slight contradiction to my earlier sentiment), I would maintain a way to create a selector from a String. Optional Selectors could provide a way to safely introduce some powerful options for things like json deserialization…

//NOT REAL SWIFT CODE
//Take all keys from dictionary and set the value on the matching selector in the model.
for let key in dictionary.allKeys {
	let value = dictionary[key]
	if let selector = Selector(ModelObject.Type, jsonKey, value.Type) {
		// Our class has a setter for jsonKey that takes the specified value
		modelObject.performSelector(selector, value)
	}
}

I’m sure this breaks down in a fundamental way I’m not fully considering, but I’d like to see more introspection and reflection options provided in Swift (assuming they’re both safe and syntactically concise :).

  1. Overtime, syntactic sugar can be added to make cleaner code, but safety (and a reputation for safety) is a hard thing to gain back once lost.

  2. Maybe I should just use Reactive Cocoa.

  3. Objective-C already throws a warning when a selector can’t be found, Swift should be capable of turning this into an error.