Fixing The «Unrecognized Selector Sent to Instance» Error in Xcode
The “Unrecognized selector sent to instance” is an annoying error in Xcode. With Swift’s syntax improvements this error occurs less often, but it still pops up every now and then. How do you solve it?
In this app development tutorial, you’ll learn to debug Unrecognized selector sent to instance. We’ll discuss where the error comes from and how to solve it.
Debugging is part of becoming an effective iOS developer. Fixing errors is a great way to learn more about iOS development, so take your time to go in-depth.
Table of Contents
Understanding Selectors in Swift and Objective-C
Selectors are a relic from the past, from Objective-C. With the latest Swift, you shouldn’t run into that Unrecognized selector sent to instance problem that often.
But… what’s a selector?
A selector is essentially the name of a function. You “select” which function to execute on an object based on the selector. You can safely assume that a selector is identical to the name of a function.
Selectors come from Objective-C:
- SEL or Selector is the variable type of a selector
- In Objective-C, you could create a selector with the @selector() function or with NSSelectorFromString(@”methodName”)
- In Objective-C, you could execute a selector on an object with the performSelector: function, and test whether an selector exists with respondsToSelector:
- In Swift, we use #selector() to indicate a function to be called, with the target-action mechanism
So why does this Unrecognized selector sent to instance error then show up in Swift code?
A large part of the Cocoa Touch SDK, i.e. the framework code you use to create native iOS apps, is still written in Objective-C! Especially the UIKit framework, the one that puts views, images and buttons on your iPhone screen, is written in Objective-C.
Many Objective-C classes use the target-action mechanism. It’s a special way of calling functions. Check this out:
let gesture = UITapGestureRecognizer(target: self, action: #selector(onTapGesture(_:)))
gesture.numberOfTapsRequired = 1
view.addGestureRecognizer(gesture)
@objc func onTapGesture(_ gesture:UITapGestureRecognizer)
{
print(“Tapped!!”)
}
In the above example, you’re creating a gesture recognizer that executes the onTapGesture(_:) function when you tap the screen once. This code can be part of a UIViewController, for instance.
Do you see the target and action parameters in the UITapGestureRecognizer initializer function?
With the target-action design pattern you’re essentially saying: “When this happens, execute that function.”
The target is the object the function is called upon, and the action is the function that should be called. In many cases you can also designate an event with the for parameter, for instance to execute the action when the value on a textfield changes.
Now you know where selectors come from!
Using Selectors in Swift 2.2 to 5.0
Selectors have been part of the Swift programming language since it was invented, because selectors originally come from Objective-C. Because the iOS SDK is partly written in Objective-C, you still work with selectors in Swift.
Before Swift 2.2 you used Objective-C selectors like this:
let gesture = UITapGestureRecognizer(target: self, action: Selector(“onTapGesture:”))
See how the Selector initializer takes one argument, a string, that contains the name of the function that should be called? Now you’re starting to see where this might go wrong…
The string with the selector can’t be checked at runtime! The Swift compiler can’t check whether the function actually exists before you run your app, so if you’ve written the selector incorrectly, you’ll get that Unrecognized selector sent to instance.
If Swift could have checked whether the selector exists when compiling the app, it would have shown you the Use of unresolved identifier error.
On top of that, the syntax for writing out selectors is confusing. Ever noticed those colons, parentheses and underscores?
- When a function has no parameters, you write onTapGesture()
- When a function has one unnamed parameter, you write onTapGesture(_:)
- When a function has multiple unnamed parameters, you write onTapGesture(_:_:), i.e. one _: for every unnamed parameter
- When a function has one named parameter, you write the name of the parameter like this onTapGesture(gesture:)
- When a function has multiple named parameters, you write the names of the parameters, like this onTapGesture(gesture:view:event:)
Objective-C doesn’t have parentheses for function names, because of its bracket-style messaging syntax, so you wouldn’t write them in the selector string: onTapGesture:.
Quick Tip: If you want to know the selector or function name of the current function, use print(#function) in Swift.
So… here’s the kicker: since Swift version 2.2, selectors can be checked at during compilation thanks to a new syntax:
let gesture = UITapGestureRecognizer(target: self, action: #selector(onTapGesture(_:)))
Instead of using a string, the new #selector() syntax refers directly to the function that should be called when the event takes place.
Because of this, Swift can pre-check during compilation whether a function exists, and inform you of an error before you run your app. The result? Less time debugging your app!
In Swift 4 and later you’ll have to designate functions that you want to use as selectors for target/action-based interactions with @objc. This is because Swift 4 doesn’t automatically make Swift functions available to Objective-C.
The quirky way of writing functions with underscores and colons remains, however. Fortunately, you can use code autocompletion to get hints, like this:
Simply place your cursor between the parentheses of #selector() and press the Esc-key. A dropdown menu shows up, and you can scroll to the right function.
Still got the error? Let’s get to fixing it…
Fixing «Unrecognized Selector Sent To Instance»
Alright, enough with the explaining! You now know where the Unrecognized selector sent to instance comes from and what causes it – so let’s get to fixing.
If you’re coding Objective-C, or working with Swift 1.0, 2.0 or 2.1. (i.e. before Swift 2.2), this solution is for you.
With Swift 2.2, the error shouldn’t happen, because it’s checked at compile-time. If you still run into issues with a selector, check this tutorial and use code completion to get to the right selector.
First off, let’s be real:
- If you’re a beginner coder, switch to learning Swift. It’s much easier than Objective-C, it’s widely adopted, and it’s the designated programming language for the future of iOS.
- If you’re still on a Swift version before 5.0, make sure to upgrade! Sure, it’s a bit of work, but Swift 5.0 has a better, more productive syntax and is much more stable than previous Swift versions.
If you’re debugging an Objective-C library, or you can’t upgrade to the latest Swift version, do this:
- First, check that the selector is called on the right object. The function name should exist on that object. You’d typically check the target argument for the target-action design pattern. In many cases, the target should be self.
- Then, check the function name. Make sure it uses the exact uppercase and lowercase letters – the selector must be identical to the function in order to work.
- Then, check the function parameters. They must be identical too! This is where those quirky colons and underscores come in, so you’ll need to use the right format. You can use print(#function) (Swift >= 2.2), or print(__FUNCTION__) (Swift < 2.2), or NSLog(@"%s", __PRETTY_FUNCTION__) in Objective-C to find out the correct selector.
In some cases, Xcode won’t take you to the offending line after a crash. When your app crashes you won’t see the line of code that caused the crash, but instead you’ll be taken to your AppDelegate class. How do you find the incorrect line of code now?
If you look at the entire error output, you’ll see that it has caused an exception:
*** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[MyApp.MyViewController viewDidLoad:]: unrecognized selector sent to instance 0xDEADBEEF’
You can catch that exception with a clever trick:
- First, open the Breakpoint Navigator on the left of Xcode
- Then, click on the +-button in the bottom-right corner
- Then, choose Exception Breakpoint
- Finally, a new breakpoint should show up in the navigator
Run your app and trigger the error. You should now see the line that is incorrect, because thanks to the breakpoint your code “breaks” on the line of code that threw the exception.
In rare cases, especially if you haven’t managed memory correctly, you can get the Unrecognized selector sent to instance error on an object that shouldn’t have received that selector in the first place.
This can happen when you incorrectly retain and/or release objects, causing an object to be allocated on a memory address that was previously associated with another object. Because this new object doesn’t have the selector you’re trying to call, you’ll see the error. You can solve this by tracing the memory leak.
Further Reading
Alright! Solved the error? Awesome! Now you know what selectors are, how to use them, and why they can cause all sorts of problems…
Related Articles
- How to Start an Ecommerce Business from Scratch? [2021 edition]
- Alexander and Maarten and Their App “Assessments 4 Students”
- Best Questions to Ask in an Interview
- How to Make a Design Portfolio? [Top 15 Tips]
- Combining Network Requests with Combine and Swift
- Top Tips for Responsive Website Design
- App Onboarding: Best Practices for Mobile Apps
- Young App Developers Who Made Millions
- 25 Best Programming Languages for Mobile Apps & Top Mobile App Development Tools & Frameworks
- How to Start Your Own Blog?
Most Popular Posts
App Builder, Instagram’s Kevin Systrom, Before It Was Popular
By Aasif Khan | August 23, 2017
- How Viral Apps Spread
By Abhinav Girdhar | January 4, 2022
8 Advantages of using no-code workflow automation platforms
By Abhinav Girdhar | August 24, 2020
- Working with UserDefaults in Swift
By Aasif Khan | December 8, 2021
- How To: Pass Data Between Views with SwiftUI
By Aasif Khan | November 29, 2021