-1

I have a protocol:

protocol InspectableSource
{
    var headerView: NSView? { get }
    var footerView: NSView? { get }
    var mainContentViews: [NSView] { get }
}

Then I have a class that adopts this protocol:

class MyClass: InspectableSource
{
    var mainContentViews: [MyNSViewSubclass]
}

The instance of MyClass provides mainContentViews, which is full of objects that are subclasses of NSView. Swift whines about this and won't compile...because it's Swift.

So how do I declare in my protocol that mainContentViews can be ANY type of object that is an NSView or a subclass thereof?

Bryan
  • 4,628
  • 3
  • 36
  • 62
  • Hi @MartinR. Your assessment is not correct. That other question is about protocol inheritance and the answer to it is full of gobbledygook that only a language expert could parse. It is of no help to those transitioning to or learning Swift for the first time. Sweeper and RickyMo were actually helpful. You were not. – Bryan Mar 04 '19 at 08:52
  • I took my time to find a Q&A which contains an excellent explanation of the problem. And if you read until “Another workaround is just to define a dummy property in order to satisfy the protocol requirement” then you'll find exactly the solution proposed below. – Martin R Mar 04 '19 at 08:55
  • @MartinR What you're not hearing is that I spent an hour searching dozens of phrases to try to turn up a discussion of this issue and never came across that post. Because it's not phrased in a way that folks who aren't...you...would think to use or google. That, and the impenetrable meta-discussion about Swift as a work-in-progress that leads the answer section make it less useful to mere mortals. Sweeper helped me and his answer will help others. – Bryan Mar 04 '19 at 09:07

2 Answers2

1

You could use associated types:

protocol InspectableSource
{
    associatedtype ViewType : NSView
    var headerView: ViewType? { get }
    var footerView: ViewType? { get }
    var mainContentViews: [ViewType] { get }
}

And in your conforming class:

class MyClass: InspectableSource
{
    typealias ViewType = MyNSViewSubclass
    var mainContentViews: [MyNSViewSubclass]
    // ...
}

But note that this will prevent you from using InspectableSource as the type of a variable.

If you really want to have InspectableSource as the type of a variable. You can try this less type safe approach:

class MyClass: InspectableSource {
    Var mainContentViews: [MyNSViewSubclass]
    var inspectableMainContentViews: [NSView] {
        return mainContentViews 
    }
}

protocol InspectableSource {
    var inspectableMainContentView: [NSView] { get }
}
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • Thanks Sweeper. I did get pretty far down the 'associatedType' rabbit hole, but that places too many limits on the protocol, as you point out. – Bryan Mar 04 '19 at 08:25
  • My issue is that I have several variants of 'MyClass' that all want to have a different kind of NSView subclass in the 'mainContentViews', and I **want** to enforce that. (Each variant of 'MyClass' should accept only one type of NSView-subclass). So leaving it as just 'NSView' prevents the compiler from type-checking that. – Bryan Mar 04 '19 at 08:27
  • @Bryan what about naming the protocol properties with a prefix, like `inspectable` and then you can have two declarations of the same property, one called `inspectableMainContentViews` of type `[NSView]` and `mainContentViews` of type `[MyNSViewSubclass]`? – Sweeper Mar 04 '19 at 08:31
  • Thanks! You beat me to it. Adding another read-only property to 'MyClass' that just casts the "real" property as [NSView] is exactly what I ended up doing. It's kludgy, but it works. – Bryan Mar 04 '19 at 08:38
  • @MartinR yes it seems to be the same idea. The only difference is that the linked question is about a protocol member having a protocol type whereas this question is about a protocol member having a class type. I am not sure whether that constitutes a duplicate. – Sweeper Mar 04 '19 at 08:47
1

If you can modify MyClass, I suggest using a dedicated field to store your [MyNSViewSubclass], then conforms to InspectableSource via computed property.

protocol InspectableSource
{
    var mainContentViews: [NSView] { get }
}

class MyClass: InspectableSource
{
    var mainContentViews : [NSView]{
        return myNSViews
    }
    var myNSViews : [MyNSViewSubclass]

    init() {
        self.myNSViews = []
    }
}
Ricky Mo
  • 6,285
  • 1
  • 14
  • 30