Last active
March 4, 2021 18:45
-
-
Save IanKeen/df3bed29e2613ead94aa3b16967b9d14 to your computer and use it in GitHub Desktop.
AnyCodable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public struct AnyCodable: Codable { | |
public let value: Any? | |
public init(_ value: Any?) { | |
self.value = value | |
} | |
public init(from decoder: Decoder) throws { | |
let container = try decoder.singleValueContainer() | |
if container.decodeNil() { | |
self.value = nil | |
} else if let value = try? container.decode([String: AnyCodable].self) { | |
self.value = value.filter({ $0.value.value != nil }).mapValues({ $0.value! }) | |
} else if let value = try? container.decode([AnyCodable].self) { | |
self.value = value.compactMap({ $0.value }) | |
} else if let value = try? container.decode(Bool.self) { | |
self.value = value | |
} else if let value = try? container.decode(String.self) { | |
self.value = value | |
} else if let value = try? container.decode(Int.self) { | |
self.value = value | |
} else if let value = try? container.decode(Int8.self) { | |
self.value = value | |
} else if let value = try? container.decode(Int16.self) { | |
self.value = value | |
} else if let value = try? container.decode(Int32.self) { | |
self.value = value | |
} else if let value = try? container.decode(Int64.self) { | |
self.value = value | |
} else if let value = try? container.decode(UInt.self) { | |
self.value = value | |
} else if let value = try? container.decode(UInt8.self) { | |
self.value = value | |
} else if let value = try? container.decode(UInt16.self) { | |
self.value = value | |
} else if let value = try? container.decode(UInt32.self) { | |
self.value = value | |
} else if let value = try? container.decode(UInt64.self) { | |
self.value = value | |
} else if let value = try? container.decode(Double.self) { | |
self.value = value | |
} else if let value = try? container.decode(Float.self) { | |
self.value = value | |
} else { | |
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Unable to decode value") | |
} | |
} | |
public func encode(to encoder: Encoder) throws { | |
var container = encoder.singleValueContainer() | |
switch value { | |
case nil: | |
try container.encodeNil() | |
case let value as [String: AnyCodable]: | |
try container.encode(value) | |
case let value as [String: Any]: | |
try container.encode(value.mapValues(AnyCodable.init)) | |
case let value as [AnyCodable]: | |
try container.encode(value) | |
case let value as [Any]: | |
try container.encode(value.map(AnyCodable.init)) | |
case let value as Bool: | |
try container.encode(value) | |
case let value as String: | |
try container.encode(value) | |
case let value as Int: | |
try container.encode(value) | |
case let value as Int8: | |
try container.encode(value) | |
case let value as Int16: | |
try container.encode(value) | |
case let value as Int32: | |
try container.encode(value) | |
case let value as Int64: | |
try container.encode(value) | |
case let value as UInt: | |
try container.encode(value) | |
case let value as UInt8: | |
try container.encode(value) | |
case let value as UInt16: | |
try container.encode(value) | |
case let value as UInt32: | |
try container.encode(value) | |
case let value as UInt64: | |
try container.encode(value) | |
case let value as Double: | |
try container.encode(value) | |
case let value as Float: | |
try container.encode(value) | |
default: | |
throw EncodingError.invalidValue(value!, .init(codingPath: [], debugDescription: "Unable to encode value")) | |
} | |
} | |
} | |
extension KeyedDecodingContainer { | |
public func decode(_ type: [String: Any].Type, forKey key: K) throws -> [String: Any] { | |
let anyCodable = try decode(AnyCodable.self, forKey: key) | |
guard let value = anyCodable.value as? [String: Any] else { | |
let type = Swift.type(of: anyCodable.value) | |
throw DecodingError.typeMismatch(type, .init(codingPath: [], debugDescription: "Expected [String: Any], found \(type)")) | |
} | |
return value | |
} | |
} | |
extension KeyedEncodingContainer { | |
public mutating func encode(_ value: [String: Any], forKey key: K) throws { | |
try encode(AnyCodable(value), forKey: key) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment