'#selector' refers to a method that is not exposed to Objective-C

XcodeSwift

Xcode Problem Overview


The new Xcode 7.3 passing the parameter via addTarget usually works for me but in this case it's throwing the error in the title. Any ideas? It throws another when I try to change it to @objc

Thank you!

cell.commentButton.addTarget(self, action: #selector(FeedViewController.didTapCommentButton(_:)), forControlEvents: UIControlEvents.TouchUpInside)

The selector it's calling

func didTapCommentButton(post: Post) {
}

Xcode Solutions


Solution 1 - Xcode

In my case the function of the selector was private. Once I removed the private the error was gone. Same goes for fileprivate.

In Swift 4
You will need to add @objc to the function declaration. Until swift 4 this was implicitly inferred.

Solution 2 - Xcode

You need to use the @objc attribute on didTapCommentButton(_:) to use it with #selector.

You say you did that but you got another error. My guess is that the new error is that Post is not a type that is compatible with Objective-C. You can only expose a method to Objective-C if all of its argument types, and its return type, are compatible with Objective-C.

You could fix that by making Post a subclass of NSObject, but that's not going to matter, because the argument to didTapCommentButton(_:) will not be a Post anyway. The argument to an action function is the sender of the action, and that sender will be commentButton, which is presumably a UIButton. You should declare didTapCommentButton like this:

@objc func didTapCommentButton(sender: UIButton) {
    // ...
}

You'll then face the problem of getting the Post corresponding to the tapped button. There are multiple ways to get it. Here's one.

I gather (since your code says cell.commentButton) that you're setting up a table view (or a collection view). And since your cell has a non-standard property named commentButton, I assume it's a custom UITableViewCell subclass. So let's assume your cell is a PostCell declared like this:

class PostCell: UITableViewCell {
    @IBOutlet var commentButton: UIButton?
    var post: Post?

    // other stuff...
}

Then you can walk up the view hierarchy from the button to find the PostCell, and get the post from it:

@objc func didTapCommentButton(sender: UIButton) {
    var ancestor = sender.superview
    while ancestor != nil && !(ancestor! is PostCell) {
        ancestor = view.superview
    }
    guard let cell = ancestor as? PostCell,
        post = cell.post
        else { return }

    // Do something with post here
}

Solution 3 - Xcode

Try having the selector point to a wrapper function, which in turn calls your delegate function. That worked for me.

cell.commentButton.addTarget(self, action: #selector(wrapperForDidTapCommentButton(_:)), forControlEvents: UIControlEvents.TouchUpInside)

-

func wrapperForDidTapCommentButton(post: Post) {
     FeedViewController.didTapCommentButton(post)
}

Solution 4 - Xcode

As you know selector[About] says that Objective-C runtime[About] should be used. Declarations that are marked as private or fileprivate are not exposed to the Objective-C runtime by default. That is why you have two variants:

  1. Mark your private or fileprivate method declaration by @objc[About]
  2. Use internal, public, open method access modifier[About]

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionEchizzleView Question on Stackoverflow
Solution 1 - XcodeShaked SayagView Answer on Stackoverflow
Solution 2 - Xcoderob mayoffView Answer on Stackoverflow
Solution 3 - XcodepfjView Answer on Stackoverflow
Solution 4 - XcodeyoAlex5View Answer on Stackoverflow