Search code examples
inheritanceswiftinterfaceprotocolstypechecking

Swift -- Require classes implementing protocol to be subclasses of a certain class


I'm creating several NSView classes, all of which support a special operation, which we'll call transmogrify. At first glance, this seems like the perfect place for a protocol:

protocol TransmogrifiableView {
    func transmogrify()
}

However, this protocol does not enforce that every TransmogrifiableView be an NSView as well. This means that any NSView methods I call on a TransmogrifiableView will not type check:

let myView: TransmogrifiableView = getTransmogrifiableView()
let theSuperView = myView.superView // error: TransmogrifiableView does not have a property called 'superview'

I don't know how to require that all classes implementing my protocol are also subclasses of NSView. I tried this:

protocol TransmogrifiableView: NSView {
    func transmogrify()
}

but Swift complains that protocols cannot inherit from classes. It does not help to turn the protocol into a class-only protocol using

protocol TransmogrifiableView: class, NSView {
    func transmogrify()
}

I cannot make TransmogrifiableView a superclass rather than a protocol, because some of my TransmogrifiableView classes must be subclasses of other, non-transmogrifiable views.

How should I require that all TransmogrifiableView's also be NSView's? I really don't want to pepper my code with "as" conversions, which are bad form and distracting.


Solution

  • I think you are after a subclass of NSView. Try this:

    protocol TransmogrifiableView {
        func transmogrify()
    }
    
    class MyNSView: NSView, TransmogrifiableView {
        // do stuff.
    }
    

    And later in the code accept objects of type MyNSView.

    Edit

    You maybe want an Extension, see this

    extension NSView: TransmogrifiableView {
        // implementation of protocol requirements goes here
    }
    
    • Note that you will not be able to get an NSView without this extra method.
    • You can separately extend subclasses of NSView to override this new method.

    Yet another option is to make a class which holds a pointer to an NSView, and implements additional methods. This will also force you to proxy all methods from NSView that you want to use.

    class NSViewWrapper: TransmogrifiableView {
        var view : NSView!
        // init with the view required.
        //  implementation of protocol requirements goes here.
        .....
       // proxy all methods from NSView.
       func getSuperView(){
           return self.view.superView
       }
    }
    

    This is quite long and not nice, but will work. I would recommend you to use this only if you really cannot work with extensions (because you need NSViews without the extra method).