Skip to content

Instantly share code, notes, and snippets.

@shaps80
Created July 29, 2025 08:47
Show Gist options
  • Save shaps80/6715f0ade2bd6d41d3bc5b507eb7960a to your computer and use it in GitHub Desktop.
Save shaps80/6715f0ade2bd6d41d3bc5b507eb7960a to your computer and use it in GitHub Desktop.
import Foundation
public enum Decode {}
public protocol DecodableDefaultSource: Sendable {
associatedtype Value: Decodable & Sendable
static var defaultValue: Value { get }
}
extension Decode {
@propertyWrapper
public struct Wrapper<Source: DecodableDefaultSource>: Sendable, CustomReflectable {
public typealias Value = Source.Value
public var wrappedValue = Source.defaultValue
public init(wrappedValue: Value = Source.defaultValue) {
self.wrappedValue = wrappedValue
}
public var customMirror: Mirror {
.init(reflecting: wrappedValue)
}
}
}
extension Decode.Wrapper: CustomStringConvertible where Value: CustomStringConvertible {
public var description: String {
wrappedValue.description
}
}
extension Decode.Wrapper: Decodable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
wrappedValue = try container.decode(Value.self)
}
}
internal extension KeyedDecodingContainer {
func decode<T>(_ type: Decode.Wrapper<T>.Type, forKey key: Key) throws -> Decode.Wrapper<T> {
try decodeIfPresent(type, forKey: key) ?? .init()
}
}
extension Decode {
public typealias Source = DecodableDefaultSource
public typealias List = Decodable & ExpressibleByArrayLiteral
public typealias Map = Decodable & ExpressibleByDictionaryLiteral
public enum Sources {
public enum True: Source {
public static var defaultValue: Bool { true }
}
public enum False: Source {
public static var defaultValue: Bool { false }
}
public enum EmptyString: Source {
public static var defaultValue: Swift.String { "" }
}
public enum EmptyArray<T: List>: Source {
public static var defaultValue: T { [] }
}
public enum EmptyDictionary<T: Map>: Source {
public static var defaultValue: T { [:] }
}
}
}
public extension Decode {
typealias True = Wrapper<Sources.True>
typealias False = Wrapper<Sources.False>
typealias String = Wrapper<Sources.EmptyString>
typealias Array<T: List> = Wrapper<Sources.EmptyArray<T>>
typealias Dictionary<T: Map> = Wrapper<Sources.EmptyDictionary<T>>
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment