2

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?

OsakaStarbux
  • 147
  • 12

1 Answers1

1

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!

Edward Jiang
  • 2,403
  • 18
  • 13
  • The class version is nice. Thanks. It appears to work as a struct too. – OsakaStarbux Feb 07 '16 at 09:49
  • Changes to Swift might make using string literals for selector names a thing of the past. https://github.com/apple/swift-evolution/blob/master/proposals/0022-objc-selectors.md?utm_campaign=This%2BWeek%2Bin%2BSwift&utm_medium=web&utm_source=This_Week_in_Swift_72 – OsakaStarbux Feb 08 '16 at 02:50
  • Oh cool! I haven't been following Swift 3 so that's pretty good to know. I'm really excited for the future of Swift =] – Edward Jiang Feb 08 '16 at 04:58
  • The improvements in Swift 2.2 and this extension is the closest thing to what I was looking for https://medium.com/swift-programming/swift-selector-syntax-sugar-81c8a8b10df3#.8mefb23e4 – OsakaStarbux Mar 28 '16 at 23:36
  • Since I asked about enums I should follow up with what I learned today. I think I could have used an enum with no cases instead of a class or struct like this: https://www.natashatherobot.com/swift-enum-no-cases/ – OsakaStarbux Mar 30 '16 at 14:20