То, что вы можете сделать, это определить пользовательский CodingKey
, который по существу объединяет как ключи кодирования вашей модели, так и любые другие ключи кодирования, которые вы хотите, вот так (предупреждение - здесь много кода, но я сделал его таким, чтобы он был достаточно универсальным)для повторного использования для любой модели в любом проекте):
protocol CompoundableCodingKey: CodingKey {
associatedtype OtherCodingKeys1
associatedtype OtherCodingKeys2
init(otherCodingKeys1: OtherCodingKeys1)
init(otherCodingKeys2: OtherCodingKeys2)
init(intValue: Int)
init(stringValue: String)
}
struct CompoundCodingKeys<OtherCodingKeys1: CodingKey, OtherCodingKeys2: CodingKey>: CompoundableCodingKey {
private let otherCodingKeys1: OtherCodingKeys1?
private let otherCodingKeys2: OtherCodingKeys2?
private let internalIntValue: Int?
private let internalStringValue: String
var intValue: Int? {
if let otherCodingKeys1 = otherCodingKeys1 {
return otherCodingKeys1.intValue
} else if let otherCodingKeys2 = otherCodingKeys2 {
return otherCodingKeys2.intValue
}
return internalIntValue
}
var stringValue: String {
if let otherCodingKeys1 = otherCodingKeys1 {
return otherCodingKeys1.stringValue
} else if let otherCodingKeys2 = otherCodingKeys2 {
return otherCodingKeys2.stringValue
}
return internalStringValue
}
init(intValue: Int) {
if let otherCodingKeys1 = OtherCodingKeys1(intValue: intValue) {
self.otherCodingKeys1 = otherCodingKeys1
otherCodingKeys2 = nil
internalIntValue = nil
internalStringValue = otherCodingKeys1.stringValue
} else if let otherCodingKeys2 = OtherCodingKeys2(intValue: intValue) {
otherCodingKeys1 = nil
self.otherCodingKeys2 = otherCodingKeys2
internalIntValue = nil
internalStringValue = otherCodingKeys2.stringValue
} else {
otherCodingKeys1 = nil
otherCodingKeys2 = nil
internalIntValue = intValue
internalStringValue = intValue.description
}
}
init(stringValue: String) {
if let otherCodingKeys1 = OtherCodingKeys1(stringValue: stringValue) {
self.otherCodingKeys1 = otherCodingKeys1
otherCodingKeys2 = nil
internalIntValue = nil
internalStringValue = otherCodingKeys1.stringValue
} else if let otherCodingKeys2 = OtherCodingKeys2(stringValue: stringValue) {
otherCodingKeys1 = nil
self.otherCodingKeys2 = otherCodingKeys2
internalIntValue = nil
internalStringValue = otherCodingKeys2.stringValue
} else {
otherCodingKeys1 = nil
otherCodingKeys2 = nil
internalIntValue = nil
internalStringValue = stringValue
}
}
init(otherCodingKeys1: OtherCodingKeys1) {
self.init(stringValue: otherCodingKeys1.stringValue)
}
init(otherCodingKeys2: OtherCodingKeys2) {
self.init(stringValue: otherCodingKeys2.stringValue)
}
}
extension KeyedEncodingContainerProtocol where Key: CompoundableCodingKey {
mutating func encode<T>(_ value: T, forKey key: Key.OtherCodingKeys1) throws where T: Encodable {
try encode(value, forKey: Key(otherCodingKeys1: key))
}
mutating func encode<T>(_ value: T, forKey key: Key.OtherCodingKeys2) throws where T: Encodable {
try encode(value, forKey: Key(otherCodingKeys2: key))
}
mutating func encodeIfPresent<T>(_ value: T?, forKey key: Key.OtherCodingKeys1) throws where T: Encodable {
try encodeIfPresent(value, forKey: Key(otherCodingKeys1: key))
}
mutating func encodeIfPresent<T>(_ value: T?, forKey key: Key.OtherCodingKeys2) throws where T: Encodable {
try encodeIfPresent(value, forKey: Key(otherCodingKeys2: key))
}
mutating func encodeConditional<T>(_ object: T, forKey key: Key.OtherCodingKeys1) throws where T: AnyObject, T: Encodable {
try encodeConditional(object, forKey: Key(otherCodingKeys1: key))
}
mutating func encodeConditional<T>(_ object: T, forKey key: Key.OtherCodingKeys2) throws where T: AnyObject, T: Encodable {
try encodeConditional(object, forKey: Key(otherCodingKeys2: key))
}
}
extension KeyedEncodingContainerProtocol where Key: CompoundableCodingKey, Key.OtherCodingKeys1: CompoundableCodingKey {
mutating func encode<T>(_ value: T, forKey key: Key.OtherCodingKeys1.OtherCodingKeys1) throws where T: Encodable {
try encode(value, forKey: Key.OtherCodingKeys1(otherCodingKeys1: key))
}
mutating func encode<T>(_ value: T, forKey key: Key.OtherCodingKeys1.OtherCodingKeys2) throws where T: Encodable {
try encode(value, forKey: Key.OtherCodingKeys1(otherCodingKeys2: key))
}
mutating func encodeIfPresent<T>(_ value: T?, forKey key: Key.OtherCodingKeys1.OtherCodingKeys1) throws where T: Encodable {
try encodeIfPresent(value, forKey: Key.OtherCodingKeys1(otherCodingKeys1: key))
}
mutating func encodeIfPresent<T>(_ value: T?, forKey key: Key.OtherCodingKeys1.OtherCodingKeys2) throws where T: Encodable {
try encodeIfPresent(value, forKey: Key.OtherCodingKeys1(otherCodingKeys2: key))
}
mutating func encodeConditional<T>(_ object: T, forKey key: Key.OtherCodingKeys1.OtherCodingKeys1) throws where T: AnyObject, T: Encodable {
try encodeConditional(object, forKey: Key.OtherCodingKeys1(otherCodingKeys1: key))
}
mutating func encodeConditional<T>(_ object: T, forKey key: Key.OtherCodingKeys1.OtherCodingKeys2) throws where T: AnyObject, T: Encodable {
try encodeConditional(object, forKey: Key.OtherCodingKeys1(otherCodingKeys2: key))
}
}
extension KeyedEncodingContainerProtocol where Key: CompoundableCodingKey, Key.OtherCodingKeys2: CompoundableCodingKey {
mutating func encode<T>(_ value: T, forKey key: Key.OtherCodingKeys2.OtherCodingKeys1) throws where T: Encodable {
try encode(value, forKey: Key.OtherCodingKeys2(otherCodingKeys1: key))
}
mutating func encode<T>(_ value: T, forKey key: Key.OtherCodingKeys2.OtherCodingKeys2) throws where T: Encodable {
try encode(value, forKey: Key.OtherCodingKeys2(otherCodingKeys2: key))
}
mutating func encodeIfPresent<T>(_ value: T?, forKey key: Key.OtherCodingKeys2.OtherCodingKeys1) throws where T: Encodable {
try encodeIfPresent(value, forKey: Key.OtherCodingKeys2(otherCodingKeys1: key))
}
mutating func encodeIfPresent<T>(_ value: T?, forKey key: Key.OtherCodingKeys2.OtherCodingKeys2) throws where T: Encodable {
try encodeIfPresent(value, forKey: Key.OtherCodingKeys2(otherCodingKeys2: key))
}
mutating func encodeConditional<T>(_ object: T, forKey key: Key.OtherCodingKeys2.OtherCodingKeys1) throws where T: AnyObject, T: Encodable {
try encodeConditional(object, forKey: Key.OtherCodingKeys2(otherCodingKeys1: key))
}
mutating func encodeConditional<T>(_ object: T, forKey key: Key.OtherCodingKeys2.OtherCodingKeys2) throws where T: AnyObject, T: Encodable {
try encodeConditional(object, forKey: Key.OtherCodingKeys2(otherCodingKeys2: key))
}
}
extension KeyedDecodingContainerProtocol where Key: CompoundableCodingKey {
func decode<T>(_ type: T.Type, forKey key: Key.OtherCodingKeys1) throws -> T where T: Decodable {
try decode(type, forKey: Key(otherCodingKeys1: key))
}
func decode<T>(_ type: T.Type, forKey key: Key.OtherCodingKeys2) throws -> T where T: Decodable {
try decode(type, forKey: Key(otherCodingKeys2: key))
}
func decodeIfPresent<T>(_ type: T.Type, forKey key: Key.OtherCodingKeys1) throws -> T? where T: Decodable {
try decodeIfPresent(type, forKey: Key(otherCodingKeys1: key))
}
func decodeIfPresent<T>(_ type: T.Type, forKey key: Key.OtherCodingKeys2) throws -> T? where T: Decodable {
try decodeIfPresent(type, forKey: Key(otherCodingKeys2: key))
}
}
extension KeyedDecodingContainerProtocol where Key: CompoundableCodingKey, Key.OtherCodingKeys1: CompoundableCodingKey {
func decode<T>(_ type: T.Type, forKey key: Key.OtherCodingKeys1.OtherCodingKeys1) throws -> T where T: Decodable {
try decode(type, forKey: Key.OtherCodingKeys1(otherCodingKeys1: key))
}
func decode<T>(_ type: T.Type, forKey key: Key.OtherCodingKeys1.OtherCodingKeys2) throws -> T where T: Decodable {
try decode(type, forKey: Key.OtherCodingKeys1(otherCodingKeys2: key))
}
func decodeIfPresent<T>(_ type: T.Type, forKey key: Key.OtherCodingKeys1.OtherCodingKeys1) throws -> T? where T: Decodable {
try decodeIfPresent(type, forKey: Key.OtherCodingKeys1(otherCodingKeys1: key))
}
func decodeIfPresent<T>(_ type: T.Type, forKey key: Key.OtherCodingKeys1.OtherCodingKeys2) throws -> T? where T: Decodable {
try decodeIfPresent(type, forKey: Key.OtherCodingKeys1(otherCodingKeys2: key))
}
}
extension KeyedDecodingContainerProtocol where Key: CompoundableCodingKey, Key.OtherCodingKeys2: CompoundableCodingKey {
func decode<T>(_ type: T.Type, forKey key: Key.OtherCodingKeys2.OtherCodingKeys1) throws -> T where T: Decodable {
try decode(type, forKey: Key.OtherCodingKeys2(otherCodingKeys1: key))
}
func decode<T>(_ type: T.Type, forKey key: Key.OtherCodingKeys2.OtherCodingKeys2) throws -> T where T: Decodable {
try decode(type, forKey: Key.OtherCodingKeys2(otherCodingKeys2: key))
}
func decodeIfPresent<T>(_ type: T.Type, forKey key: Key.OtherCodingKeys2.OtherCodingKeys1) throws -> T? where T: Decodable {
try decodeIfPresent(type, forKey: Key.OtherCodingKeys2(otherCodingKeys1: key))
}
func decodeIfPresent<T>(_ type: T.Type, forKey key: Key.OtherCodingKeys2.OtherCodingKeys2) throws -> T? where T: Decodable {
try decodeIfPresent(type, forKey: Key.OtherCodingKeys2(otherCodingKeys2: key))
}
}
Затем, если мы изменим SharedCodingKeyValues
в перечисление CodingKey
, например:
enum SharedCodingKeys: String, CodingKey {
case version
case serializer
}
... затем мы можем получить контейнер с ключом типа CompoundCodingKeys<CodingKeys, SharedCodingKeys>
, который позволяет вам указать либо Person.CodingKeys
(который не нужно определять вручную), или SharedCodingKeys
,вот так:
// MARK: Encodable
extension Person {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CompoundCodingKeys<CodingKeys, SharedCodingKeys>.self)
// From `Person.CodingKeys`
try container.encode(name, forKey: .name)
// From `SharedCodingKeys`
try container.encode(version, forKey: .version)
try container.encode(serializerName, forKey: .serializer)
}
}
Поскольку CompoundCodingKeys
само соответствует CodingKey
, вы даже можете сделать этот шаг еще дальше и объединить столько ключей кодирования, сколько хотите, в один, например, CompoundCodingKeys<CodingKeys, CompoundCodingKeys<SomeOtherCodingKeys, SharedCodingKeys>>
:
enum SomeOtherCodingKeys: String, CodingKey {
case someOtherKey
}
// MARK: Encodable
extension Person {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CompoundCodingKeys<CodingKeys, CompoundCodingKeys<SomeOtherCodingKeys, SharedCodingKeys>>.self)
// From `Person.CodingKeys`
try container.encode(name, forKey: .name)
// From `SharedCodingKeys`
try container.encode(version, forKey: .version)
try container.encode(serializerName, forKey: .serializer)
// From `SomeOtherCodingKeys`
try container.encode(serializerName, forKey: .someOtherKey)
}
}