Skip to content

Commit 39fa3ec

Browse files
authored
Support NSNull in OpenAPIContainer types (#109)
### Motivation When receiving containers from adopter code, there might be NSNull values, which represent a nil value. Previously, this value would not be treated as nil, instead it'd throw an error as an unrecognized type. ### Modifications Handle NSNull and treat it as nil. ### Result You can provide a container with an NSNull nested value and it'll get encoded correctly. ### Test Plan Added a unit test.
1 parent e80046b commit 39fa3ec

File tree

2 files changed

+38
-0
lines changed

2 files changed

+38
-0
lines changed

Sources/OpenAPIRuntime/Base/OpenAPIValue.swift

+17
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15+
#if canImport(Foundation)
16+
#if canImport(Darwin)
17+
import class Foundation.NSNull
18+
#else
19+
@preconcurrency import class Foundation.NSNull
20+
#endif
21+
#endif
22+
1523
/// A container for a value represented by JSON Schema.
1624
///
1725
/// Contains an untyped JSON value. In some cases, the structure of the data
@@ -62,6 +70,9 @@ public struct OpenAPIValueContainer: Codable, Hashable, Sendable {
6270
/// - Throws: When the value is not supported.
6371
static func tryCast(_ value: (any Sendable)?) throws -> (any Sendable)? {
6472
guard let value = value else { return nil }
73+
#if canImport(Foundation)
74+
if value is NSNull { return value }
75+
#endif
6576
if let array = value as? [(any Sendable)?] { return try array.map(tryCast(_:)) }
6677
if let dictionary = value as? [String: (any Sendable)?] { return try dictionary.mapValues(tryCast(_:)) }
6778
if let value = tryCastPrimitiveType(value) { return value }
@@ -123,6 +134,12 @@ public struct OpenAPIValueContainer: Codable, Hashable, Sendable {
123134
try container.encodeNil()
124135
return
125136
}
137+
#if canImport(Foundation)
138+
if value is NSNull {
139+
try container.encodeNil()
140+
return
141+
}
142+
#endif
126143
switch value {
127144
case let value as Bool: try container.encode(value)
128145
case let value as Int: try container.encode(value)

Tests/OpenAPIRuntimeTests/Base/Test_OpenAPIValue.swift

+21
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414
import XCTest
15+
#if canImport(Foundation)
16+
#if canImport(Darwin)
17+
import class Foundation.NSNull
18+
#else
19+
@preconcurrency import class Foundation.NSNull
20+
#endif
21+
#endif
1522
@_spi(Generated) @testable import OpenAPIRuntime
1623

1724
final class Test_OpenAPIValue: Test_Runtime {
@@ -22,6 +29,10 @@ final class Test_OpenAPIValue: Test_Runtime {
2229
_ = OpenAPIValueContainer(1)
2330
_ = OpenAPIValueContainer(4.5)
2431

32+
#if canImport(Foundation)
33+
XCTAssertEqual(try OpenAPIValueContainer(unvalidatedValue: NSNull()).value as? NSNull, NSNull())
34+
#endif
35+
2536
_ = try OpenAPIValueContainer(unvalidatedValue: ["hello"])
2637
_ = try OpenAPIValueContainer(unvalidatedValue: ["hello": "world"])
2738

@@ -60,6 +71,16 @@ final class Test_OpenAPIValue: Test_Runtime {
6071
"""#
6172
try _testPrettyEncoded(container, expectedJSON: expectedString)
6273
}
74+
#if canImport(Foundation)
75+
func testEncodingNSNull() throws {
76+
let value = NSNull()
77+
let container = try OpenAPIValueContainer(unvalidatedValue: value)
78+
let expectedString = #"""
79+
null
80+
"""#
81+
try _testPrettyEncoded(container, expectedJSON: expectedString)
82+
}
83+
#endif
6384

6485
func testEncoding_container_failure() throws {
6586
struct Foobar: Equatable {}

0 commit comments

Comments
 (0)