metatext-app-ios-iphone-ipad/Shared/Property Wrappers/DecodableDefault.swift

98 lines
2.8 KiB
Swift
Raw Normal View History

2020-08-24 01:39:52 +02:00
// Copyright © 2020 Metabolist. All rights reserved.
import Foundation
// Thank you https://www.swiftbysundell.com/tips/default-decoding-values/
protocol DecodableDefaultSource {
associatedtype Value: Decodable
static var defaultValue: Value { get }
}
enum DecodableDefault {}
// swiftlint:disable nesting
extension DecodableDefault {
@propertyWrapper
struct Wrapper<Source: DecodableDefaultSource> {
typealias Value = Source.Value
var wrappedValue = Source.defaultValue
}
}
extension DecodableDefault {
typealias Source = DecodableDefaultSource
typealias List = Decodable & ExpressibleByArrayLiteral
typealias Map = Decodable & 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 StatusVisibilityPublic: Source {
static var defaultValue: Status.Visibility { .public }
}
enum ExpandMediaDefault: Source {
static var defaultValue: MastodonPreferences.ExpandMedia { .default }
}
}
}
// swiftlint:enable nesting
extension DecodableDefault {
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 StatusVisibilityPublic = Wrapper<Sources.StatusVisibilityPublic>
typealias ExpandMediaDefault = Wrapper<Sources.ExpandMediaDefault>
}
extension DecodableDefault.Wrapper: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
wrappedValue = try container.decode(Value.self)
}
}
extension DecodableDefault.Wrapper: Equatable where Value: Equatable {}
extension DecodableDefault.Wrapper: Hashable where Value: Hashable {}
extension DecodableDefault.Wrapper: Encodable where Value: Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(wrappedValue)
}
}
extension KeyedDecodingContainer {
func decode<T>(_ type: DecodableDefault.Wrapper<T>.Type,
forKey key: Key) throws -> DecodableDefault.Wrapper<T> {
try decodeIfPresent(type, forKey: key) ?? .init()
}
}