[Pre-Pitch] Codable Customization using PropertyWrappers

TIL that code synthesized for Codable is as malleable as other pieces of code. Thanks!

I've rewritten my sample code above, with full support for missing values:

It works!
import Foundation

// A basic property wrapper
@propertyWrapper struct Wrapped<T> {
    var wrappedValue: T
}

// Make Wrapped support Decodable
extension Wrapped: Decodable where T: Decodable {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        wrappedValue = try container.decode(T.self)
    }
}

// Make Wrapped support decoding nil and missing values
protocol OmittableType {
    static var nilValue: Self { get }
}
extension Wrapped: OmittableType where T: ExpressibleByNilLiteral {
    static var nilValue: Self { .init(wrappedValue: nil)}
}
extension KeyedDecodingContainer {
    func decode<T>(_ type: T.Type, forKey key: KeyedDecodingContainer<K>.Key) throws -> T where T : Decodable, T: OmittableType {
        return try decodeIfPresent(T.self, forKey: key) ?? T.nilValue
    }
}

// Setup
let decoder = JSONDecoder()
let jsonWithValue = #"{"property":"Hello"}"#.data(using: .utf8)!
let jsonWithNull =  #"{"property":null}"#.data(using: .utf8)!
let jsonEmpty =     #"{}"#.data(using: .utf8)!

// A regular decodable struct: full success
struct Struct: Decodable {
    var property: String?
}
try! decoder.decode(Struct.self, from: jsonWithValue).property        // "Hello"
try! decoder.decode(Struct.self, from: jsonWithNull).property         // nil
try! decoder.decode(Struct.self, from: jsonEmpty).property            // nil

// A decodable struct with wrapped property: full success
struct WrappedStruct: Decodable {
    @Wrapped var property: String?
}
try! decoder.decode(WrappedStruct.self, from: jsonWithValue).property // "Hello"
try! decoder.decode(WrappedStruct.self, from: jsonWithNull).property  // nil
try! decoder.decode(WrappedStruct.self, from: jsonEmpty).property     // nil
4 Likes