24

For the most part, Swift is a huge improvement over Objective-C in terms of type safety. One glaring exception is selectors. In Objective-C, using the expression @selector(notARealSelector:) will give a compiler warning. The Swift equivalent, Selector("notARealSelector:") will always compile but will fail at runtime.

Is there a typesafe way to use selectors in Swift, so I can work with Objective-C APIs that require them?

I have a lot of NSNotification observers in my app and would like to have some kind of compile-time checking that I'm not making typos in my selectors.

Edit: The specific use case is NSNotificationCenter.addObserver.

Bill
  • 44,502
  • 24
  • 122
  • 213
  • Can you give an example of how you're using the `@selector()` block currently? Do the objects you're calling those methods on expose (either through protocol conformance or subclassing) the methods at any time? You can use `myDelegate?.scrollViewDidScroll?(...)`, but this requires the delegate at least conform to the protocol so the compiler has a chance at determining if it's even possible for that method to be there. – Craig Otis Jun 09 '14 at 14:55
  • Like I said in the question, `NSNotificationCenter.addObserver` – Bill Jun 09 '14 at 14:58
  • Another common use of @selector is the target/action used by `UIControl` and subclasses. e.g. `button.addTarget(self, action: "doTap", forControlEvents: .TouchUpInside)` - frustrating that the swift compiler cannot warn of mistakes due to typos or method names changing. – Jason Moore Oct 13 '14 at 17:38
  • not a specific answer to your question about general type-safety with swift selectors, but if you want to maintain type safety in this specific case, there is a version of this notification call that takes a block instead of a selector: `public func addObserverForName(name: String?, object obj: AnyObject?, queue: NSOperationQueue?, usingBlock block: (NSNotification) -> Void) -> NSObjectProtocol`. see https://www.codefellows.org/blog/how-to-implement-an-nsnotification-observer-in-swift – Tom Lubitz Oct 28 '15 at 19:11

3 Answers3

4

Typesafe selectors were just released in Xcode 7.3 beta 4:

let sel = #selector(insertSubview(_:aboveSubview:)) // sel has type

Selector is now a first class citizen and comes with some nice Swift compiler warnings. If needed you can still pass in a string:

let sel = Selector("propertyName")

See a much more complete answer here: @selector() in Swift?

Xcode Release Notes: http://adcdownload.apple.com/Developer_Tools/Xcode_7.3_beta_4/Xcode_7.3_beta_4_Release_Notes.pdf

Community
  • 1
  • 1
Andrew
  • 1,279
  • 2
  • 13
  • 19
  • 1
    @Bill See also my discussion here: http://stackoverflow.com/a/35658335/341994 This explains thoroughly the new `#selector` syntax. It only took them, what, two years since you (rightly) asked your question to come up with this... :) In my opinion this hole in the type-safety has been, all this time, a massive contradiction of all that Swift is supposed to stand for. Now the hole has been filled. – matt Mar 22 '16 at 15:01
1

Use the Swift notion of optionals as:

if let result = object.notARealSelector?(/* args */) {
   // Use Result
}

where the ? used following notARealSelector with return false to if when there is no such method defined on the type of object.

There is a caveat for optional protocol requirements:

Optional protocol requirements can only be specified if your protocol is marked with the @objc attribute. Even if you are not interoperating with Objective-C, you need to mark your protocols with the @objc attribute if you want to specify optional requirements.

But since your are asking about optional methods in the first place, you must be talking about this in the Objective-C context.

rickster
  • 124,678
  • 26
  • 272
  • 326
GoZoner
  • 67,920
  • 20
  • 95
  • 145
  • Yes, as I said in the question, I'm trying to pass selectors to `NSNotificationCenter` – Bill Jun 09 '14 at 15:52
  • 2
    Also, treating a function as an optional works only for optional protocol methods — you can't do `object.notARealSelector` unless `object` is a variable/constant of protocol type and said protocol is an `@obcj` protocol that declares `notARealSelector` as an `@optional` method. – rickster Jul 02 '14 at 18:19
-1

You can use reflection to get the names of an object's properties as strings, as summed up in this answer, but that won't make the connection between an identifier and its string name the way @selector does in ObjC. Some creative use of reflection might work around that problem, but you might also do well to file a bug.

Community
  • 1
  • 1
rickster
  • 124,678
  • 26
  • 272
  • 326