Skip to content

Instantly share code, notes, and snippets.

@shawnthroop
Last active May 28, 2018 11:49
Show Gist options
  • Save shawnthroop/f94a1c083454f6b5fd67817e56f16861 to your computer and use it in GitHub Desktop.
Save shawnthroop/f94a1c083454f6b5fd67817e56f16861 to your computer and use it in GitHub Desktop.
Type-erased Equatable and Encodable values
// swift 4.1
/// A type-erased encodable value
public struct AnyEncodable {
public var base: Any {
return value.base
}
private let value: AnyEquatable
private let encodeValue: (Encoder, AnyEquatable) throws -> Void
init<E>(_ base: E) where E : Encodable & Equatable {
self.value = AnyEquatable(base)
self.encodeValue = { try AnyEncodable.encode($1, of: E.self, to: $0) }
}
}
extension AnyEncodable: Encodable {
public func encode(to encoder: Encoder) throws {
try encodeValue(encoder, value)
}
}
extension AnyEncodable: Equatable {
public static func == (lhs: AnyEncodable, rhs: AnyEncodable) -> Bool {
return lhs.value == rhs.value
}
}
private extension AnyEncodable {
static func encode<T: Encodable>(_ value: AnyEquatable, of type: T.Type, to encoder: Encoder) throws {
guard let base = value.base as? T else {
fatalError("Value of base should always be \(T.self)")
}
var container = encoder.singleValueContainer()
try container.encode(base)
}
}
// swift 4.1
/// A type-erased equatable value
public struct AnyEquatable {
public let base: Any
private let valuesAreEqual: (AnyEquatable, AnyEquatable) -> Bool
public init<E>(_ base: E) where E : Equatable {
self.valuesAreEqual = { AnyEquatable.value($0, ofType: E.self, isEqualTo: $1) }
self.base = base
}
}
extension AnyEquatable: Equatable {
public static func == (lhs: AnyEquatable, rhs: AnyEquatable) -> Bool {
return lhs.baseEqual(to: rhs)
}
}
private extension AnyEquatable {
/// Assumes value is actually Equatable or throws a fatal error
static func value<T: Equatable>(_ value: AnyEquatable, ofType type: T.Type, isEqualTo other: AnyEquatable) -> Bool {
guard let lhv = value.base as? T else {
fatalError("Value of base will always be \(T.self)")
}
guard let rhv = other.base as? T else {
return false
}
return lhv == rhv
}
func baseEqual(to other: AnyEquatable) -> Bool {
return valuesAreEqual(self, other)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment