Christopher Pickslay
Christopher Pickslay

Reputation: 17762

Selector string from Swift function

Given a function reference, is there a way in Swift to get the name of that function as a string suitable for passing to NSSelectorFromString?

I'd like to wrap NSNotificationCenter's addObserver with a version that takes a function reference instead of a selector string:

addObserver(self, function: someCallback, name: "some notification", object: nil)

But addObserver takes a String selector argument.

Upvotes: 3

Views: 1216

Answers (3)

Rob Napier
Rob Napier

Reputation: 299275

Edit: I'm leaving this here for interest, but it's way too complicated (got wrapped up in how the question was asked, rather than the goal). Beyond the existing block-based approach, there's also this handy extension to it.


I wouldn't do it this way. It's too limiting because it would exclude function literals (anonymous functions).

Instead, I would play this game:

  • Create an dictionary property mapping [String: Void -> ()] (string to function)
  • When you register a new function, make up a unique, random key and store the function you're passed in your dictionary with that key.
  • Register with the selector observer_dispatcher_<key> (or whatever prefix you like).
  • Implement resolveInstanceMethod: to dynamically create any observer_dispatcher_ method you're requested. They can all point to the same IMP, something like:

(assuming this is good Swift; I haven't tried it):

 void _observer_dispatcher(self: AnyObject, _cmd: Selector) {
    let name = // strip the leading stuff off of _cmd
    if let f = self.dispatchTable[name] {
         f()
     }
 }

(This is still pretty sloppy; I'll have to think about the correct correct impl more, but this is the direction I'd go in.)

Upvotes: 0

Christopher Pickslay
Christopher Pickslay

Reputation: 17762

I'd still like to find an answer to my original question, but based on @matt's suggestion, I'm currently using this extension:

extension NSNotificationCenter {

    class func addObserver(function: (NSNotification!) -> (), name: String? = nil, object: AnyObject? = nil, queue: NSOperationQueue? = nil) {
        defaultCenter().addObserverForName(name, object: object, queue: nil, usingBlock: function)
    }

}

Since it implicitly uses defaultCenter() and provides defaults for object and queue, which I'd almost always pass nil for, it allows for a more succinct call:

NSNotificationCenter.addObserver(doSomething, name: "some notification")

I like that it links against the actual function (doSomething), rather than a string representation. It's not a general purpose extension, but I think it covers 90% of the cases where I register for notifications.

Upvotes: 0

matt
matt

Reputation: 534950

You're reinventing an unnecessary wheel. NSNotificationCenter already has an observation method that takes a function (what Objective-C calls a "block"):

addObserverForName:object:queue:usingBlock:

So just use that.

Upvotes: 4

Related Questions