Skip to content

Instantly share code, notes, and snippets.

@phillipcaudell
Created September 19, 2023 09:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save phillipcaudell/99d1fe52873d0c5115bc281272003784 to your computer and use it in GitHub Desktop.
Save phillipcaudell/99d1fe52873d0c5115bc281272003784 to your computer and use it in GitHub Desktop.
A type-erased codable wrapper
import Foundation
/// A type-erased codable wrapper.
public struct AnyCodable {
public typealias Value = any Codable & Hashable
public let value: Value
/// Creates an instance that type-erases a value.
public init(_ value: Value) {
self.value = value
}
}
// MARK: - Codable
extension AnyCodable: Codable {
enum CodingKeys: CodingKey {
case type
case value
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(_mangledTypeName(type(of: value)), forKey: .type)
try container.encode(value, forKey: .value)
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let typeName = try container.decode(String.self, forKey: .type)
guard let type = _typeByName(typeName) as? any Codable.Type else {
throw DecodingError.dataCorruptedError(
forKey: .type,
in: container,
debugDescription: "Could not decode type \(typeName)"
)
}
guard let value = try container.decode(
type, forKey: .value) as? Value else {
throw DecodingError.dataCorruptedError(
forKey: .value,
in: container,
debugDescription: "\(typeName) is not Codable & Hashable"
)
}
self.value = value
}
}
// MARK: - Hashable
extension AnyCodable: Hashable {
public static func == (lhs: AnyCodable, rhs: AnyCodable) -> Bool {
lhs.value.isEqual(to: rhs.value)
}
public func hash(into hasher: inout Hasher) {
hasher.combine(value)
}
}
// MARK: - Equatable
extension Equatable {
func isEqual(to other: any Equatable) -> Bool {
guard let other = other as? Self else {
return false
}
return self == other
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment