Explorar o código

Implementing protocol EmptyResponse so that an empty response value can be returned when an empty http status code is encountered (#2664)

* Implemented protocol EmptyResponse so that an empty response value can be returned without requiring that T be dervied from Empty

(cherry picked from commit 7048265f881e31722b522deb8291f7a638f35945)

* adding isInvalidEmptyResponse convenience vars

* adding tests for DecodableResponseSerializer that test empty http status codes against types that do and do not conform to EmptyResponse

* Empty Response has a func that returns Self instead of a property

* code review: name changes an unnecessary things removed

* code review: removed sorta-duplicate test
James Van Noord %!s(int64=6) %!d(string=hai) anos
pai
achega
8006676112

+ 11 - 1
Source/ResponseSerialization.swift

@@ -515,12 +515,22 @@ extension DownloadRequest {
 }
 
 // MARK: - Empty
+/// A protocol for a type representing an empty response. Use `T.emptyValue` to get an instance.
+public protocol EmptyResponse {
+    static func emptyValue() -> Self
+}
 
 /// A type representing an empty response. Use `Empty.value` to get the instance.
 public struct Empty: Decodable {
     public static let value = Empty()
 }
 
+extension Empty: EmptyResponse {
+    public static func emptyValue() -> Empty {
+        return value
+    }
+}
+
 // MARK: - DataDecoder Protocol
 
 /// Any type which can decode `Data`.
@@ -575,7 +585,7 @@ public final class DecodableResponseSerializer<T: Decodable>: ResponseSerializer
                 throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)
             }
 
-            guard let emptyValue = Empty.value as? T else {
+            guard let emptyResponseType = T.self as? EmptyResponse.Type, let emptyValue = emptyResponseType.emptyValue() as? T else {
                 throw AFError.responseSerializationFailed(reason: .invalidEmptyResponse(type: "\(T.self)"))
             }
 

+ 10 - 0
Tests/AFError+AlamofireTests.swift

@@ -137,6 +137,11 @@ extension AFError {
         return false
     }
 
+    var isInvalidEmptyResponse: Bool {
+        if case let .responseSerializationFailed(reason) = self, reason.isInvalidEmptyResponse { return true }
+        return false
+    }
+
     // ResponseValidationFailureReason
 
     var isDataFileNil: Bool {
@@ -280,6 +285,11 @@ extension AFError.ResponseSerializationFailureReason {
         if case .decodingFailed = self { return true }
         return false
     }
+
+    var isInvalidEmptyResponse: Bool {
+        if case .invalidEmptyResponse = self { return true }
+        return false
+    }
 }
 
 // MARK: -

+ 60 - 1
Tests/ResponseSerializationTests.swift

@@ -441,10 +441,21 @@ final class DataResponseSerializationTestCase: BaseTestCase {
 
 // MARK: -
 
+// used by testThatDecodableResponseSerializerSucceedsWhenDataIsNilWithEmptyResponseConformingTypeAndEmptyResponseStatusCode
+extension Bool: EmptyResponse {
+    public static func emptyValue() -> Bool {
+        return true
+    }
+}
+
 final class DecodableResponseSerializerTests: BaseTestCase {
     private let error = AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)
 
-    struct DecodableValue: Decodable {
+    struct DecodableValue: Decodable, EmptyResponse {
+        static func emptyValue() -> DecodableValue {
+            return DecodableValue(string: "")
+        }
+
         let string: String
     }
 
@@ -555,6 +566,20 @@ final class DecodableResponseSerializerTests: BaseTestCase {
     }
 
     func testThatDecodableResponseSerializerSucceedsWhenDataIsNilWithEmptyResponseStatusCode() {
+        // Given
+        let serializer = DecodableResponseSerializer<DecodableValue>()
+        let response = HTTPURLResponse(statusCode: 204)
+
+        // When
+        let result = Result { try serializer.serialize(request: nil, response: response, data: nil, error: nil) }
+
+        // Then
+        XCTAssertTrue(result.isSuccess)
+        XCTAssertNotNil(result.value)
+        XCTAssertNil(result.error)
+    }
+
+   func testThatDecodableResponseSerializerSucceedsWhenDataIsNilWithEmptyTypeAndEmptyResponseStatusCode() {
         // Given
         let serializer = DecodableResponseSerializer<Empty>()
         let response = HTTPURLResponse(statusCode: 204)
@@ -567,6 +592,40 @@ final class DecodableResponseSerializerTests: BaseTestCase {
         XCTAssertNotNil(result.value)
         XCTAssertNil(result.error)
     }
+
+    func testThatDecodableResponseSerializerSucceedsWhenDataIsNilWithEmptyResponseConformingTypeAndEmptyResponseStatusCode() {
+        // Given
+        let serializer = DecodableResponseSerializer<Bool>()
+        let response = HTTPURLResponse(statusCode: 204)
+
+        // When
+        let result = Result { try serializer.serialize(request: nil, response: response, data: nil, error: nil) }
+
+        // Then
+        XCTAssertTrue(result.isSuccess)
+        XCTAssertNotNil(result.value)
+        XCTAssertNil(result.error)
+    }
+
+    func testThatDecodableResponseSerializerFailsWhenDataIsNilWithEmptyResponseNonconformingTypeAndEmptyResponseStatusCode() {
+        // Given
+        let serializer = DecodableResponseSerializer<Int>()
+        let response = HTTPURLResponse(statusCode: 204)
+
+        // When
+        let result = Result { try serializer.serialize(request: nil, response: response, data: nil, error: nil) }
+
+        // Then
+        XCTAssertTrue(result.isFailure)
+        XCTAssertNil(result.value)
+        XCTAssertNotNil(result.error)
+
+        if let error = result.error?.asAFError {
+            XCTAssertTrue(error.isInvalidEmptyResponse)
+        } else {
+            XCTFail("error should not be nil")
+        }
+    }
 }
 
 // MARK: -