Skip to content

Instantly share code, notes, and snippets.

@jordanebelanger
Created December 20, 2019 14:50
Show Gist options
  • Save jordanebelanger/8469d397dc43018edfc7641e871460b9 to your computer and use it in GitHub Desktop.
Save jordanebelanger/8469d397dc43018edfc7641e871460b9 to your computer and use it in GitHub Desktop.
import Foundation
enum Nullable<Value: Codable>: Codable {
case some(_ value: Value)
case null
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if container.decodeNil() {
self = .null
} else {
self = .some(try container.decode(Value.self))
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .some(let value):
try container.encode(value)
case .null:
try container.encodeNil()
}
}
}
struct SomePatchRequest: Codable {
var nullableIntProperty: Nullable<Int>?
}
let json = #"{"nullableIntProperty": null}"#
//let json = #"{}"#
let data = json.data(using: .utf8)!
// The `Nullable` decode function won't be called since null is treated the same as if the value was missing entirely
// and therefore does not need to be decoded.
let patchRequest = try JSONDecoder().decode(SomePatchRequest.self, from: data)
print(patchRequest)
@jordanebelanger
Copy link
Author

You can achieve this using a custom decoder for the parent object like this

struct SomeCustomDecoderPatchRequest: Decodable {
    var nullableIntProperty: Nullable<Int>?
    
    enum DecodingKeys: CodingKey { case nullableIntProperty }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: DecodingKeys.self)
        do {
            nullableIntProperty = try container.decode(Nullable<Int>.self, forKey: .nullableIntProperty)
        } catch DecodingError.keyNotFound {
            nullableIntProperty = nil
        }
    }
}

Is there a better way?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment