1

In Swift, how can I test if a given selector is the noop: selector?

// Compiler error: Cannot find `noop` in score
override func doCommand(by selector: Selector) {
  if selector == #selector(noop(_:)) {
  }
}

When handling keyboard events in an AppKit application, NSResponder.doCommand(by:) will be called with a Selector. If the key chord does not map to a known action, then the selector noop: will be returned.

In Objective-C, you can test for this with @selector(noop:), however in Swift if you attempt to use #selector(noop(_:)) then the compiler complains it cannot resolve that selector. When this happens, you generally just have to prefix the selector with the class it belongs in, ex: #selector(NSResponder.moveLeft(_:)).

However, I cannot find where noop: is defined. No amount of searching through the header files reveals a suitable match.

kennyc
  • 5,490
  • 5
  • 34
  • 57
  • 1
    Is this a `NSTextView` thing, related to https://stackoverflow.com/q/9150773/3141234 ? – Alexander Nov 24 '21 at 22:53
  • 1
    Ok, thanks for double-checking. It's a very small thing so I guess I'll just use `NSSelectorFromString`. Was only trying to be "Swifty". Happy to mark this the answer if you post it as such. (And yeah, somewhat related to that other question. Implementing a custom collection-like view and want to handle keyDown events properly.) – kennyc Nov 24 '21 at 23:20
  • 1
    Posted, but I would suggest you hold off on marking it as accepted. Perhaps somebody has the real answer. – Alexander Nov 24 '21 at 23:26
  • 1
    By the way, it's entirely possible that there just is no method called `noop:`. Sending selectors up the responder chain is pretty conceptually similar to sending strings to an `NSNotificationCenter`. The selector is like the notification name, and it just so happens that one of the objects in the chain has to `respondTo:` that selector (and the system will then send it that message) – Alexander Nov 24 '21 at 23:31
  • 1
    Agreed. Coming from Objective-C, it's normal to do things like converting strings to and from classes or selectors. But that didn't seem particularly Swift'y to me so I was curious if I had overlooked where that selector was defined, as all the normal NSResponder ones are. Sounds like it might not be so I'll just use the `NS` calls. Thank you. – kennyc Nov 24 '21 at 23:46
  • 2
    This kind of comes with the territory; the entire NSResponder pattern isn't very "Swifty", because it's completely untyped. I don't think that's necessarily a bad thing. Presumably, one can envision a stronger-typed counterpart to the responder chain, which uses protocols to be more specific about the supported messages and the parameter types involved. Though I think this is an area where dynamism is well applied, because the objects should know so little about each other, that trying to nail their types down with protocols or such might actually be undesirable. – Alexander Nov 24 '21 at 23:53

1 Answers1

4

I can confirm that there's no headers in the entire MacOSX.sdk that define a noop: method.

Work-around: you can use NSSelectorFromString("noop:").

Of course, this circumvents the correctness-checking the compiler does for you with the more special #selector(Class.method) syntax.

Alexander
  • 59,041
  • 12
  • 98
  • 151
  • Seems like `Selector` in Swift is also `ExpressibleByStringLiteral` so the one could simply compare `selector == "noop:"` without going through Objective-C `NSSelectorFromString` – pronebird Nov 25 '21 at 09:47
  • Although the docs don't mention it, it's deprecated: "Use of string literal for Objective-C selectors is deprecated; use `#selector` instead" – Alexander Nov 25 '21 at 13:32