0

I recently started to update my app to Swift 2.0, but I've run into a problem that has a hundred different answers on SO, none of which seemed to be related to my problem.

(This worked before updating the application to Swift 2.0), but I havn't been able to find any changes made to tap gesture recognisers?

Here's the full error I'm getting:

[Stocks.SidePanelViewController proFormulaTapGesture]: unrecognized selector sent to instance 0x14f06ae00

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Stocks.SidePanelViewController proFormulaTapGesture]: unrecognized selector sent to instance 0x14f06ae00'

*** First throw call stack: (0x1830904d0 0x197afff9c 0x1830971ec 0x183094188 0x182f9920c 0x188c3c58c 0x188899b50 0x18872d104 0x188c3bc30 0x1886ed28c 0x1886eb3ac 0x18872b088 0x18872a5ec 0x1886fb908 0x1886f9fac 0x183047d6c 0x183047800 0x183045500 0x182f75280 0x18e0ec0cc 0x188762df8 0x100158184 0x1983428b8) libc++abi.dylib: terminating with uncaught exception of type NSException

It's a simple tap gesture. But it seems to not recognize the selector anymore.

Here's the code I use to set up the recognizer:

let proFormulaTap = UITapGestureRecognizer()

proFormulaTap.addTarget(self, action:"proFormulaTapGesture")
proFormulaView.addGestureRecognizer(proFormulaTap)

Here's the function I'm trying to run:

func proFormulaTapGesture() throws {
        print("proFormulaTapGesture")
        selectView(proFormulaView)
        selectedMenuItem = 0
        Formula = 1
        menuTabBarController.selectedIndex = 0
        navigationController?.navigationBar.topItem!.title = "BASIC FORMULA"
        try (menuTabBarController.viewControllers as! [SuggestionsViewController])[0].loadSuggestions()
    }

However since it never prints "proFormulaTapGesture" in the console. I definitely think the error occurs before the function. Also suggested by the error mentioning the selector.

Obviously try/catch has been added to the function since the update to Swift 2.0, but nothing has been changed in the setup of the tapGestureRecognizer.

I tried removing the "throws" and try from the function, but the problem perists. I also tried making a button instead of a tap gesture recognizer. But I'm still getting the same error, suggesting it might be a problem with the selector (function) and not the tap gesture / button. However all my other buttons in my app works just fine?

I also tried to rename the selector/function, and the tap gesture recognizer. Still the same.

The original Code was programmed in Swift, not Obj-C. The throws and try, was added during Apple's code conversion to Swift 2.0

Any help to why this is suddenly broken would be greatly appreciated.

Thanks!

Community
  • 1
  • 1
Mark L
  • 759
  • 2
  • 12
  • 29
  • Did you just try commenting that try statement and removing that 'throws' - simply to check if that is causing the issue? – Shripada Jul 27 '15 at 04:36
  • @nshebbar I just tried that, still the same error. Thank you for the suggestion though! Really appreciate it! – Mark L Jul 27 '15 at 04:39
  • Hmm, OK, also try changing the name of the function also. – Shripada Jul 27 '15 at 04:42
  • @nshebbar I just updated my post with that and a couple of other attempts I've made to fix it. Yeah :( – Mark L Jul 27 '15 at 04:43
  • Try fully qualified function that also takes gestureRecogniser as argument: func proFormulaTapGesture(recognizer: UITapGestureRecognizer) throws . and to match - proFormulaTap.addTarget(self, action:"proFormulaTapGesture:") – Shripada Jul 27 '15 at 04:51
  • @nshebbar I tried making a new function that only had a print in it. Still the same issue. – Mark L Jul 27 '15 at 05:07
  • `@objc func proFormulaTapGesture()` & [Using Swift with Cocoa and Objective-C](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216-CH2-ID0). – zrzka Jul 27 '15 at 07:29
  • @robertvojta It's not Obj-C code. – Mark L Jul 27 '15 at 07:43
  • @MarkL but the _UIKit_ is ... `UITapGestureRecognizer` (Obj-C) is calling your Swift code. – zrzka Jul 27 '15 at 07:50
  • Forgot to mention that `@objc` is required when the method is `private`. These are not automatically exposed to Objective-C. – zrzka Jul 27 '15 at 08:09

1 Answers1

5

The issue may be that UIKit does not know how to call a selector that throws, at least in this context.

Remember that UIKit will be calling this function in response to a tap action. It is not accustomed to calling a function that throws; obviously the throw must alter the method's call signature as far as the Objective-C runtime is concerned. Beyond that, if your function proFormulaTapGesture did throw an error, what would UIKit do with the error even if it could catch it?

I'm not sure where you added do/try/catch. But unless it was inside your action function proFormulaTapGesture, I do not see how it could be relevant. I believe that you will need to implement the try/catch error handling inside proFormulaTapGesture, so that it effectively swallows the error, so that proFormulaTapGesture does not throw.

I just created a quick test project myself and found that a throwing target action gives the same "Unrecognized selector" error you got:

override func viewDidLoad() {
    super.viewDidLoad()

    self.tapper = NSClickGestureRecognizer(target: self, action: "didClick")
    self.view.addGestureRecognizer(self.tapper!)
}

func didClick() throws {
    print("did click")
}

2015-07-27 00:16:43.260 Test1[71047:54227175] -[Test1.ViewController didClick]: unrecognized selector sent to instance 0x6000000c5010

...

I believe you will need to rework your code to handle the error internally in your action function so that it does not throw - something like this:

func proFormulaTapGesture() {
    print("proFormulaTapGesture")
    selectView(proFormulaView)
    selectedMenuItem = 0
    Formula = 1
    menuTabBarController.selectedIndex = 0
    navigationController?.navigationBar.topItem!.title = "BASIC FORMULA"
    do {
        try (menuTabBarController.viewControllers as! [SuggestionsViewController])[0].loadSuggestions()
    } catch {
        // Handle the error here somehow
    }
}

If you are unable to handle the error within proFormulaTapGesture, you'll have to be more creative with passing it out of the function (think NSNotification, etc...).


To be clear, I created a new Single View iOS application in Xcode 7 beta 4, and used this exact code as my ViewController. No other changes. The app compiles and works just fine in the iOS simulator.

@robertvojta also points out that if didTap below is marked private, @objc is also required to avoid a dynamic dispatch crash.

import UIKit

class ViewController: UIViewController {

    var tapper: UITapGestureRecognizer?

    override func viewDidLoad() {
        super.viewDidLoad()

        self.tapper = UITapGestureRecognizer(target: self, action: "didTap")
        self.view.addGestureRecognizer(self.tapper!)
    }

    func didTap() {
        print("did Tap")
    }

}

(Note I also tested on an OS X project, which is the code I used earlier in the answer).

justinpawela
  • 1,968
  • 1
  • 14
  • 18
  • _I'm not 100% sure, I believe, ..._ Did you test your code? You have to use `@objc` otherwise you'll still get _Unrecognized selector ..._. – zrzka Jul 27 '15 at 07:31
  • ill try this, but as mentioned in the post, I already tried making a simple function only containing a print, that didn't throw. And it still didn't work. But I'll try again to make sure :) Thanks for the answer. – Mark L Jul 27 '15 at 07:38
  • Yeah I'm still getting the same error with your edit. – Mark L Jul 27 '15 at 07:42
  • I figured it out. And this answer was right, despite my last error. After I cleaned the project, deleted old log files and such. Restarted Xcode and ran it again, this worked fine. :) Thank you so much! – Mark L Jul 27 '15 at 08:00
  • @user2194039 deleted my answer. To make your answer better, just add note about `@objc`, which is needed when the method is private (these are not exposed to ObjC by default). `private func didTap()` will not work without `@objc`. – zrzka Jul 27 '15 at 08:07
  • @robertvojta Thanks and I added a little more code to show clearly exactly what I ran that worked. And really, like I mentioned earlier, `@objc` was my first thought as well. I was using language like "_not 100% sure_" because my tested code worked, but I don't have any documentation to back it up. – justinpawela Jul 27 '15 at 08:09
  • @robertvojta I revised the note about `@objc` based on your comment (I also tested it and found it to be spot on ;) ). Thanks for that bit of info regarding `private` and `@objc`; that does explain some things for me. – justinpawela Jul 27 '15 at 08:14
  • @user2194039 safe approach is probably to mark all these dynamic things with `@objc` even if they're exposed automatically to Objective-C. Doesn't hurt and if you mark it with `private` in the future, it will not cause crash. Also it acts like a tag/comment - be aware, this one is going to be used from Objective-C framework, ... – zrzka Jul 27 '15 at 08:17
  • Just FYI - Release notes quote: _Declarations marked private are not exposed to the Objective-C runtime if not otherwise annotated. IB outlets, IB actions, and Core Data managed properties remain exposed to Objective-C whatever their access level. If you need a private method or property to be callable from Objective-C (such as for an older API that uses a selector-based callback), add the @objc attribute to the declaration explicitly._ – zrzka Jul 27 '15 at 08:20
  • @robertvojta Agreed 1000x and now I know why. Thanks! – justinpawela Jul 27 '15 at 08:20