Swift protocol to require properties as protocol

你说的曾经没有我的故事 提交于 2019-12-29 07:05:08

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!