Last active
May 22, 2024 20:06
-
-
Save IanKeen/26605d6b7bb4fa97100e45f7098d84d2 to your computer and use it in GitHub Desktop.
PropertyWrapper: LosslessCodable attempts to brute force convert the incoming value to the required type - the underlying type is maintained and used when encoding the value back
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 typealias LosslessStringCodable = LosslessStringConvertible & Codable | |
@propertyWrapper | |
public struct LosslessCodable<Value: LosslessStringCodable>: Codable { | |
private let type: LosslessStringCodable.Type | |
public var wrappedValue: Value | |
public init(wrappedValue: Value) { | |
self.wrappedValue = wrappedValue | |
self.type = Value.self | |
} | |
public init(from decoder: Decoder) throws { | |
do { | |
self.wrappedValue = try Value.init(from: decoder) | |
self.type = Value.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 = Value.init("\(rawValue)") | |
else { throw error } | |
self.wrappedValue = value | |
self.type = Swift.type(of: rawValue) | |
} | |
} | |
public func encode(to encoder: Encoder) throws { | |
let string = String(describing: wrappedValue) | |
guard let original = type.init(string) else { | |
let description = "Unable to encode '\(wrappedValue)' back to source type '\(type)'" | |
throw EncodingError.invalidValue(string, .init(codingPath: [], debugDescription: description)) | |
} | |
try original.encode(to: encoder) | |
} | |
} | |
extension LosslessCodable: Equatable where Value: Equatable { | |
public static func ==(lhs: Self, rhs: Self) -> Bool { | |
return lhs.wrappedValue == rhs.wrappedValue | |
} | |
} | |
extension Optional: CustomStringConvertible where Wrapped: CustomStringConvertible { | |
public var description: String { | |
return self?.description ?? "" | |
} | |
} | |
extension Optional: LosslessStringConvertible where Wrapped: LosslessStringConvertible { | |
public init?(_ description: String) { | |
self = Wrapped(description) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example:
Decoding would convert the
String
toInt
Encoding would convert the
Int
back toString