Skip to content

Instantly share code, notes, and snippets.

@MahdiBM
Created April 20, 2021 13:11
Show Gist options
  • Save MahdiBM/260a6551f4b9a1af1994f44e305c313e to your computer and use it in GitHub Desktop.
Save MahdiBM/260a6551f4b9a1af1994f44e305c313e to your computer and use it in GitHub Desktop.
PropertyWrapper for Codable to not fail when finding `nil`
import Foundation
//MARK: - EmptyInitializable
protocol EmptyInitializable {
init()
}
extension String: EmptyInitializable {}
extension Bool: EmptyInitializable {}
extension Int: EmptyInitializable {}
extension Array: EmptyInitializable {}
//MARK: - DecodeNilable
@propertyWrapper
struct DecodeNilable<Model> where Model: EmptyInitializable & Codable {
var wrappedValue: Model = .init()
}
extension DecodeNilable: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
wrappedValue = try container.decode(Model.self)
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(wrappedValue)
}
}
extension KeyedDecodingContainer {
func decode<Model>(_ type: DecodeNilable<Model>.Type,
forKey key: Key) throws -> DecodeNilable<Model>
where Model: EmptyInitializable & Codable {
try decodeIfPresent(type, forKey: key) ?? .init()
}
}
//MARK: - Extra conformances
extension DecodeNilable: CustomStringConvertible {
var description: String { .init(reflecting: wrappedValue) }
}
extension DecodeNilable: Equatable where Model: Equatable { }
//MARK: - Examples
/*
Make sure the type conforms to `EmptyInitializable`:
extension String: EmptyInitializable {}
struct MyModel: Codable {
@DecodeNillable var name: String = ""
//if Codable fails to find a value for `name`,
//it will assign value of .init() to `name`.
//In this case we have an String, and
//String.init() is equal to ""
//so `name` will be equal to `""` if Codable fails.
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment