Search code examples
iosswiftunrecognized-selector

Adding a target to a button programmatically throws an error "unrecognized selector sent to class"


Not sure why I am getting an "Unrecognized selector sent to class" error when I try to use the button I've created in code.

Here is my code:

let sendButton: UIButton = {
        let button = UIButton(type: .system)
        button.setTitle("Send", for: .normal)
        button.setTitleColor(UIColor.white, for: .normal)
        button.backgroundColor = #colorLiteral(red: 0.5098039216, green: 0.5215686275, blue: 0.8392156863, alpha: 1)
        button.layer.cornerRadius = 5
        button.isUserInteractionEnabled = true
        button.addTarget(self, action: #selector(postComment), for: .touchUpInside)
        return button
    }()

Solution

  • It's actually all a matter of timing. You are declaring an instance property along with a define-and-call function that initializes that property, and you proceed to say button.addTarget(self... in that function. The question then is: when will that function's code run? Incredibly, that matters a lot, so much so that it actually changes the meaning of self.

    When you say let to make this declaration, the code runs at a time when the instance does not yet exist. The instance is exactly what we are in the process of forming at that moment; it isn't "cooked" yet. In fact, unbeknownst to you, self at that moment means the UIButton class! Therefore the resulting target-action is invalid; you have no class method postComment; it's an instance method (rightly). And so later on when you tap the button and we try to say postComment to the class, we crash. "Unrecognized selector sent to class", exactly as you say.

    On the other hand, when you say lazy var, the code is not called until after the instance self does exist, self means the instance, and all is well.

    It's terribly confusing to beginners, and to not-so-beginners; even after pointing out the problem, I've proceeded to make exactly the same mistake myself. In my opinion the compiler should catch this and stop you, but it doesn't. I've filed a bug: http://bugs.swift.org/browse/SR-4865