Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trailing where clause for extension of non-generic type

I have the following code:

func registerNotification(name:String, selector:Selector)
{
    NSNotificationCenter.defaultCenter().addObserver(self, selector: selector, name: name, object: nil)
}

func registerKeyboardNotifications()
{
    let isInPopover = navigationController?.popoverPresentationController != nil
    let ignore = isInPopover && DEVICE_IS_IPAD
    if !ignore {
        registerNotification(UIKeyboardWillShowNotification, selector: Selector("keyboardWillShow:"))
        registerNotification(UIKeyboardWillHideNotification, selector: Selector("keyboardWillHide:"))
    }
}

in an extension to UIViewController. This code is reused by many viewcontroller to register for keyboard notifications. However with Swift 2.2 it produces a warning. I like the new #selector syntax but not sure how to implement it in this case.

I think the correct solution is to make a protocol and extend UIViewController only for instances that conform to that protocol. My code so far:

@objc protocol KeyboardNotificationDelegate
{
    func keyboardWillShow(notification: NSNotification)
    func keyboardWillHide(notification: NSNotification)
}

extension UIViewController where Self: KeyboardNotificationDelegate
{
    func registerKeyboardNotifications()
    {
        let isInPopover = navigationController?.popoverPresentationController != nil
        let ignore = isInPopover && DEVICE_IS_IPAD
        if !ignore {
            registerNotification(UIKeyboardWillShowNotification, selector: #selector(KeyboardNotificationDelegate.keyboardWillShow(_:)))
            registerNotification(UIKeyboardWillHideNotification, selector: #selector(KeyboardNotificationDelegate.keyboardWillHide(_:)))
        }
    }
}

However this get me the error

trailing where clause for extension of non-generic type

on the extension row. Any ideas?

like image 818
Sunkas Avatar asked Mar 23 '16 08:03

Sunkas


2 Answers

The solution was simple to switch the order in the extension clause:

extension UIViewController where Self: KeyboardNotificationDelegate

should be

extension KeyboardNotificationDelegate where Self: UIViewController
like image 76
Sunkas Avatar answered Oct 16 '22 23:10

Sunkas


extension Foo where ... can only be used if Foo is

  1. a generic class or structure: extend with a default implementation for generics conforming to some type constraint,
  2. a protocol containing some associated types, extend with a default implementation for when an associated type conform to some type constraint
  3. a protocol where we extend with a default implementation for when Self is of a specific (object/reference) type, or conforms to some type constraint.

E.g.

// 1
class Foo<T> { }
extension Foo where T: IntegerType {}

struct Foz<T> {}
extension Foz where T: IntegerType {}

// 2
protocol Bar {
    associatedtype T
}
extension Bar where T: IntegerType {}

// 3
protocol Baz {}
extension Baz where Self: IntegerType {}

class Bax<T>: Baz {}
extension Baz where Self: Bax<Int> {
    func foo() { print("foo") }
}

let a = Bax<Int>()
a.foo() // foo

In your case, UIViewController is a non-generic class type, which does not conform to any of the two above.


As you've written in your own answer, the solution is to extend your delegate protocol with a default implementation for cases where Self: UIViewController, rather than attempting to extend UIViewController.

like image 27
dfrib Avatar answered Oct 16 '22 23:10

dfrib