Skip to content

Commit 15f8f34

Browse files
committed
bug fixes for AnyCodable null-like types
1 parent 2ee233c commit 15f8f34

File tree

2 files changed

+50
-5
lines changed

2 files changed

+50
-5
lines changed

Sources/OpenAPIKitCore/AnyCodable/AnyCodable.swift

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ import Foundation
3131
You can encode or decode mixed-type values in dictionaries
3232
and other collections that require `Encodable` or `Decodable` conformance
3333
by declaring their contained type to be `AnyCodable`.
34+
35+
Note that there are some caveats related to the fact that this type centers around
36+
encoding/decoding values. For example, some technically distinct nil-like types
37+
are all encoded as `nil` and compare equally under the `AnyCodable` type:
38+
- `nil`
39+
- `NSNull()`
40+
- `Void()`
3441
*/
3542
public struct AnyCodable: @unchecked Sendable {
3643
// IMPORTANT:
@@ -54,7 +61,11 @@ protocol _Optional {
5461
}
5562

5663
extension Optional: _Optional {
57-
var isNil: Bool { return self == nil }
64+
var isNil: Bool { self == nil }
65+
}
66+
67+
extension NSNull: _Optional {
68+
var isNil: Bool { true }
5869
}
5970

6071
extension AnyCodable: Encodable {
@@ -178,8 +189,28 @@ extension AnyCodable: Decodable {
178189
}
179190
}
180191

192+
func isNilEquivalent(value: AnyCodable) -> Bool {
193+
let valueIsNil: Bool
194+
195+
if let optionalValue = value.value as? _Optional,
196+
optionalValue.isNil {
197+
valueIsNil = true
198+
} else if let _ = value.value as? Void {
199+
valueIsNil = true
200+
} else {
201+
valueIsNil = false
202+
}
203+
204+
return valueIsNil
205+
}
206+
181207
extension AnyCodable: Equatable {
182208
public static func == (lhs: AnyCodable, rhs: AnyCodable) -> Bool {
209+
// special case for nil
210+
if isNilEquivalent(value: lhs) && isNilEquivalent(value: rhs) {
211+
return true
212+
}
213+
183214
switch (lhs.value, rhs.value) {
184215
case is (Void, Void):
185216
return true

Tests/AnyCodableTests/AnyCodableTests.swift

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ class AnyCodableTests: XCTestCase {
1414
}
1515

1616
func testEquality() throws {
17-
XCTAssertEqual(AnyCodable(()), AnyCodable(()))
17+
// nil, NSNull(), and Void() all encode as "null" and
18+
// compare equally.
19+
XCTAssertEqual(AnyCodable(nil), AnyCodable(nil))
20+
XCTAssertEqual(AnyCodable(nil), AnyCodable(NSNull()))
21+
XCTAssertEqual(AnyCodable(nil), AnyCodable(()))
22+
1823
XCTAssertEqual(AnyCodable(true), AnyCodable(true))
1924
XCTAssertEqual(AnyCodable(2), AnyCodable(2))
2025
XCTAssertEqual(AnyCodable(Int8(2)), AnyCodable(Int8(2)))
@@ -53,7 +58,8 @@ class AnyCodableTests: XCTestCase {
5358
"a": "alpha",
5459
"b": "bravo",
5560
"c": "charlie"
56-
}
61+
},
62+
"null": null
5763
}
5864
""".data(using: .utf8)!
5965
let decoder = JSONDecoder()
@@ -133,7 +139,8 @@ class AnyCodableTests: XCTestCase {
133139
"a": "alpha",
134140
"b": "bravo",
135141
"c": "charlie"
136-
}
142+
},
143+
"null": null
137144
}
138145
""".data(using: .utf8)!
139146

@@ -146,6 +153,7 @@ class AnyCodableTests: XCTestCase {
146153
XCTAssertEqual(dictionary["string"]?.value as! String, "string")
147154
XCTAssertEqual(dictionary["array"]?.value as! [Int], [1, 2, 3])
148155
XCTAssertEqual(dictionary["nested"]?.value as! [String: String], ["a": "alpha", "b": "bravo", "c": "charlie"])
156+
XCTAssertEqual(dictionary["null"], AnyCodable(nil))
149157
}
150158

151159
func testJSONEncoding() throws {
@@ -159,6 +167,9 @@ class AnyCodableTests: XCTestCase {
159167
"b": "bravo",
160168
"c": "charlie",
161169
]),
170+
"null": nil,
171+
"void": .init(Void()),
172+
"nsnull": .init(NSNull())
162173
]
163174

164175
let result = try testStringFromEncoding(of: dictionary)
@@ -179,7 +190,10 @@ class AnyCodableTests: XCTestCase {
179190
"b" : "bravo",
180191
"c" : "charlie"
181192
},
182-
"string" : "string"
193+
"nsnull" : null,
194+
"null" : null,
195+
"string" : "string",
196+
"void" : null
183197
}
184198
"""
185199
)

0 commit comments

Comments
 (0)