Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
DictionaryEncoder for Encodable types
class DictionaryEncoder {
init() { }
func encode(_ value: Encodable) throws -> [String: Any] {
let encoder = _Encoder(codingPath: [])
try value.encode(to: encoder)
guard let result = encoder.value as? [String: Any] else {
throw EncodingError.invalidValue(encoder.value as Any, .init(codingPath: [], debugDescription: "Invalid root container"))
}
return result
}
}
// MARK: - Encoder
extension DictionaryEncoder {
private class _Encoder: Encoder {
var codingPath: [CodingKey]
var userInfo: [CodingUserInfoKey: Any] = [:]
private(set) var value: Any?
init(codingPath: [CodingKey]) {
self.codingPath = codingPath
}
// MARK: - KeyedContainer
func container<Key: CodingKey>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> {
return KeyedEncodingContainer(KeyedContainer<Key>(encoder: self, codingPath: codingPath))
}
struct KeyedContainer<Key: CodingKey>: KeyedEncodingContainerProtocol {
let codingPath: [CodingKey]
let encoder: _Encoder
init(encoder: _Encoder, codingPath: [CodingKey]) {
self.encoder = encoder
self.codingPath = codingPath
encoder.value = [String: Any]()
}
mutating private func setValue(_ value: Any?, key: Key) {
guard let value = value else { return }
var current = encoder.value as! [String: Any]
current[key.stringValue] = value
encoder.value = current
}
mutating func encodeNil(forKey key: Key) throws { }
mutating func encode<T: Encodable>(_ value: T, forKey key: Key) throws {
switch value {
case is CodablePrimitive:
setValue(value, key: key)
default:
let encoder = _Encoder(codingPath: codingPath + [key])
try value.encode(to: encoder)
setValue(encoder.value, key: key)
}
}
mutating func nestedContainer<NestedKey: CodingKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> {
return KeyedEncodingContainer<NestedKey>(KeyedContainer<NestedKey>(encoder: encoder, codingPath: codingPath + [key]))
}
mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
return UnkeyedContainer(encoder: encoder, codingPath: codingPath + [key])
}
mutating func superEncoder() -> Encoder { fatalError() }
mutating func superEncoder(forKey key: Key) -> Encoder { fatalError() }
}
// MARK: - UnkeyedContainer
func unkeyedContainer() -> UnkeyedEncodingContainer {
return UnkeyedContainer(encoder: self, codingPath: codingPath)
}
struct UnkeyedContainer: UnkeyedEncodingContainer {
let codingPath: [CodingKey]
private(set) var count: Int = 0
let encoder: _Encoder
init(encoder: _Encoder, codingPath: [CodingKey]) {
self.encoder = encoder
self.codingPath = codingPath
encoder.value = [Any]()
}
mutating private func append(_ value: Any?) {
guard let value = value else { return }
var current = encoder.value as! [Any]
current.append(value)
encoder.value = current
}
mutating func encodeNil() throws { }
mutating func encode<T: Encodable>(_ value: T) throws {
defer { count += 1 }
switch value {
case is CodablePrimitive:
append(value)
default:
let encoder = _Encoder(codingPath: codingPath + [AnyCodingKey(count)])
try value.encode(to: encoder)
append(encoder.value)
}
}
mutating func nestedContainer<NestedKey: CodingKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> {
return KeyedEncodingContainer<NestedKey>(KeyedContainer<NestedKey>(encoder: encoder, codingPath: codingPath))
}
mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
return UnkeyedContainer(encoder: encoder, codingPath: codingPath)
}
mutating func superEncoder() -> Encoder { fatalError() }
}
// MARK: - SingleValueContainer
func singleValueContainer() -> SingleValueEncodingContainer {
return SingleValueContainer(encoder: self, codingPath: [])
}
struct SingleValueContainer: SingleValueEncodingContainer {
let codingPath: [CodingKey]
let encoder: _Encoder
init(encoder: _Encoder, codingPath: [CodingKey]) {
self.encoder = encoder
self.codingPath = codingPath
}
mutating private func update(_ value: Any?) {
guard let value = value else { return }
encoder.value = value
}
mutating func encodeNil() throws { }
mutating func encode<T: Encodable>(_ value: T) throws {
switch value {
case is CodablePrimitive:
encoder.value = value
default:
let encoder = _Encoder(codingPath: codingPath)
try value.encode(to: encoder)
update(encoder.value)
}
}
}
}
}
@IanKeen

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.