Last active
October 19, 2023 12:04
-
-
Save mredig/f6d9efb196a25d857fe04a28357551a6 to your computer and use it in GitHub Desktop.
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
import Foundation | |
/// Makes an Encodable and Decodable properties encode to `null` instead of omitting the value altogether. | |
@propertyWrapper | |
public struct NullCodable<T> { | |
public var wrappedValue: T? | |
public init(wrappedValue: T?){ | |
self.wrappedValue = wrappedValue | |
} | |
} | |
extension NullCodable: Encodable where T: Encodable { | |
public func encode(to encoder: Encoder) throws { | |
var container = encoder.singleValueContainer() | |
switch wrappedValue { | |
case .some(let value): | |
try container.encode(value) | |
case .none: | |
try container.encodeNil() | |
} | |
} | |
} | |
extension NullCodable: Decodable where T: Decodable { | |
public init(from decoder: Decoder) throws { | |
let container = try decoder.singleValueContainer() | |
self.wrappedValue = try? container.decode(T.self) | |
} | |
} | |
struct WithNullCodable: Codable { | |
let value: String | |
@NullCodable var value2: String? | |
} | |
struct WithoutNullCodable: Codable { | |
let value: String | |
let value2: String? | |
} | |
let withA = WithNullCodable(value: "blah", value2: "bleh") | |
let withB = WithNullCodable(value: "blah", value2: nil) | |
let withoutA = WithoutNullCodable(value: "blah", value2: "bleh") | |
let withoutB = WithoutNullCodable(value: "blah", value2: nil) | |
func jsonString<Item: Encodable>(from item: Item) -> String { | |
let data = try! JSONEncoder().encode(item) | |
return String(data: data, encoding: .utf8)! | |
} | |
func object<Item: Decodable>(from string: String) -> Item { | |
let decoder = JSONDecoder() | |
let data = string.data(using: .utf8)! | |
return try! decoder.decode(Item.self, from: data) | |
} | |
let withAStr = jsonString(from: withA) | |
let withBStr = jsonString(from: withB) | |
print(withAStr) // {"value":"blah","value2":"bleh"} | |
print(withBStr) // {"value":"blah","value2":null} | |
let withoutAStr = jsonString(from: withoutA) | |
let withoutBStr = jsonString(from: withoutB) | |
print(withoutAStr) // {"value":"blah","value2":"bleh"} | |
print(withoutBStr) // {"value":"blah"} | |
let withADecoded: WithNullCodable = object(from: withAStr) | |
let withBDecoded: WithNullCodable = object(from: withBStr) | |
print(withADecoded) | |
print(withBDecoded) | |
let withoutADecoded: WithoutNullCodable = object(from: withoutAStr) | |
let withoutBDecoded: WithoutNullCodable = object(from: withoutBStr) | |
print(withoutADecoded) | |
print(withoutBDecoded) |
Without the singleValueContainer()
it might be trying to decode a value embedded within another container, like an array or dictionary.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for quick answer! What do you mean by
flattening the wrapper of the content
?