Swift 2 unrecognized selector in tapgesture - swift

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!

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).

Related

How to check if Parse initialized already

So I created a Keyboard app extension that uses Parse to get some needed data. I initialized Parse after the keyboard loads like this:
func connectParser() {
// Initialize Parse
let configuration = ParseClientConfiguration {
$0.applicationId = "BlahBlahBlah123456"
$0.clientKey = "BlahBlahBlah123456"
$0.server = "https://parseapi.back4app.com"
}
Parse.initialize(with: configuration)
}
note: Initialization is normally done in AppDelegate, but since this is an App Extension I did it in the main InputView that gets loaded first
The problem occurs when the user switches out of the keyboard after loading it and then tries to switch back to the keyboard. The extension crashes and I get the following error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Parse is already initialized.'
How do I check to see if Parse is initialized already so I don't re-initialize it and cause a crash?
So I didn't find any real solutions to this anywhere, but I did find my own solution and I'll keep it here and answer my own question in case anyone else runs into this:
Basically I check to see if Parse.currentConfiguration() == nil
if it does, then I know Parse hasn't been initialized yet. I don't know if this is the best way of going about this, so if you have a better alternative, please add it as an answer.
Final function looks like this:
func connectParse() {
if Parse.currentConfiguration() == nil {
// Initialize Parse
let configuration = ParseClientConfiguration {
$0.applicationId = "BlahBlahBlah123456"
$0.clientKey = "BlahBlahBlah123456"
$0.server = "https://parseapi.back4app.com"
}
}
}

Swift:"Unrecognized selector sent to instance", Xcode 9.4.1

Can anyone help me with this?
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate=self
tableView.dataSource=self
searchBar.autocorrectionType = .yes
searchBar.delegate=self
searchBarView.translatesAutoresizingMaskIntoConstraints=false
let tap:UIGestureRecognizer = UITapGestureRecognizer(target: self, action: Selector(("tapCancel:")))
searchBarView.addGestureRecognizer(tap)
tableView.addGestureRecognizer(tap)
tableView.isUserInteractionEnabled=true
}
...
func tapCancel(sender:UITapGestureRecognizer){
hideSearchBarView()
isSearchon=false
print("Tap cancel!")
}
Every time I tap the View, it crash.
"unrecognized selector sent to instance 0x7feb85d109e0"
I will appreciate any help!
Thanks!
I believe the reason is due to how method names get mapped between Swift and Objective-C, which underlies the Cocoa implementation and the whole target/action mechanism.
In your case, the Swift method:
#objc func tapCancel(sender:UITapGestureRecognizer)
...corresponds to the Objective-C selector:
-tapCancelWithSender:
Note: In order to work with the target/action paradigm (i.e., called by means of a selector), the method needs to be declared as #objc. The alternative attribute #IBOutlet (for use in conjunction with Interface Builder) also supports this. (tip of the hat to #rmaddy)
In order to remove the "withSender" part and get a selector that matches tapCancel:, you need to tell Swift to remove the argument label sender, like this:
func tapCancel(_ sender:UITapGestureRecognizer) // Notice the underscore (_)
Also, in line with the comment by #dan, perhaps you can use:
#selector(self.tapCancel(_:))
or more succinctly, as pointed by (thanks again) #rmaddy, just:
#selector(tapCancel)
(Xcode will try to autocomplete it to #selector(tapCancel(_:)), but the shorter syntax works as well and the method name is highlighted)
I was not familiar with the Selector() syntax you used, so I tried playing a bit with it, and behold:
(Selector does not match any method the compiler can "see").
(After adding "withSender", the compiler can match the method, but it suggests using the better #selector(... syntax).
As #rmaddy also pointed out in the comments, using the shorter #selector(doSomething) syntax (no colons, no underscore, no self) also does away with the problem of whether "withSender" is needed or not.
use
let tap:UIGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapCancel))
instead of
let tap:UIGestureRecognizer = UITapGestureRecognizer(target: self, action: Selector(("tapCancel:")))
And add #objc before method tapCancel()

How can you mark a method that it is fatal, like `fatalError`?

We have a special logging function that represents a particular type of crash, and as such, internally it calls fatalError. However, unlike fatalError, when we use it in a guard statement, it still complains we haven't exited the scope of the guard.
I checked the source code and fatalError returns -> Never so I tried that, but that didn't work either. I also didn't see any attributes applied to it that may be telling the compiler 'this is the end of the road'.
So is it possible to have your own fatalError-like method?
func myFatalError(memberName:String = #function) -> Never {
let message = "Failed in \(memberName)"
NSLog(message)
fatalError(message)
}
As pointed out by others, the issue was something on my machine specifically. The following (returning 'Never') works as expected.
func myFatalError() -> Never {
print("Doing something fatal")
}

Erratic error in SnapKit

The following is my viewDidAppear() method with which I'm trying to prototype a feature for my app. The graphicsView instance variable is bound via the storyboard to an instance of a subclass of NSView I've written that in turn is enclosed in an NSScrollView within an NSSplitView. This code is within the view controller for that view.
override func viewWillAppear() {
super.viewWillAppear()
let red = CGColor.init(red: 1, green: 0, blue: 0, alpha: 1)
self.view.layer?.backgroundColor = red
let box = NSTextView()
self.graphicsView.addSubview(box)
box.snp.makeConstraints { (make) -> Void in
make.edges.equalTo(self.graphicsView).inset(NSEdgeInsetsMake(100, 100, self.graphicsView.bounds.height - 200, self.graphicsView.bounds.width - 300))
}
box.textStorage?.append(NSAttributedString(string: "Hello Sailor"))
box.alignCenter(self)
}
When executed, I get the error Cannot form weak reference to instance (0x6000001224e0) of class NSTextView. It is possible that this object was over-released, or is in the process of deallocation. along with the usual EXC_BAD_INSTRUCTION fault on the closing bracket of the trailing closure for the constraints.
As far as I can see, the NSTextView will be strongly retained by box, and so I'm at a loss to see the source of the error. The error shows up at the first line of ConstraintItem.init(target: AnyObject?, attributes: ConstraintAttributes). Per the instructions in the readme I'm posting here; can someone on the SnapKit team perhaps shed any additional light on the error? (The app works normally if I remove the box-related code.)
Added information:
The exception happens at line 37 of ConstraintItem.swift, which is self.target = target. I set a breakpoint right before that line and executed e target in the debugger; here's what I got:
(lldb) e target
(AnyObject?) $R1 = (instance_type = 0x0000608000164c80) {
instance_type = 0x0000608000164c80 {
AppKit.NSTextView = {
baseNSText#0 = <extracting data from value failed>
}
title = "some random text"
minimumWidth = 100
}
}
I found several answers.
How you search Google remains important. I varied my searches some more and came upon this here on SO, the short version of which is that it says you can't form a weak reference specifically to NSTextView and includes a link to explanatory Clang documentation.
Perhaps more interestingly, I also found the answer for "erratic" errors I mentioned in the title - one of the machines I develop on turns out to have Swift 3.1, but the other has 3.0.2. The more recent version does not exhibit the error forming the weak link, suggesting Apple has upgraded the NSTextView implementation.

Swift 2: Selector in UIPinchGestureRecognizer: How to access func from another class

I'm following this class on Swift and building apps.
At 43:30 in the video, the instructor teaches how to set up a UIPinchGestureRecognizer, which takes in a function from another file in its Selector.
This is the code the instructor uses:
#IBOutlet weak var faceView: FaceView! {
didSet {
faceView.addGestureRecognizer(UIPinchGestureRecognizer(target: faceView, action: #selector(FaceView.changeScale(_:))))
updateUI()
}
}
I get 2 errors:
Expected expression in list of expressions,
and:
Expected ',' separator.
I have tried changing #selector to Selector with no luck.
the function changeScale:
func changeScale(recognizer: UIPinchGestureRecognizer)
{
switch recognizer.state {
case .Changed, .Ended:
scale *= recognizer.scale //scale is the displayed image scale
recognizer.scale = 1.0
default:
break
}
If I surround the Selector argument with quotes, the app crashes when I pinch, giving the following error:
unrecognized selector sent to instance.
I am running Xcode 8.1(8B62) and a iPhone 5 simulator on MacBook Air (Ver 100.11.6)
This is the code I used that works
faceView.addGestureRecognizer(UIPinchGestureRecognizer(target:faceView,
action:#selector(FaceView.changeScale(recognizer:))))
When you pinch the face, make sure click "Option + Left Click + Movement on your mousepad". I make the mistake of just pressing "Option + Movement on the mousepad" and it does not work.
Hope it helps.
As can be seen in the comments above, the Xcode version is 7.2 and the #selector syntax was introduced in Xcode 7.3 and therefore not available here. This just means that you should be able to use the "old" Selector syntax.
The difference is that you just give pass a strings to the Selector with the name of your function and then a : for each of the parameters your function requires. You require one parameter (the recognizer) so in your case the string looks like this:
"changeScale:"
So you'd use:
Selector("changeScale:")
And you end up with:
#IBOutlet weak var faceView: FaceView! {
didSet {
faceView.addGestureRecognizer(UIPinchGestureRecognizer(target: faceView, action: Selector("changeScale:")))
}
}
As you can see, this is error prone...one typo and kaboom! Which is why the new #selector syntax is a fine improvement...sorry...not trying to rub it in.
Hope this helps you.