Skip to content

Commit

Permalink
Add encodeIfPresent Support to URLEncodedFormEncoder (#3779)
Browse files Browse the repository at this point in the history
### Issue Link 🔗
Fixes #3778

### Goals ⚽
The encoding of synthesized `Encodable` values is different than
`[String: String?]` so we were missing coverage for `encodeIfPresent`,
causing `Optional` values to be dropped. This PR adds the missing
support.

### Implementation Details 🚧
Added all of the `encodeIfPresent` overloads to
`_URLEncodedFormEncoder.KeyedContainer` so that the protocol uses the
custom implementation.

### Testing Details 🔍
Tests added for `Encodable` values with optional properties.
  • Loading branch information
jshier authored Sep 20, 2023
1 parent e58140b commit 1df2a75
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 0 deletions.
68 changes: 68 additions & 0 deletions Source/URLEncodedFormEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,74 @@ extension _URLEncodedFormEncoder.KeyedContainer: KeyedEncodingContainerProtocol
try encode(nilValue, forKey: key)
}

func encodeIfPresent(_ value: Bool?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}

func encodeIfPresent(_ value: String?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}

func encodeIfPresent(_ value: Double?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}

func encodeIfPresent(_ value: Float?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}

func encodeIfPresent(_ value: Int?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}

func encodeIfPresent(_ value: Int8?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}

func encodeIfPresent(_ value: Int16?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}

func encodeIfPresent(_ value: Int32?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}

func encodeIfPresent(_ value: Int64?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}

func encodeIfPresent(_ value: UInt?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}

func encodeIfPresent(_ value: UInt8?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}

func encodeIfPresent(_ value: UInt16?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}

func encodeIfPresent(_ value: UInt32?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}

func encodeIfPresent(_ value: UInt64?, forKey key: Key) throws {
try _encodeIfPresent(value, forKey: key)
}

func encodeIfPresent<Value>(_ value: Value?, forKey key: Key) throws where Value: Encodable {
try _encodeIfPresent(value, forKey: key)
}

func _encodeIfPresent<Value>(_ value: Value?, forKey key: Key) throws where Value: Encodable {
if let value = value {
try encode(value, forKey: key)
} else {
try encodeNil(forKey: key)
}
}

func encode<T>(_ value: T, forKey key: Key) throws where T: Encodable {
var container = nestedSingleValueEncoder(for: key)
try container.encode(value)
Expand Down
49 changes: 49 additions & 0 deletions Tests/ParameterEncoderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,17 @@ final class URLEncodedFormEncoderTests: BaseTestCase {
XCTAssertEqual(result.success, "")
}

func testThatNilCanBeEncodedInSynthesizedEncodableByDroppingTheKeyByDefault() {
// Given
let encoder = URLEncodedFormEncoder()

// When
let result = Result<String, Error> { try encoder.encode(OptionalEncodableStruct()) }

// Then
XCTAssertEqual(result.success, "one=one")
}

func testThatNilCanBeEncodedAsNull() {
// Given
let encoder = URLEncodedFormEncoder(nilEncoding: .null)
Expand All @@ -864,6 +875,17 @@ final class URLEncodedFormEncoderTests: BaseTestCase {
XCTAssertEqual(result.success, "a=null")
}

func testThatNilCanBeEncodedInSynthesizedEncodableAsNull() {
// Given
let encoder = URLEncodedFormEncoder(nilEncoding: .null)

// When
let result = Result<String, Error> { try encoder.encode(OptionalEncodableStruct()) }

// Then
XCTAssertEqual(result.success, "one=one&two=null")
}

func testThatNilCanBeEncodedByDroppingTheKey() {
// Given
let encoder = URLEncodedFormEncoder(nilEncoding: .dropKey)
Expand All @@ -876,6 +898,17 @@ final class URLEncodedFormEncoderTests: BaseTestCase {
XCTAssertEqual(result.success, "")
}

func testThatNilCanBeEncodedInSynthesizedEncodableByDroppingTheKey() {
// Given
let encoder = URLEncodedFormEncoder(nilEncoding: .dropKey)

// When
let result = Result<String, Error> { try encoder.encode(OptionalEncodableStruct()) }

// Then
XCTAssertEqual(result.success, "one=one")
}

func testThatNilCanBeEncodedByDroppingTheValue() {
// Given
let encoder = URLEncodedFormEncoder(nilEncoding: .dropValue)
Expand All @@ -888,6 +921,17 @@ final class URLEncodedFormEncoderTests: BaseTestCase {
XCTAssertEqual(result.success, "a=")
}

func testThatNilCanBeEncodedInSynthesizedEncodableByDroppingTheValue() {
// Given
let encoder = URLEncodedFormEncoder(nilEncoding: .dropValue)

// When
let result = Result<String, Error> { try encoder.encode(OptionalEncodableStruct()) }

// Then
XCTAssertEqual(result.success, "one=one&two=")
}

func testThatSpacesCanBeEncodedAsPluses() {
// Given
let encoder = URLEncodedFormEncoder(spaceEncoding: .plusReplaced)
Expand Down Expand Up @@ -1093,6 +1137,11 @@ private struct NestedEncodableStruct: Encodable {
let a = "a"
}

private struct OptionalEncodableStruct: Encodable {
let one = "one"
let two: String? = nil
}

private class EncodableSuperclass: Encodable {
let one = "one"
let two = 2
Expand Down

0 comments on commit 1df2a75

Please sign in to comment.