Swift Enum to Cocoa Touch UIButton selector string

1.2k Views Asked by At

I was adding a target to a UIButton for my letterTapped() function like this:

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

I like how .TouchUpInside works with autocompletion and it looks neater and seems safer than the string I'm supposed to use for the action: parameter. So I searched and found this tutorial which uses enums to replace "magic strings".

I created an enum like this:

    enum functionForAction: Selector {

    case clearTapped = "clearTapped:"
    case submitTapped = "submitTapped:"
    case letterTapped = "letterTapped:"

}

Then I use it like this:

 button.addTarget(self, action: functionForAction.letterTapped.rawValue, forControlEvents: .TouchUpInside)

I get code completion and I'm never trying to call a misspelled selector. Feels better. But is it possible to improve it? I'd really just like to type this:

 button.addTarget(self, action: .letterTapped, forControlEvents: .TouchUpInside)

Can enums make this happen for me in Swift?

1

There are 1 best solutions below

5
On BEST ANSWER

There are two ways to do this, and while it's possible to get to the syntax you want, I'd argue that the 2nd way is most likely a better way of doing it.

To do what you want, we'll create an enum like you did, but also add an extension to UIControl (UIButton's superclass) so that we can addTarget with a ControlSelector

enum ControlSelector: Selector {
    case ClearTapped = "clearTapped:"
    case SubmitTapped = "submitTapped:"
    case LetterTapped = "letterTapped:"
}

extension UIControl {
    func addTarget(target: AnyObject?, action: ControlSelector, forControlEvents controlEvents: UIControlEvents) {
        addTarget(target, action: action.rawValue, forControlEvents: controlEvents)
    }
}

Now we can call addTarget with your preferred syntax:

button.addTarget(self, action: .ClearTapped, forControlEvents: .TouchUpInside)

(By the way, enums usually have the first letter in capital letters by convention, which is why I changed it slightly from your code.)

However, this can get messy, unless you want your ControlSelector enum shared between all of your code (and now, what's the point of making the selectors more clear)? To make this work across all of your code, you'd either have to keep the access levels of both public, or write the UIControl extension in each file you'd like to write things this way.

I suggest instead using a private class to keep the selectors clear.

private class ControlSelector {
    static var clearTapped: Selector = "clearTapped:"
    static var submitTapped: Selector = "submitTapped:"
    static var letterTapped: Selector = "letterTapped:"
}

You then would not need the protocol extension anymore, and instead call your addTarget method this way:

button.addTarget(self, action: ControlSelector.clearTapped, forControlEvents: .TouchUpInside)

Hope this helps!