Decoding/Encoding 具有协议类型属性的结构
Decoding/Encoding a struct with protocol type properties
我正在尝试使用 UserDefaults
保存配置数据结构,因此该数据结构需要符合 Codable
协议。这是我的数据结构:
// 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
}
如果我只是将 Codable
添加到 Configuration
,那是行不通的。所以只好自己实现了。
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)
}
}
对于 decode()
或 encode()
的每次调用,我都会收到错误 Protocol type 'Connection' cannot conform to 'Decodable/Encodable' because only concrete types can conform to protocols
。
我可以看到编译器很难识别应该使用哪个 class 来解码给定的对象。但我认为对对象进行编码应该很容易,因为每个 Connection
类型的对象都实现了 encode()
方法。
我知道,问题出在协议上,协议不能与 Decodable/Encodable
一起使用。我将如何更改 decode/encode
中的代码,以便我仍然可以将协议与各种实现一起使用?我的猜测是以某种方式告诉 decode/encode
要使用协议的哪个实现。对于这个问题,我将不胜感激!
Swift 协议不能符合自身的限制。因此 from
和 to
不符合 Codable
看起来很奇怪。
您可以通过使用泛型来绕过它,这基本上意味着您将 from
和 to
声明为符合 Codable
的任意类型。方法如下:
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)
所以F
和T
是符合Connection
的类型。当您在最后一行实例化 example
时,编译器推断 F
是 SFTPConnection
并且 T
是 FTPConnection
.
添加通用参数后,Configuration
无需扩展即可合成对 Codable
的一致性。
为了回答 Sh_kahn 关于有两个通用参数的观点,我这样做是为了让 from
和 to
成为不同类型的连接。如果你总是希望两个连接的类型相同,即总是两个 SFTPConnection
s 或两个 FTPConnection
s 你应该像这样声明 Configuration
:
struct Configuration<C: Connection>: Codable {
var from: C
var to: C
}
我正在尝试使用 UserDefaults
保存配置数据结构,因此该数据结构需要符合 Codable
协议。这是我的数据结构:
// 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
}
如果我只是将 Codable
添加到 Configuration
,那是行不通的。所以只好自己实现了。
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)
}
}
对于 decode()
或 encode()
的每次调用,我都会收到错误 Protocol type 'Connection' cannot conform to 'Decodable/Encodable' because only concrete types can conform to protocols
。
我可以看到编译器很难识别应该使用哪个 class 来解码给定的对象。但我认为对对象进行编码应该很容易,因为每个 Connection
类型的对象都实现了 encode()
方法。
我知道,问题出在协议上,协议不能与 Decodable/Encodable
一起使用。我将如何更改 decode/encode
中的代码,以便我仍然可以将协议与各种实现一起使用?我的猜测是以某种方式告诉 decode/encode
要使用协议的哪个实现。对于这个问题,我将不胜感激!
Swift 协议不能符合自身的限制。因此 from
和 to
不符合 Codable
看起来很奇怪。
您可以通过使用泛型来绕过它,这基本上意味着您将 from
和 to
声明为符合 Codable
的任意类型。方法如下:
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)
所以F
和T
是符合Connection
的类型。当您在最后一行实例化 example
时,编译器推断 F
是 SFTPConnection
并且 T
是 FTPConnection
.
添加通用参数后,Configuration
无需扩展即可合成对 Codable
的一致性。
为了回答 Sh_kahn 关于有两个通用参数的观点,我这样做是为了让 from
和 to
成为不同类型的连接。如果你总是希望两个连接的类型相同,即总是两个 SFTPConnection
s 或两个 FTPConnection
s 你应该像这样声明 Configuration
:
struct Configuration<C: Connection>: Codable {
var from: C
var to: C
}