简体   繁体   English

使用协议类型属性对结构进行解码/编码

[英]Decoding/Encoding a struct with protocol type properties

I am trying to save a configuration data structure with UserDefaults , thus the data structure needs to conform to the Codable protocol.我正在尝试使用UserDefaults保存配置数据结构,因此数据结构需要符合Codable协议。 This is my data structure:这是我的数据结构:

// Data structure which saves two objects, which conform to the Connection protocol
struct Configuration {
    var from: Connection
    var to: Connection
}

protocol Connection: Codable {
    var path: String { get set }
}


// Two implementations of the Connection protocol
struct SFTPConnection: Connection, Codable {
    var path: String
    var user: String
    var sshKey: String
}

struct FTPConnection: Connection, Codable {
    var path: String
    var user: String
    var password: String
}

If I just add Codable to Configuration , it won't work.如果我只是将Codable添加到Configuration ,它将不起作用。 So I have to implement it myself.所以我必须自己实现它。

extension Configuration: Codable {

    enum CodingKeys: String, CodingKey {
        case from, to
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        let from = try container.decode(Connection.self, forKey: .from)
        let to = try container.decode(Connection.self, forKey: .to)

        self.from = from
        self.to = to
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)

        try container.encode(from, forKey: .from)
        try container.encode(to, forKey: .to)
    }
}

For every call on decode() or encode() I get the error Protocol type 'Connection' cannot conform to 'Decodable/Encodable' because only concrete types can conform to protocols .对于decode()encode()每次调用,我都会收到错误Protocol type 'Connection' cannot conform to 'Decodable/Encodable' because only concrete types can conform to protocols

I can see that it is difficult for the compiler to identify, which class should be used to decode the given object.我可以看到编译器很难识别,应该使用哪个类来解码给定的对象。 But I figured it should be easy to encode an object, since every object of type Connection implements the encode() method.但我认为对对象进行编码应该很容易,因为Connection类型的每个对象都实现了encode()方法。

I know, that the problem lies with the protocol and that the protocol can't be used with Decodable/Encodable .我知道,问题出在协议上,并且该协议不能与Decodable/Encodable一起使用。 How would I change the code in decode/encode , so that I can still use the protocol with the various implementations?我将如何更改decode/encode的代码,以便我仍然可以将协议与各种实现一起使用? My guess is to somehow tell decode/encode which implementation of the protocol to use.我的猜测是以某种方式告诉decode/encode要使用的协议实现。 I would appreciate any elegant solutions for this problem!我很感激这个问题的任何优雅的解决方案!

It's a limitation of Swift that a protocol cannot conform to itself. Swift 的一个限制是协议不能符合自身。 Thus from and to do not conform to Codable as bizarre as that seems.因此fromto不符合Codable看起来很奇怪。

You can get around it by using generics which basically means you declare from and to as arbitrary types that conform to Codable .您可以通过使用泛型来绕过它,这基本上意味着您将fromto声明为符合Codable任意类型。 Here's how:就是这样:

struct Configuration<F: Connection, T: Connection>: Codable {
    var from: F
    var to: T
}


let myFrom = SFTPConnection(path: "foo", user: "me", sshKey: "hgfnjsfdjs")
let myTo = FTPConnection(path: "foo", user: "me", password: "hgfnjsfdjs")
let example = Configuration(from: myFrom, to: myTo)

So F and T are types that conform to Connection .所以FT是符合Connection类型。 When you instantiate example in the last line, the compiler infers F is SFTPConnection and T is FTPConnection .当您在最后一行实例化example时,编译器推断FSFTPConnectionTFTPConnection

Once I added the generic parameters, Configuration was able to synthesise the conformance to Codable without the extension.一旦我添加了通用参数, Configuration就能够在没有扩展的情况下综合符合Codable


To answer Sh_kahn's point about having two generic parameters, I did this to allow from and to to be connections of different types.要回答Sh_kahn的大约有两个通用的参数来看,我这样做是为了让fromto是两种不同类型的连接。 If you always want the two connections to be of the same type ie always two SFTPConnection s or two FTPConnection s you should declare the Configuration like this:如果您总是希望两个连接的类型相同,即总是两个SFTPConnection或两个FTPConnection您应该像这样声明Configuration

struct Configuration<C: Connection>: Codable {
    var from: C
    var to: C
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM