7

I am trying to define a protocol "Repository" which requires de definition of a couple of properties (which implement a specific protocol "DataSource")

But due to the complexity of my real scenario one of this properties need to be a subprotocol of "DataSource".

I reduced the problem to this simple code:

protocol DataSource { }

protocol ExtraDataSouce: DataSource {
    func method1() -> String
}

struct MyDataSource: ExtraDataSouce {
    func method1() -> String {
        return "whatever"
    }
}


protocol Repository {
    var firstDataSource: DataSource { get }
    var secondDataSource: DataSource { get }
}

struct MyRepository: Repository {
    var firstDataSource: DataSource
    var secondDataSource: MyDataSource
}

Which return an error at compilation time because "MyRepository" doesn't conform "Repository". But I think it actually does... Any idea about why it doesn't accept "secondDataSource" in "MyRepository" defined as "MyDataSource" ?

2 Answers2

2

Your Repository protocol implement 2 variable of DataSource type, when you are trying to modify a type of variable in struct that conforms to Repository protocol it won't let you do this because of required type. You should to use an associatedtype to make this change

protocol Repository {
    associatedtype type = DataSource
    var firstDataSource: DataSource { get }
    var secondDataSource: type { get }
}
Radu Nunu
  • 134
  • 5
  • Because secondDataSource var with DataSource type will require strongly to implement only this type. With associatedtype it let you implement a object of Datasource type. – Radu Nunu Jun 24 '16 at 07:57
  • Please include the explanation in the answer. You should always include an explanation in your answer, not just post code. – luk2302 Jun 24 '16 at 08:00
  • Do line `associatedtype type = DataSource` equivalent to line `associatedtype type`? I never met this usage of `associatedtype`. I have same result for both lines: in `MyRepository` I can now specify any type for `secondDataSource` that I want, `String` for example. Please, explain more wide if you can. I understand `associatedtype` main concept, but I am not sure that this is result that author was want to achieve – Yury Jun 24 '16 at 08:21
  • The idea is that you can define a protocol with functions or variables that take in a type that is defined by the object that uses that protocol. In fact associatedtype is a Placeholder for an Unknown Type. – Radu Nunu Jun 24 '16 at 08:33
2

After searching I found this information about your question (please, correct me if I wrong somewhere or if I miss something):

Even while logically your code should work, swift compiler don't separate cases when you use regular or read- only protocol variables when you declaring their type in your MyRepository class. On other words, error in your code become obvious if you will write in Repository

var secondDataSource: DataSource { get set }

and compiler don't separate this case. I did not found fully right way to do what you want. But there is two close ways:

1) Obvious and probably most right way - change secondDataSource type in MyRepository, and use additional variable if you wish:

var _secondDataSource: MyDataSource
var secondDataSource: DataSource {
        get {return _secondDataSource}
        set {
            guard let newValue = newValue as? MyDataSource else {
                fatalError("MyRepository: attempt to set DataSource type, MyDataSource type expected")
            }
            _secondDataSource = newValue
        }
    }

2) Associated type in protocol way. Here I will improve @RaduNunu answer, since associatedtype type = DataSource line in his code have only placeholder effect, and his solution allows you to choose on adoption any type of secondDataSource ever, String for example:

protocol Repository {
    associatedtype Type = DataSource
    var firstDataSource: DataSource { get }
    var secondDataSource: Type { get }
}

struct MyRepository: Repository {
    var firstDataSource: DataSource
    var secondDataSource: String // - this is ok!
}

This code will compile and work, but it looks pretty bad. Instead type placeholder you better use protocol conformance:

protocol Repository {
    associatedtype Type: DataSource
    var firstDataSource: DataSource { get }
    var secondDataSource: Type { get }
}

struct MyRepository: Repository {
    var firstDataSource: DataSource
    //var secondDataSource: String - this line not allowed now
    var secondDataSource: MyDataSource
}

This code already pretty close to goal. However, from now you can't use a protocol as associated type, so declaration

var secondDataSource: DataSource

in MyRepository will not work. This is pay for using associated type: you can use only DataSource conformed class/enum/struct types.

Yury
  • 6,044
  • 3
  • 19
  • 41