问题
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" ?
回答1:
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.
回答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 }
}
来源:https://stackoverflow.com/questions/38007881/swift-protocol-to-require-properties-as-protocol