Skip to content

Instantly share code, notes, and snippets.

@IanKeen
Last active June 25, 2019 16:47
Show Gist options
  • Save IanKeen/b4f20e7d7b544e564754b1a0ca4d6f92 to your computer and use it in GitHub Desktop.
Save IanKeen/b4f20e7d7b544e564754b1a0ca4d6f92 to your computer and use it in GitHub Desktop.
Brute force generic Codable value that lets you work in 1 type while maintaining the raw type
public typealias LosslessStringCodable = LosslessStringConvertible & Codable
public struct Value<T: LosslessStringCodable>: Codable {
private let type: LosslessStringCodable.Type
public var value: T
public init(from decoder: Decoder) throws {
do {
self.value = try T.init(from: decoder)
self.type = T.self
} catch let error {
func decode<T: LosslessStringCodable>(_: T.Type) -> (Decoder) -> LosslessStringCodable? {
return { try? T.init(from: $0) }
}
let types: [(Decoder) -> LosslessStringCodable?] = [
decode(String.self),
decode(Bool.self),
decode(Int.self),
decode(Int8.self),
decode(Int16.self),
decode(Int64.self),
decode(UInt.self),
decode(UInt8.self),
decode(UInt16.self),
decode(UInt64.self),
decode(Double.self),
decode(Float.self),
]
guard
let rawValue = types.lazy.compactMap({ $0(decoder) }).first,
let value = T.init("\(rawValue)")
else { throw error }
self.value = value
self.type = Swift.type(of: rawValue)
}
}
public func encode(to encoder: Encoder) throws {
let string = String(describing: value)
guard let original = type.init(string) else {
let description = "Unable to encode '\(value)' back to source type '\(type)'"
throw EncodingError.invalidValue(string, .init(codingPath: [], debugDescription: description))
}
try original.encode(to: encoder)
}
}
// The json uses a `String` but we want an `Int`
struct Object: Codable {
var property: Value<Int>
}
let json = """
{"property": "123"}
""".data(using: .utf8)!
var obj = try! JSONDecoder().decode(Object.self, from: json)
print(obj.property.value) // the String was decoded as an Int
obj.property.value *= 2
let data = try! JSONEncoder().encode(obj)
print(String(data: data, encoding: .utf8)!) // the Int was encoded back to a String
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment