5

I have a protocol that I've created (in Swift 4.2), and one of its requirements is a property that is of the same type as the protocol itself.

As an example, I have a protocol defined like so:

protocol A {
    var a: A? { get set }
}

I have several Models that conform to this protocol:

class Model1: A {
    var a: A?
}
class Model2: A {
    var a: A?
}

For one of my models, I need to satisfy the protocol requirement by being more specific for the property defined by variable a (i.e. the variable with the protocol type). So for example I may want to implement Model2 as:

class Model2: A {
    var a: Model1?
}

In this case since Model1 conforms to the protocol A you would expect this to be able to satisfy the protocol requirement, however I get an error instead:

Type 'Model2' does not conform to protocol 'A'

Why is this happening, and what can I do to make it work as described above?

Appendix

I've modelled the above scenario in an Xcode Playground and here is a screenshot of the error I'm seeing. enter image description here

Christian Gossain
  • 5,942
  • 12
  • 53
  • 85
  • 1
    Does `d` actually need to be settable? If it's not settable, this is straightforward. If it is settable, what would you want `a.d = a` to do if `a` were of type `A`, but implemented as `Model2`? – Rob Napier Mar 18 '19 at 23:26
  • 3
    For a `{ get set }` requirement [this is unsound](https://stackoverflow.com/q/40410884/2976878), as the protocol `A` says you can assign any `A?` value to `var a`, but `Model2`'s `var a` is of type `Model1?`. However for a `{ get }` requirement, this is reasonable [but currently isn't supported](https://stackoverflow.com/q/42561685/2976878). – Hamish Mar 18 '19 at 23:26
  • 2
    But is easily implemented with a computed property with a different backing ivar. (A technique that's common in ObjC and most OOP languages.) – Rob Napier Mar 18 '19 at 23:28

2 Answers2

5

To conform to protocol A, Model2 would need a member var a that allows storing a reference to anything conforming to protocol A, not just a reference to a Model1. So you can't do this.

jscs
  • 63,694
  • 13
  • 151
  • 195
gnasher729
  • 51,477
  • 5
  • 75
  • 98
  • 1
    Although this is not a solution, this seems to be the correct answer to the question. A possible workaround is to use a computed property with a backing iVar as suggested above. – Christian Gossain Mar 20 '19 at 18:08
4

You could do this with associated types:

protocol A {
    associatedtype B: A
    var a: B? { get }
}

This would let you declare Model2 with the specificity you wish:

class Model2: A {
    var a: Model1?
}

But unfortunately, that would mean you could no longer declare variables of type A. To fix that, you could use generic models:

class Model1<T: A>: A {
    var a: T?
}
dalton_c
  • 6,876
  • 1
  • 33
  • 42