Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@BrentMifsud
Last active June 16, 2021 22:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BrentMifsud/30c065c944264c5fa618040ef9fc59a6 to your computer and use it in GitHub Desktop.
Save BrentMifsud/30c065c944264c5fa618040ef9fc59a6 to your computer and use it in GitHub Desktop.
Allows more control over encoding or omitting nil values in JSON.
/// 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