Swift make protocol extension a Notification observer

Swift Hipster picture Swift Hipster · Oct 8, 2015 · Viewed 8.5k times · Source

Let's consider the following code:

protocol A {
    func doA()
}

extension A {
  func registerForNotification() {
      NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardDidShow:"), name: UIKeyboardDidShowNotification, object: nil)
  }

  func keyboardDidShow(notification: NSNotification) {

  }
}

Now look at a UIViewController subclass that implements A:

class AController: UIViewController, A {
   override func viewDidLoad() {
      super.viewDidLoad()
      self.registerForNotification()
      triggerKeyboard()
   }

   func triggerKeyboard() {
      // Some code that make key board appear
   }

   func doA() {
   }
}

But surprisingly this crashes with an error:

keyboardDidShow:]: unrecognized selector sent to instance 0x7fc97adc3c60

So should I implement the observer in the view controller itself? Can't it stay in the extension?

Following things already tried.

making A a class protocol. Adding keyboardDidShow to protocol itself as signature.

protocol A:class {
   func doA()
   func keyboardDidShow(notification: NSNotification)
}

Answer

James Paolantonio picture James Paolantonio · Jan 19, 2016

I solved a similar problem by implementing the newer - addObserverForName:object:queue:usingBlock: method of NSNotificationCenter and calling the method directly.

extension A where Self: UIViewController  {
    func registerForNotification() {
        NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardDidShowNotification, object: nil, queue: nil) { [unowned self] notification in
            self.keyboardDidShow(notification)
        }
    }

    func keyboardDidShow(notification: NSNotification) {
        print("This will get called in protocol extension.")
    }
}

This example will cause keyboardDidShow to be called in the protocol extension.