Swift: Selector Syntax Sugar

Objective-C developers be jelly

Objective-C has been around for several years, and during that time developers been crafting their code style to make it nice and readable for following generations to benefit. But not Swift, Swift is new. There’s not really a single or most common style to conform with, so a lot of us have had to forge our own way with experimentation.

Over the past 12 months I’ve been fortunate enough to work with Swift for about 98.2% of my working week. During that time I’ve learnt to craft some (in my opinion) really nice code styles, which I’ll share part of with you today.

Selectors

Before Swift 2.2, selectors were string literals and prone to error because we, as humans invented, and still contribute to typos whenever given the chance to write something without autocomplete.

let button = UIButton(type: .system)
button.addTarget(self, action: Selector(“buttonTapped:”), for: .touchUpInside)
...
func buttonTapped(sender: UIButton) { }

One good naming convention for your function calls should be the object’s name suffixed with the action. In this case we had our button object tapped, which equates to buttonTapped:. Also remember to always pass in the sender and the correct type as the first and only argument, because it’s better to have it and not need it, than to need it and not have it.

A few other examples of how I like to name corresponding user interaction functions:

func segmentedControlValueChanged(sender: UISegmentedControl) { }
func barButtonItemTapped(sender: UIBarButtonItem) { }
func keyboardWillShowNotification(notification: Notification) { }

Improvements in Swift 2.2

However, our selectors are now much safer in Swift 2.2, but it’s still 
U-G-L-Y (It ain’t got no alibi). And to have all these selectors littered throughout your codebase makes the matter worse. What if you have Massive View Controller? What if you use the same selector multiple times?

button.addTarget(self, action:  
#selector(ViewController.buttonTapped(_:)), for: .touchUpInside)

This line is seriously way too long and kind of hard to read if you’re just scanning through. Imagine writing/copy-pasting that several times. Let’s tidy it up by placing all the selectors in one spot, where we can reference and edit them in a uniform matter.

fileprivate struct Action {
    static let buttonTapped = 
#selector(ViewController.buttonTapped(_:))
}
...
button.addTarget(self, action: Action.buttonTapped,       
for: .touchUpInside)

Awesome. Now we have one spot to put all our selectors in, and each object that wants to use that selector, gets it’s value from a static constant inside the Action struct. We have to use the name Action because it’s the next best thing to Selector, because Selector is taken by the Selector class (obviously).

Another wise thing to do is to use the same names for the static constant and functions, this will help you remember and maintain uniformity.

It’s fileprivate because we don’t want Xcode presenting us with “redeclaration conflict” errors, so this struct is only available to this specific .swift file.

I had actually been using this for quite a few months now, and it has served me well for that time. But this morning I realised I could take this even further, and make it more …sugary. Why make an Action struct when we can make a Selector extension?

fileprivate extension Selector {
    static let buttonTapped = 
#selector(ViewController.buttonTapped(_:))
}
...
button.addTarget(self, action: .buttonTapped, for: .touchUpInside)

Omg, right? We’ve made an extension to Selector which contains a static constant of the selector we want to use to call the method in our class.

We’ve taken advantage of Swift’s type inference too. Because this method is expecting a Selector object for the action: argument, we can omit the Selector. prefix completely when passing in a value.

It’s just like when you omit UIColor namespace for setting a color to a view:

view.backgroundColor = .black

Sample code of this post can be found on GitHub.


I’m incredibly overwhelmed and filled with joy that Chris Lattner enjoyed my post enough to share it with the Swift community.

Thank you to everyone that has liked, recommended, responded and retweeted this post.


If you like what you’ve read today you can check our my other articles or want to get in touch, please send me a tweet or follow me on Twitter, it really makes my day. I also organise Playgrounds Conference in Melbourne, Australia and would to see you at the next event.