20

I'm defining a protocol called PanelController in which I'd like to store a PanelView. PanelView itself is a subclass of UIView and defines the basic structure of panel. I have three different views that subclass PanelView: LeftPanel, MidPanel, and RightPanel. For each of those panels I'd like to define a xxxPanelController (left, mid, right) that conforms to the PanelController protocol.

The issue I'm running up against is in the protocol and xxxPanelController

protocol PanelController {
    var panelView: PanelView { get set }
    ...
}

and

class LeftPanelController: UIViewController, PanelController {
    var panelView = LeftPanelView()
    ...
}

where

class LeftPanelView: PanelView {
     ...
}

and (one last piece...)

class PanelView: UIView {
    ...
}

I get an error saying that: LeftPanelController does not conform to protocol PanelController for an obvious reason: panelView is of type LeftPanelView not PanelView. This seems really limited to me, though, because LeftPanelView is a subclass of PanelView so it should just work! But it doesn't!

Can someone explain to me why this is, and if anyone can come up with one, a possible workaround? Thanks!

Clay Ellis
  • 4,960
  • 2
  • 37
  • 45
  • 1
    It does not conform because the protocol would allow that an instance of PanelView is assigned to a property of type LeftPanelView. It should compile if the property is declared as read only in the protocol. – Martin R Aug 26 '15 at 16:27
  • I assume that this has been answered before, but it is difficult for me to search for it on the phone:) – Martin R Aug 26 '15 at 16:28

1 Answers1

11

The problem is with the setter in the protocol.

Let's say you want to GET the panelView from LeftPanelController. That's fine, because LeftPanelView can do everything PanelView can do (and more).

If you want to SET the panelView of LeftPanelController though, you can give it any PanelView. Because you're defining the panelView variable as a LeftPanelView, the setter could sometimes fail.

To fix this, you could do the following in LeftPanelController:

var panelView: PanelView = LeftPanelView()

The implication of this is that you won't be able to access any methods or properties that are specific to LeftPanelView without casting it first. If that's not an issue, then this should fix your problem!

Eric
  • 2,077
  • 17
  • 16
  • 1
    Do you know of a way to store the type (i.e. `LeftPanelView`) in a variable to use for casting later? (In order to decouple the view from the `PanelController` further.) – Clay Ellis Aug 26 '15 at 21:57
  • What is the use case? If you need a `LeftPanelView` object, you should store that type of object. If you have a protocol that you're using instead, I'm assuming that the specific type of object does not matter. If it does, a protocol may not be the appropriate solution. – Eric Aug 27 '15 at 00:34
  • 1
    Does the same explanation apply if `PanelView` was a protocol that they all conformed to? I'm having an issue similar to this, except that my `PanelView` is a protocol. Your explanation makes sense in the case of inheriting from a base class, but I'm struggling to understand why this wouldn't work with a protocol since all you care about is that they implement the methods as defined in the protocol. – blau May 03 '16 at 20:45
  • Yes, the explanation still applies. The issue is that the controller thinks the `panelView` is a `LeftPanelView` instead of a `PanelView`. That's why the explicit declaration fixes the issue. Does that help? – Eric May 06 '16 at 16:21
  • 12
    The issue exists even with a get-only property. – Frederic Adda Jun 29 '18 at 07:46