What is the Swift equivalent of respondsToSelector?

Objective CSwiftSelector

Objective C Problem Overview


I've googled but not been able to find out what the swift equivalent to respondsToSelector: is.

This is the only thing I could find (https://stackoverflow.com/questions/24142906/swift-alternative-to-respondstoselector) but isn't too relevant in my case as its checking the existence of the delegate, I don't have a delegate I just want to check if a new API exists or not when running on the device and if not fall back to a previous version of the api.

Objective C Solutions


Solution 1 - Objective C

As mentioned, in Swift most of the time you can achieve what you need with the ? optional unwrapper operator. This allows you to call a method on an object if and only if the object exists (not nil) and the method is implemented.

In the case where you still need respondsToSelector:, it is still there as part of the NSObject protocol.

If you are calling respondsToSelector: on an Obj-C type in Swift, then it works the same as you would expect. If you are using it on your own Swift class, you will need to ensure your class derives from NSObject.

Here's an example of a Swift class that you can check if it responds to a selector:

class Worker : NSObject
{
    func work() { }
    func eat(food: AnyObject) { }
    func sleep(hours: Int, minutes: Int) { }
}

let worker = Worker()

let canWork = worker.respondsToSelector(Selector("work"))   // true
let canEat = worker.respondsToSelector(Selector("eat:"))    // true
let canSleep = worker.respondsToSelector(Selector("sleep:minutes:"))    // true
let canQuit = worker.respondsToSelector(Selector("quit"))   // false

It is important that you do not leave out the parameter names. In this example, Selector("sleep::") is not the same as Selector("sleep:minutes:").

Solution 2 - Objective C

There is no real Swift replacement.

You can check in the following way:

someObject.someMethod?()

This calls the method someMethod only if it's defined on object someObject but you can use it only for @objc protocols which have declared the method as optional.

Swift is inherently a safe language so everytime you call a method Swift has to know the method is there. No runtime checking is possible. You can't just call random methods on random objects.

Even in Obj-C you should avoid such things when possible because it doesn't play well with ARC (ARC then triggers warnings for performSelector:).

However, when checking for available APIs, you can still use respondsToSelector:, even if Swift, if you are dealing with NSObject instances:

@interface TestA : NSObject

- (void)someMethod;

@end

@implementation TestA

//this triggers a warning

@end   


var a = TestA()

if a.respondsToSelector("someMethod") {
   a.someMethod()
}

Solution 3 - Objective C

Update Mar 20, 2017 for Swift 3 syntax:

If you don't care whether the optional method exists, just call delegate?.optionalMethod?()

Otherwise, using guard is probably the best approach:

weak var delegate: SomeDelegateWithOptionals?

func someMethod() {
    guard let method = delegate?.optionalMethod else {
        // optional not implemented
        alternativeMethod()
        return
    }
    method()
}

Original answer:

You can use the "if let" approach to test an optional protocol like this:

weak var delegate: SomeDelegateWithOptionals?

func someMethod() {
  if let delegate = delegate {
    if let theMethod = delegate.theOptionalProtocolMethod? {
      theMethod()
      return
    }
  }
  // Reaching here means the delegate doesn't exist or doesn't respond to the optional method
  alternativeMethod()
}

Solution 4 - Objective C

If the method you are testing for is defined as an optional method in a @objc protocol (which sounds like your case), then use the optional chaining pattern as:

if let result = object.method?(args) {
  /* method exists, result assigned, use result */
}
else { ... }

When the method is declare as returning Void, simply use:

if object.method?(args) { ... }

See:

> “Calling Methods Through Optional Chaining”
> Excerpt From: Apple Inc. “The Swift Programming Language.”
> iBooks. https://itun.es/us/jEUH0.l

Solution 5 - Objective C

It seems you need to define your protocol as as subprotocol of NSObjectProtocol ... then you'll get respondsToSelector method

@objc protocol YourDelegate : NSObjectProtocol
{
    func yourDelegateMethod(passObject: SomeObject)
}

note that only specifying @objc was not enough. You should be also careful that the actual delegate is a subclass of NSObject - which in Swift might not be.

Solution 6 - Objective C

For swift3

If you just want to call the method, run the code below.

self.delegate?.method?()

Solution 7 - Objective C

Functions are first-class types in Swift, so you can check whether an optional function defined in a protocol has been implemented by comparing it to nil:

if (someObject.someMethod != nil) {
    someObject.someMethod!(someArgument)
} else {
    // do something else
}

Solution 8 - Objective C

In Swift 2,Apple introduced a new feature called API availability checking, which might be a replacement for respondsToSelector: method.The following code snippet comparison is copied from the WWDC2015 Session 106 What's New in Swift which I thought might help you,please check it out if you need to know more.

> The Old Approach:

@IBOutlet var dropButton: NSButton!
override func awakeFromNib() {
    if dropButton.respondsToSelector("setSpringLoaded:") {
        dropButton.springLoaded = true
    }
}

> The Better Approach:

@IBOutlet var dropButton: NSButton!
override func awakeFromNib() {
    if #available(OSX 10.10.3, *) {
        dropButton.springLoaded = true
    }
}

Solution 9 - Objective C

For swift 3.0

import UIKit

@objc protocol ADelegate : NSObjectProtocol {

    @objc optional func hi1()
    @objc optional func hi2(message1:String, message2:String)
}

class SomeObject : NSObject {

    weak var delegate:ADelegate?

    func run() {
    
        // single method
        if let methodHi1 = delegate?.hi1 {
            methodHi1()
        } else {
            print("fail h1")
        }

        // multiple parameters
        if let methodHi2 = delegate?.hi2 {
            methodHi2("superman", "batman")
        } else {
            print("fail h2")
        }
    }
}

class ViewController: UIViewController, ADelegate {

    let someObject = SomeObject()

    override func viewDidLoad() {
        super.viewDidLoad()
    
        someObject.delegate = self
        someObject.run()
    }

    // MARK: ADelegate
    func hi1() {
    
        print("Hi")
    }

    func hi2(message1: String, message2: String) {
    
        print("Hi \(message1) \(message2)")
    }
}

Solution 10 - Objective C

Currently (Swift 2.1) you can check it using 3 ways:

  1. Using respondsToSelector answered by @Erik_at_Digit

  2. Using '?' answered by @Sulthan

  3. And using as? operator:

    if let delegateMe = self.delegate as? YourCustomViewController
    {
       delegateMe.onSuccess()
    }
    

Basically it depends on what you are trying to achieve:

  • If for example your app logic need to perform some action and the delegate isn't set or the pointed delegate didn't implement the onSuccess() method (protocol method) so option 1 and 3 are the best choice, though I'd use option 3 which is Swift way.
  • If you don't want to do anything when delegate is nil or method isn't implemented then use option 2.

Solution 11 - Objective C

As I started to update my old project to Swift 3.2, I just needed to change the method from

respondsToSelector(selector)

to:

responds(to: selector)

Solution 12 - Objective C

I just implement this myself in a project, see code below. As mentions by @Christopher Pickslay it is important to remember that functions are first class citizens and can therefore be treated like optional variables.

@objc protocol ContactDetailsDelegate: class {

    optional func deleteContact(contact: Contact) -> NSError?
}

...

weak var delegate:ContactDetailsDelegate!

if let deleteContact = delegate.deleteContact {
    deleteContact(contact)
}

Solution 13 - Objective C

another possible syntax by swift..

 if let delegate = self.delegate, method = delegate.somemethod{
        method()
    }

Solution 14 - Objective C

I use guard let else, so that can do some default stuff if the delegate func is not implemented.

@objc protocol ViewController2Delegate: NSObjectProtocol {
    
    optional func viewController2(controller: ViewController2, didSomethingWithStringAndReturnVoid string: String)
    
    optional func viewController2(controller: ViewController2, didSomethingWithStringAndReturnString string: String) -> String
}

class ViewController2: UIViewController {
    
    weak var delegate: ViewController2Delegate?        

    @IBAction func onVoidButtonClicked(sender: AnyObject){
        
        if (delegate != nil && delegate!.respondsToSelector(Selector("viewController2:didSomethingWithStringAndReturnVoid:"))) {
            NSLog("ReturnVoid is implemented")
            
            delegate!.viewController2!(self, didSomethingWithStringAndReturnVoid: "dummy")
        }
        else{
            NSLog("ReturnVoid is not implemented")
            // Do something by default
        }
    }
    
    @IBAction func onStringButtonClicked(sender: AnyObject){
        
        guard let result = delegate?.viewController2?(self, didSomethingWithStringAndReturnString: "dummy") else {
            NSLog("ReturnString is not implemented")
            // Do something by default
            return
        }
        
        NSLog("ReturnString is implemented with result: \(result)")
    }
}

Solution 15 - Objective C

I guess you want to make a default implementation for delegate. You can do this:

let defaultHandler = {}
(delegate?.method ?? defaultHandler)()

Solution 16 - Objective C

Swift 3:

protocol

@objc protocol SomeDelegate {
    @objc optional func method()
}

Object

class SomeObject : NSObject {

weak var delegate:SomeObject?

func delegateMethod() {

     if let delegateMethod = delegate?.method{
         delegateMethod()
     }else {
        //Failed
     }

   }

}

Solution 17 - Objective C

The equivalent is the ? operator:

var value: NSNumber? = myQuestionableObject?.importantMethod()

importantMethod will only be called if myQuestionableObject exists and implements it.

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
QuestionGruntcakesView Question on Stackoverflow
Solution 1 - Objective CErikView Answer on Stackoverflow
Solution 2 - Objective CSulthanView Answer on Stackoverflow
Solution 3 - Objective ChyouuuView Answer on Stackoverflow
Solution 4 - Objective CGoZonerView Answer on Stackoverflow
Solution 5 - Objective CMatej UkmarView Answer on Stackoverflow
Solution 6 - Objective CJason YuView Answer on Stackoverflow
Solution 7 - Objective CChristopher PickslayView Answer on Stackoverflow
Solution 8 - Objective CtounaobunView Answer on Stackoverflow
Solution 9 - Objective CKakashiView Answer on Stackoverflow
Solution 10 - Objective COhadMView Answer on Stackoverflow
Solution 11 - Objective CchAlexeyView Answer on Stackoverflow
Solution 12 - Objective CBulwinkelView Answer on Stackoverflow
Solution 13 - Objective CJanubView Answer on Stackoverflow
Solution 14 - Objective CdichenView Answer on Stackoverflow
Solution 15 - Objective CZeroOnetView Answer on Stackoverflow
Solution 16 - Objective CetzukView Answer on Stackoverflow
Solution 17 - Objective CBen GottliebView Answer on Stackoverflow