Skip to content

Instantly share code, notes, and snippets.

@liuzhida33
Created September 8, 2022 08:47

Revisions

  1. liuzhida33 created this gist Sep 8, 2022.
    88 changes: 88 additions & 0 deletions CodableDefault.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,88 @@

    protocol CodableDefaultSource {
    associatedtype Value: Codable
    static var defaultValue: Value { get }
    }

    enum CodableDefault {}

    extension CodableDefault {
    @propertyWrapper
    struct Wrapper<Source: CodableDefaultSource> {
    typealias Value = Source.Value
    var wrappedValue = Source.defaultValue
    }
    }

    extension CodableDefault.Wrapper: Decodable {
    init(from decoder: Decoder) throws {
    let container = try decoder.singleValueContainer()
    wrappedValue = try container.decode(Value.self)
    }
    }

    extension CodableDefault.Wrapper: Encodable {
    func encode(to encoder: Encoder) throws {
    var container = encoder.singleValueContainer()
    try container.encode(wrappedValue)
    }
    }

    extension KeyedDecodingContainer {
    func decode<T>(_ type: CodableDefault.Wrapper<T>.Type,
    forKey key: Key) throws -> CodableDefault.Wrapper<T> {
    try decodeIfPresent(type, forKey: key) ?? .init()
    }
    }

    extension KeyedEncodingContainer {
    mutating func encode<T>(_ value: CodableDefault.Wrapper<T>, forKey key: Key) throws {
    try encode(value.wrappedValue, forKey: key)
    }
    }

    extension CodableDefault {
    typealias Source = CodableDefaultSource
    typealias List = Codable & ExpressibleByArrayLiteral
    typealias Map = Codable & ExpressibleByDictionaryLiteral

    enum Sources {
    enum True: Source {
    static var defaultValue: Bool { true }
    }

    enum False: Source {
    static var defaultValue: Bool { false }
    }

    enum EmptyString: Source {
    static var defaultValue: String { "" }
    }

    enum EmptyList<T: List>: Source {
    static var defaultValue: T { [] }
    }

    enum EmptyMap<T: Map>: Source {
    static var defaultValue: T { [:] }
    }

    enum Zero: Source {
    static var defaultValue: Int { 0 }
    }

    enum One: Source {
    static var defaultValue: Int { 1 }
    }
    }
    }

    extension CodableDefault {
    typealias True = Wrapper<Sources.True>
    typealias False = Wrapper<Sources.False>
    typealias EmptyString = Wrapper<Sources.EmptyString>
    typealias EmptyList<T: List> = Wrapper<Sources.EmptyList<T>>
    typealias EmptyMap<T: Map> = Wrapper<Sources.EmptyMap<T>>
    typealias Zero = Wrapper<Sources.Zero>
    typealias One = Wrapper<Sources.One>
    }