Last active
June 16, 2021 22:40
-
-
Save BrentMifsud/30c065c944264c5fa618040ef9fc59a6 to your computer and use it in GitHub Desktop.
Allows more control over encoding or omitting nil values in JSON.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Property wrapper that provides control over whether to explicitly encode nil values to json or not. | |
/// - Parameters: | |
/// - wrappedValue: the value to be encoded. | |
/// - encodeNil: Should the value be explicitly encoded as "null" in json if nil. | |
/// - Note: | |
/// Usage: | |
/// | |
/// ```swift | |
/// struct MyStruct: Codable { | |
/// @NilCodable var myNullable: String? // defaults to omitting null (codable default behavior) | |
/// @NilCodable(encodeNil: true) var myEncodedNullable: String? // Encodes "Null" in json | |
/// @NilCodable(encodeNil: false) var myOmittedNullable: String? // Omits Null values from json | |
/// | |
/// func encode(to encoder: Encoder) throws { | |
/// var container = try encoder.container(keyedBy: CodingKeys.self) | |
/// try _myNullable.encode(to: &container, key: .myNullable) | |
/// try _myEncodedNullable.encode(to: &container, key: .myEncodedNullable) | |
/// try _myOmittedNullable.encode(to: &container, key: .myOmittedNullable) | |
/// } | |
/// } | |
/// | |
/// func encodeMyStruct(myStruct: inout MyStruct, encodeNil: Bool) throws { | |
/// // You can set encodeNil either using projectedValue syntax, or with the provided helper method. | |
/// myStruct.$myNullable = encodeNil // Using property wrapper syntax | |
/// myStruct._myNullable.encodeNil(encodeNil) // Using the included helper method | |
/// | |
/// try JSONEncoder().encode(myStruct) | |
/// } | |
/// ``` | |
@propertyWrapper public struct NilCodable<Nullable: Codable>: Codable { | |
public var wrappedValue: Nullable? | |
public var projectedValue: Bool { | |
get { encodeNil } | |
set { encodeNil = newValue } | |
} | |
private var encodeNil: Bool = false | |
/// Property wrapper that provides control over whether to explicitly encode nil values to json or not. | |
/// - Parameters: | |
/// - wrappedValue: the value to be encoded. | |
/// - encodeNil: Should the value be explicitly encoded as "null" in json if nil. | |
/// - Note: | |
/// Usage: | |
/// | |
/// ```swift | |
/// struct MyStruct: Codable { | |
/// @NilCodable var myNullable: String? // defaults to omitting null (codable default behavior) | |
/// @NilCodable(encodeNil: true) var myEncodedNullable: String? // Encodes "Null" in json | |
/// @NilCodable(encodeNil: false) var myOmittedNullable: String? // Omits Null values from json | |
/// | |
/// func encode(to encoder: Encoder) throws { | |
/// var container = try encoder.container(keyedBy: CodingKeys.self) | |
/// try _myNullable.encode(to: &container, key: .myNullable) | |
/// try _myEncodedNullable.encode(to: &container, key: .myEncodedNullable) | |
/// try _myOmittedNullable.encode(to: &container, key: .myOmittedNullable) | |
/// } | |
/// } | |
/// | |
/// func encodeMyStruct(myStruct: inout MyStruct, encodeNil: Bool) throws { | |
/// // You can set encodeNil either using projectedValue syntax, or with the provided helper method. | |
/// myStruct.$myNullable = encodeNil // Using property wrapper syntax | |
/// myStruct._myNullable.encodeNil(encodeNil) // Using the included helper method | |
/// | |
/// try JSONEncoder().encode(myStruct) | |
/// } | |
/// ``` | |
public init(_ wrappedValue: Nullable? = nil, encodeNil: Bool = false) { | |
self.wrappedValue = wrappedValue | |
self.encodeNil = encodeNil | |
} | |
// MARK: Codable Conformance | |
public init(from decoder: Decoder) throws { | |
let container = try decoder.singleValueContainer() | |
self.init(try container.decode(Nullable?.self)) | |
} | |
public func encode(to encoder: Encoder) throws { | |
switch wrappedValue { | |
case let .some(value): | |
try value.encode(to: encoder) | |
case .none: | |
if encodeNil { | |
var container = encoder.singleValueContainer() | |
try container.encodeNil() | |
} | |
} | |
} | |
/// Helper Method for setting `encodeNil`. | |
/// - Note | |
/// Usage: | |
/// ```swift | |
/// _myNilCodable.encodeNil(true) | |
/// ``` | |
public mutating func encodeNil(_ encodeNil: Bool) { | |
self.encodeNil = encodeNil | |
} | |
/// Encodes the `NilCodable` value to the provided keyed coding container. | |
/// - Parameters: | |
/// - container: The keyed coding container to encode to. | |
/// - key: The coding key used to encode. | |
/// - Throws: `EncodingError` | |
public func encode<Key: CodingKey>(to container: inout KeyedEncodingContainer<Key>, key: Key) throws { | |
switch (wrappedValue, encodeNil) { | |
case let (.some(value), _): | |
try container.encode(value, forKey: key) | |
case (.none, true): | |
try container.encodeNil(forKey: key) | |
case (.none, false): | |
try container.encodeIfPresent(wrappedValue, forKey: key) | |
} | |
} | |
/// Encodes the `NilCodable` value to a single value container | |
/// - Parameter container: the single value container to encode to. | |
/// - Throws: `EncodingError` | |
public func encode(to container: inout SingleValueEncodingContainer) throws { | |
switch (wrappedValue, encodeNil) { | |
case let (.some(value), _): | |
try container.encode(value) | |
case (.none, true): | |
try container.encodeNil() | |
case (.none, false): | |
return | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment