ソースを参照

Add ArrayEncoding.custom to URLEncodedFormEncoder and ParameterEncoding (#3699)

### Goals :soccer:
Let the user define a custom key formatting for arrays.

### Implementation Details :construction:
This helps for uncommon formats such as in the AWS API where it expects
Array.1.Key or Array.member.1.Key. Rather than add specifics for those,
providing a simple customization helps overcome this.

This follows other `.custom` conventions in the encoding options.

### Testing Details :mag:
Added a test under
`ParameterEncodingTests.testThatArraysCanBeEncodedWithCustomClosure`
that covers the change consistent with the other array encoding tests.

---------

Co-authored-by: Jon Shier <jon@jonshier.com>
Mike Naquin 2 年 前
コミット
fd87f7ce74

+ 4 - 0
Source/ParameterEncoding.swift

@@ -87,6 +87,8 @@ public struct URLEncoding: ParameterEncoding {
         case noBrackets
         /// Brackets containing the item index are appended. This matches the jQuery and Node.js behavior.
         case indexInBrackets
+        /// Provide a custom array key encoding with the given closure.
+        case custom((_ key: String, _ index: Int) -> String)
 
         func encode(key: String, atIndex index: Int) -> String {
             switch self {
@@ -96,6 +98,8 @@ public struct URLEncoding: ParameterEncoding {
                 return key
             case .indexInBrackets:
                 return "\(key)[\(index)]"
+            case let .custom(encoding): 
+                return encoding(key, index)
             }
         }
     }

+ 3 - 0
Source/URLEncodedFormEncoder.swift

@@ -60,6 +60,8 @@ public final class URLEncodedFormEncoder {
         case noBrackets
         /// Brackets containing the item index are appended. This matches the jQuery and Node.js behavior.
         case indexInBrackets
+        /// Provide a custom array key encoding with the given closure.
+        case custom((_ key: String, _ index: Int) -> String)
 
         /// Encodes the key according to the encoding.
         ///
@@ -73,6 +75,7 @@ public final class URLEncodedFormEncoder {
             case .brackets: return "\(key)[]"
             case .noBrackets: return key
             case .indexInBrackets: return "\(key)[\(index)]"
+            case let .custom(encoding): return encoding(key, index)
             }
         }
     }

+ 14 - 0
Tests/ParameterEncoderTests.swift

@@ -624,6 +624,20 @@ final class URLEncodedFormEncoderTests: BaseTestCase {
         XCTAssertEqual(result.success, "array=1&array=2")
     }
 
+    func testThatArraysCanBeEncodedWithCustomClosure() {
+        // Given
+        let encoder = URLEncodedFormEncoder(arrayEncoding: .custom({ key, index in
+          "\(key).\(index + 1)"
+        }))
+        let parameters = ["array": [1, 2]]
+
+        // When
+        let result = Result<String, Error> { try encoder.encode(parameters) }
+
+        // Then
+        XCTAssertEqual(result.success, "array.1=1&array.2=2")
+    }
+
     func testThatBoolsCanBeLiteralEncoded() {
         // Given
         let encoder = URLEncodedFormEncoder(boolEncoding: .literal)

+ 14 - 0
Tests/ParameterEncodingTests.swift

@@ -186,6 +186,20 @@ final class URLParameterEncodingTestCase: ParameterEncodingTestCase {
         XCTAssertEqual(urlRequest.url?.query, "foo=a&foo=1&foo=1")
     }
 
+    func testURLParameterEncodeStringKeyArrayValueParameterWithCustomClosure() throws {
+        // Given
+        let encoding = URLEncoding(arrayEncoding: .custom({ key, index in
+          "\(key).\(index + 1)"
+        }))
+        let parameters = ["foo": ["a", 1, true]]
+
+        // When
+        let urlRequest = try encoding.encode(urlRequest, with: parameters)
+
+        // Then
+        XCTAssertEqual(urlRequest.url?.query, "foo.1=a&foo.2=1&foo.3=1")
+    }
+
     func testURLParameterEncodeStringKeyDictionaryValueParameter() throws {
         // Given
         let parameters = ["foo": ["bar": 1]]