浏览代码

Merge pull request #792 from Alamofire/feature/response_generic_type

Feature - Response Generic Type
Christian Noon 10 年之前
父节点
当前提交
00a0130133

+ 8 - 0
Alamofire.xcodeproj/project.pbxproj

@@ -28,6 +28,9 @@
 		4C341BBA1B1A865A00C1B34D /* CacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C341BB91B1A865A00C1B34D /* CacheTests.swift */; };
 		4C341BBB1B1A865A00C1B34D /* CacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C341BB91B1A865A00C1B34D /* CacheTests.swift */; };
 		4C3C53B21BAC5D40004F7F68 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8111E3319A95C8B0040E7D1 /* Alamofire.framework */; settings = {ASSET_TAGS = (); }; };
+		4C3C53DF1BADEF79004F7F68 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3C53DE1BADEF79004F7F68 /* Response.swift */; settings = {ASSET_TAGS = (); }; };
+		4C3C53E01BADEF79004F7F68 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3C53DE1BADEF79004F7F68 /* Response.swift */; settings = {ASSET_TAGS = (); }; };
+		4C3C53E11BADEF79004F7F68 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3C53DE1BADEF79004F7F68 /* Response.swift */; settings = {ASSET_TAGS = (); }; };
 		4C7C8D221B9D0D9000948136 /* NSURLSessionConfiguration+AlamofireTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C7C8D211B9D0D9000948136 /* NSURLSessionConfiguration+AlamofireTests.swift */; settings = {ASSET_TAGS = (); }; };
 		4C7C8D231B9D0D9000948136 /* NSURLSessionConfiguration+AlamofireTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C7C8D211B9D0D9000948136 /* NSURLSessionConfiguration+AlamofireTests.swift */; settings = {ASSET_TAGS = (); }; };
 		4C811F8D1B51856D00E0F59A /* ServerTrustPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C811F8C1B51856D00E0F59A /* ServerTrustPolicy.swift */; };
@@ -149,6 +152,7 @@
 		4C33A1241B5207DB00873DFF /* unicorn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = unicorn.png; sourceTree = "<group>"; };
 		4C33A1421B52089C00873DFF /* ServerTrustPolicyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerTrustPolicyTests.swift; sourceTree = "<group>"; };
 		4C341BB91B1A865A00C1B34D /* CacheTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CacheTests.swift; sourceTree = "<group>"; };
+		4C3C53DE1BADEF79004F7F68 /* Response.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Response.swift; sourceTree = "<group>"; };
 		4C7C8D211B9D0D9000948136 /* NSURLSessionConfiguration+AlamofireTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSURLSessionConfiguration+AlamofireTests.swift"; sourceTree = "<group>"; };
 		4C811F8C1B51856D00E0F59A /* ServerTrustPolicy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerTrustPolicy.swift; sourceTree = "<group>"; };
 		4C812C3A1B535F220017E0BF /* alamofire-root-ca.cer */ = {isa = PBXFileReference; lastKnownFileType = file; name = "alamofire-root-ca.cer"; path = "alamofire.org/alamofire-root-ca.cer"; sourceTree = "<group>"; };
@@ -379,6 +383,7 @@
 				4CDE2C361AF8932A00BABAE5 /* Manager.swift */,
 				4CE2724E1AF88FB500F1D59A /* ParameterEncoding.swift */,
 				4CDE2C391AF899EC00BABAE5 /* Request.swift */,
+				4C3C53DE1BADEF79004F7F68 /* Response.swift */,
 				4C0E5BF71B673D3400816CCC /* Result.swift */,
 			);
 			name = Core;
@@ -708,6 +713,7 @@
 				4CDE2C471AF89FF300BABAE5 /* ResponseSerialization.swift in Sources */,
 				4C1DC8551B68908E00476DE3 /* Error.swift in Sources */,
 				4CDE2C381AF8932A00BABAE5 /* Manager.swift in Sources */,
+				4C3C53E01BADEF79004F7F68 /* Response.swift in Sources */,
 				4DD67C251A5C590000ED2280 /* Alamofire.swift in Sources */,
 				4C23EB441B327C5B0090E0BC /* MultipartFormData.swift in Sources */,
 				4C811F8E1B51856D00E0F59A /* ServerTrustPolicy.swift in Sources */,
@@ -728,6 +734,7 @@
 				4CEC605A1B745C9100E684F4 /* Error.swift in Sources */,
 				E4202FD21B667AA100C997FB /* ResponseSerialization.swift in Sources */,
 				E4202FD31B667AA100C997FB /* Manager.swift in Sources */,
+				4C3C53E11BADEF79004F7F68 /* Response.swift in Sources */,
 				4CEC605B1B745C9100E684F4 /* Result.swift in Sources */,
 				E4202FD41B667AA100C997FB /* Alamofire.swift in Sources */,
 				E4202FD51B667AA100C997FB /* MultipartFormData.swift in Sources */,
@@ -748,6 +755,7 @@
 				4CDE2C461AF89FF300BABAE5 /* ResponseSerialization.swift in Sources */,
 				4C1DC8541B68908E00476DE3 /* Error.swift in Sources */,
 				4CDE2C371AF8932A00BABAE5 /* Manager.swift in Sources */,
+				4C3C53DF1BADEF79004F7F68 /* Response.swift in Sources */,
 				F897FF4119AA800700AB5182 /* Alamofire.swift in Sources */,
 				4C23EB431B327C5B0090E0BC /* MultipartFormData.swift in Sources */,
 				4C811F8D1B51856D00E0F59A /* ServerTrustPolicy.swift in Sources */,

+ 3 - 3
Example/DetailViewController.swift

@@ -75,11 +75,11 @@ class DetailViewController: UITableViewController {
         refreshControl?.beginRefreshing()
 
         let start = CACurrentMediaTime()
-        request.responseString { request, response, _, result in
+        request.responseString { response in
             let end = CACurrentMediaTime()
             self.elapsedTime = end - start
 
-            if let response = response {
+            if let response = response.response {
                 for (field, value) in response.allHeaderFields {
                     self.headers["\(field)"] = "\(value)"
                 }
@@ -88,7 +88,7 @@ class DetailViewController: UITableViewController {
             if let segueIdentifier = self.segueIdentifier {
                 switch segueIdentifier {
                 case "GET", "POST", "PUT", "DELETE":
-                    self.body = result.value
+                    self.body = response.result.value
                 case "DOWNLOAD":
                     self.body = self.downloadedBodyString()
                 default:

+ 83 - 0
Source/Response.swift

@@ -0,0 +1,83 @@
+// Result.swift
+//
+// Copyright (c) 2014–2015 Alamofire Software Foundation (http://alamofire.org/)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+import Foundation
+
+/// Used to store all response data returned from a completed `Request`.
+public struct Response<Value, Error: ErrorType> {
+    /// The URL request sent to the server.
+    public let request: NSURLRequest?
+
+    /// The server's response to the URL request.
+    public let response: NSHTTPURLResponse?
+
+    /// The data returned by the server.
+    public let data: NSData?
+
+    /// The result of response serialization.
+    public let result: Result<Value, Error>
+
+    /**
+        Initializes the `Response` instance with the specified URL request, URL response, server data and response
+        serialization result.
+    
+        - parameter request:  The URL request sent to the server.
+        - parameter response: The server's response to the URL request.
+        - parameter data:     The data returned by the server.
+        - parameter result:   The result of response serialization.
+    
+        - returns: the new `Response` instance.
+    */
+    public init(request: NSURLRequest?, response: NSHTTPURLResponse?, data: NSData?, result: Result<Value, Error>) {
+        self.request = request
+        self.response = response
+        self.data = data
+        self.result = result
+    }
+}
+
+// MARK: - CustomStringConvertible
+
+extension Response: CustomStringConvertible {
+    /// The textual representation used when written to an output stream, which includes whether the result was a
+    /// success or failure.
+    public var description: String {
+        return result.debugDescription
+    }
+}
+
+// MARK: - CustomDebugStringConvertible
+
+extension Response: CustomDebugStringConvertible {
+    /// The debug textual representation used when written to an output stream, which includes the URL request, the URL
+    /// response, the server data and the response serialization result.
+    public var debugDescription: String {
+        var output: [String] = []
+
+        output.append(request != nil ? "[Request]: \(request!)" : "[Request]: nil")
+        output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil")
+        output.append("[Data]: \(data?.length ?? 0) bytes")
+        output.append("[Result]: \(result.debugDescription)")
+
+        return output.joinWithSeparator("\n")
+    }
+}

+ 36 - 38
Source/ResponseSerialization.swift

@@ -27,44 +27,44 @@ import Foundation
 /**
     The type in which all response serializers must conform to in order to serialize a response.
 */
-public protocol ResponseSerializer {
-    /// The type of serialized object to be created by this `ResponseSerializer`.
+public protocol ResponseSerializerType {
+    /// The type of serialized object to be created by this `ResponseSerializerType`.
     typealias SerializedObject
 
     /// The type of error to be created by this `ResponseSerializer` if serialization fails.
-    typealias Error: ErrorType
+    typealias ErrorObject: ErrorType
 
     /**
         A closure used by response handlers that takes a request, response, data and error and returns a result.
     */
-    var serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Result<SerializedObject, Error> { get }
+    var serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Result<SerializedObject, ErrorObject> { get }
 }
 
 // MARK: -
 
 /**
-    A generic `ResponseSerializer` used to serialize a request, response, and data into a serialized object.
+    A generic `ResponseSerializerType` used to serialize a request, response, and data into a serialized object.
 */
-public struct GenericResponseSerializer<T, E: ErrorType>: ResponseSerializer {
+public struct ResponseSerializer<Value, Error: ErrorType>: ResponseSerializerType {
     /// The type of serialized object to be created by this `ResponseSerializer`.
-    public typealias SerializedObject = T
+    public typealias SerializedObject = Value
 
     /// The type of error to be created by this `ResponseSerializer` if serialization fails.
-    public typealias Error = E
+    public typealias ErrorObject = Error
 
     /**
         A closure used by response handlers that takes a request, response, data and error and returns a result.
     */
-    public var serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Result<T, E>
+    public var serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Result<Value, Error>
 
     /**
-        Initializes the `GenericResponseSerializer` instance with the given serialize response closure.
+        Initializes the `ResponseSerializer` instance with the given serialize response closure.
 
         - parameter serializeResponse: The closure used to serialize the response.
 
         - returns: The new generic response serializer instance.
     */
-    public init(serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Result<T, E>) {
+    public init(serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Result<Value, Error>) {
         self.serializeResponse = serializeResponse
     }
 }
@@ -105,10 +105,10 @@ extension Request {
 
         - returns: The request.
     */
-    public func response<T: ResponseSerializer, V where T.SerializedObject == V>(
+    public func response<T: ResponseSerializerType>(
         queue queue: dispatch_queue_t? = nil,
         responseSerializer: T,
-        completionHandler: (NSURLRequest?, NSHTTPURLResponse?, NSData?, Result<V, T.Error>) -> Void)
+        completionHandler: Response<T.SerializedObject, T.ErrorObject> -> Void)
         -> Self
     {
         delegate.queue.addOperationWithBlock {
@@ -120,7 +120,14 @@ extension Request {
             )
 
             dispatch_async(queue ?? dispatch_get_main_queue()) {
-                completionHandler(self.request, self.response, self.delegate.data, result)
+                let response = Response<T.SerializedObject, T.ErrorObject>(
+                    request: self.request,
+                    response: self.response,
+                    data: self.delegate.data,
+                    result: result
+                )
+
+                completionHandler(response)
             }
         }
 
@@ -137,8 +144,8 @@ extension Request {
 
         - returns: A data response serializer.
     */
-    public static func dataResponseSerializer() -> GenericResponseSerializer<NSData, NSError> {
-        return GenericResponseSerializer { _, _, data, error in
+    public static func dataResponseSerializer() -> ResponseSerializer<NSData, NSError> {
+        return ResponseSerializer { _, _, data, error in
             guard error == nil else { return .Failure(error!) }
 
             guard let validData = data where validData.length > 0 else {
@@ -154,16 +161,11 @@ extension Request {
     /**
         Adds a handler to be called once the request has finished.
 
-        - parameter completionHandler: The code to be executed once the request has finished. The closure takes 4
-                                       arguments: the URL request, the URL response, the server data and the result
-                                       produced while extracting the data.
+        - parameter completionHandler: The code to be executed once the request has finished.
 
         - returns: The request.
     */
-    public func responseData(
-        completionHandler: (NSURLRequest?, NSHTTPURLResponse?, NSData?, Result<NSData, NSError>) -> Void)
-        -> Self
-    {
+    public func responseData(completionHandler: Response<NSData, NSError> -> Void) -> Self {
         return response(responseSerializer: Request.dataResponseSerializer(), completionHandler: completionHandler)
     }
 }
@@ -183,9 +185,9 @@ extension Request {
     */
     public static func stringResponseSerializer(
         var encoding encoding: NSStringEncoding? = nil)
-        -> GenericResponseSerializer<String, NSError>
+        -> ResponseSerializer<String, NSError>
     {
-        return GenericResponseSerializer { _, response, data, error in
+        return ResponseSerializer { _, response, data, error in
             guard error == nil else { return .Failure(error!) }
 
             guard let validData = data where validData.length > 0 else {
@@ -218,15 +220,13 @@ extension Request {
         - parameter encoding:          The string encoding. If `nil`, the string encoding will be determined from the 
                                        server response, falling back to the default HTTP default character set, 
                                        ISO-8859-1.
-        - parameter completionHandler: A closure to be executed once the request has finished. The closure takes 3
-                                       arguments: the URL request, the URL response, the server data and the result 
-                                       produced while creating the string.
+        - parameter completionHandler: A closure to be executed once the request has finished.
 
         - returns: The request.
     */
     public func responseString(
         encoding encoding: NSStringEncoding? = nil,
-        completionHandler: (NSURLRequest?, NSHTTPURLResponse?, NSData?, Result<String, NSError>) -> Void)
+        completionHandler: Response<String, NSError> -> Void)
         -> Self
     {
         return response(
@@ -250,9 +250,9 @@ extension Request {
     */
     public static func JSONResponseSerializer(
         options options: NSJSONReadingOptions = .AllowFragments)
-        -> GenericResponseSerializer<AnyObject, NSError>
+        -> ResponseSerializer<AnyObject, NSError>
     {
-        return GenericResponseSerializer { _, _, data, error in
+        return ResponseSerializer { _, _, data, error in
             guard error == nil else { return .Failure(error!) }
 
             guard let validData = data where validData.length > 0 else {
@@ -274,15 +274,13 @@ extension Request {
         Adds a handler to be called once the request has finished.
 
         - parameter options:           The JSON serialization reading options. `.AllowFragments` by default.
-        - parameter completionHandler: A closure to be executed once the request has finished. The closure takes 3
-                                       arguments: the URL request, the URL response, the server data and the result 
-                                       produced while creating the JSON object.
+        - parameter completionHandler: A closure to be executed once the request has finished.
 
         - returns: The request.
     */
     public func responseJSON(
         options options: NSJSONReadingOptions = .AllowFragments,
-        completionHandler: (NSURLRequest?, NSHTTPURLResponse?, NSData?, Result<AnyObject, NSError>) -> Void)
+        completionHandler: Response<AnyObject, NSError> -> Void)
         -> Self
     {
         return response(
@@ -306,9 +304,9 @@ extension Request {
     */
     public static func propertyListResponseSerializer(
         options options: NSPropertyListReadOptions = NSPropertyListReadOptions())
-        -> GenericResponseSerializer<AnyObject, NSError>
+        -> ResponseSerializer<AnyObject, NSError>
     {
-        return GenericResponseSerializer { _, _, data, error in
+        return ResponseSerializer { _, _, data, error in
             guard error == nil else { return .Failure(error!) }
 
             guard let validData = data where validData.length > 0 else {
@@ -338,7 +336,7 @@ extension Request {
     */
     public func responsePropertyList(
         options options: NSPropertyListReadOptions = NSPropertyListReadOptions(),
-        completionHandler: (NSURLRequest?, NSHTTPURLResponse?, NSData?, Result<AnyObject, NSError>) -> Void)
+        completionHandler: Response<AnyObject, NSError> -> Void)
         -> Self
     {
         return response(

+ 4 - 0
Source/Result.swift

@@ -73,6 +73,8 @@ public enum Result<Value, Error: ErrorType> {
 // MARK: - CustomStringConvertible
 
 extension Result: CustomStringConvertible {
+    /// The textual representation used when written to an output stream, which includes whether the result was a 
+    /// success or failure.
     public var description: String {
         switch self {
         case .Success:
@@ -86,6 +88,8 @@ extension Result: CustomStringConvertible {
 // MARK: - CustomDebugStringConvertible
 
 extension Result: CustomDebugStringConvertible {
+    /// The debug textual representation used when written to an output stream, which includes whether the result was a
+    /// success or failure in addition to the value or error.
     public var debugDescription: String {
         switch self {
         case .Success(let value):

+ 10 - 19
Tests/DownloadTests.swift

@@ -429,38 +429,29 @@ class DownloadResumeDataTestCase: BaseTestCase {
     func testThatCancelledDownloadResumeDataIsAvailableWithJSONResponseSerializer() {
         // Given
         let expectation = expectationWithDescription("Download should be cancelled")
-
-        var request: NSURLRequest?
-        var response: NSHTTPURLResponse?
-        var data: NSData?
-        var result: Result<AnyObject, NSError>?
+        var response: Response<AnyObject, NSError>?
 
         // When
         let download = Alamofire.download(.GET, URLString, destination: destination)
         download.progress { _, _, _ in
             download.cancel()
         }
-        download.responseJSON { responseRequest, responseResponse, responseData, responseResult in
-            request = responseRequest
-            response = responseResponse
-            data = responseData
-            result = responseResult
-
+        download.responseJSON { closureResponse in
+            response = closureResponse
             expectation.fulfill()
         }
 
         waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
 
         // Then
-        XCTAssertNotNil(request, "request should not be nil")
-        XCTAssertNotNil(response, "response should not be nil")
-        XCTAssertNotNil(data, "data should not be nil")
-
-        if let result = result {
-            XCTAssertTrue(result.isFailure, "result should be a failure")
-            XCTAssertTrue(result.error != nil, "error should not be nil")
+        if let response = response {
+            XCTAssertNotNil(response.request, "request should not be nil")
+            XCTAssertNotNil(response.response, "response should not be nil")
+            XCTAssertNotNil(response.data, "data should not be nil")
+            XCTAssertTrue(response.result.isFailure, "result should be failure")
+            XCTAssertNotNil(response.result.error, "result error should not be nil")
         } else {
-            XCTFail("result should not be nil")
+            XCTFail("response should not be nil")
         }
 
         XCTAssertNotNil(download.resumeData, "resume data should not be nil")

+ 10 - 18
Tests/ManagerTests.swift

@@ -139,34 +139,26 @@ class ManagerConfigurationHeadersTestCase: BaseTestCase {
 
         let expectation = expectationWithDescription("request should complete successfully")
 
-        var request: NSURLRequest?
-        var response: NSHTTPURLResponse?
-        var data: NSData?
-        var result: Result<AnyObject, NSError>?
+        var response: Response<AnyObject, NSError>?
 
         // When
         manager.request(.GET, "https://httpbin.org/headers")
-            .responseJSON { responseRequest, responseResponse, responseData, responseResult in
-                request = responseRequest
-                response = responseResponse
-                data = responseData
-                result = responseResult
-
+            .responseJSON { closureResponse in
+                response = closureResponse
                 expectation.fulfill()
             }
 
         waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
 
         // Then
-        XCTAssertNotNil(request, "request should not be nil")
-        XCTAssertNotNil(response, "response should not be nil")
-        XCTAssertNotNil(data, "data should not be nil")
-
-        if let result = result {
-            XCTAssertTrue(result.isSuccess, "result should be a success")
+        if let response = response {
+            XCTAssertNotNil(response.request, "request should not be nil")
+            XCTAssertNotNil(response.response, "response should not be nil")
+            XCTAssertNotNil(response.data, "data should not be nil")
+            XCTAssertTrue(response.result.isSuccess, "result should be a success")
 
             if let
-                headers = result.value?["headers" as NSString] as? [String: String],
+                headers = response.result.value?["headers" as NSString] as? [String: String],
                 authorization = headers["Authorization"]
             {
                 XCTAssertEqual(authorization, "Bearer 123456", "authorization header value does not match")
@@ -174,7 +166,7 @@ class ManagerConfigurationHeadersTestCase: BaseTestCase {
                 XCTFail("failed to extract authorization header value")
             }
         } else {
-            XCTFail("result should not be nil")
+            XCTFail("response should not be nil")
         }
     }
 }

+ 41 - 48
Tests/RequestTests.swift

@@ -290,40 +290,36 @@ class RequestResponseTestCase: BaseTestCase {
 
         let expectation = expectationWithDescription("request should succeed")
 
-        var request: NSURLRequest?
-        var response: NSHTTPURLResponse?
-        var data: NSData?
-        var result: Result<AnyObject, NSError>?
+        var response: Response<AnyObject, NSError>?
 
         // When
         Alamofire.request(.POST, URLString, parameters: parameters)
-            .responseJSON { responseRequest, responseResponse, responseData, responseResult in
-                request = responseRequest
-                response = responseResponse
-                data = responseData
-                result = responseResult
-
+            .responseJSON { closureResponse in
+                response = closureResponse
                 expectation.fulfill()
             }
 
         waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
 
         // Then
-        XCTAssertNotNil(request, "request should not be nil")
-        XCTAssertNotNil(response, "response should not be nil")
-        XCTAssertNotNil(data, "data should not be nil")
-        XCTAssertNotNil(result, "result should be nil")
-
-        if let
-            JSON = result?.value as? [String: AnyObject],
-            form = JSON["form"] as? [String: String]
-        {
-            XCTAssertEqual(form["french"], parameters["french"], "french parameter value should match form value")
-            XCTAssertEqual(form["japanese"], parameters["japanese"], "japanese parameter value should match form value")
-            XCTAssertEqual(form["arabic"], parameters["arabic"], "arabic parameter value should match form value")
-            XCTAssertEqual(form["emoji"], parameters["emoji"], "emoji parameter value should match form value")
+        if let response = response {
+            XCTAssertNotNil(response.request, "request should not be nil")
+            XCTAssertNotNil(response.response, "response should not be nil")
+            XCTAssertNotNil(response.data, "data should not be nil")
+
+            if let
+                JSON = response.result.value as? [String: AnyObject],
+                form = JSON["form"] as? [String: String]
+            {
+                XCTAssertEqual(form["french"], parameters["french"], "french parameter value should match form value")
+                XCTAssertEqual(form["japanese"], parameters["japanese"], "japanese parameter value should match form value")
+                XCTAssertEqual(form["arabic"], parameters["arabic"], "arabic parameter value should match form value")
+                XCTAssertEqual(form["emoji"], parameters["emoji"], "emoji parameter value should match form value")
+            } else {
+                XCTFail("form parameter in JSON should not be nil")
+            }
         } else {
-            XCTFail("form parameter in JSON should not be nil")
+            XCTFail("response should not be nil")
         }
     }
 
@@ -353,39 +349,36 @@ class RequestResponseTestCase: BaseTestCase {
 
         let expectation = expectationWithDescription("request should succeed")
 
-        var request: NSURLRequest?
-        var response: NSHTTPURLResponse?
-        var data: NSData?
-        var result: Result<AnyObject, NSError>?
+        var response: Response<AnyObject, NSError>?
 
         // When
         Alamofire.request(.POST, URLString, parameters: parameters)
-            .responseJSON { responseRequest, responseResponse, responseData, responseResult in
-                request = responseRequest
-                response = responseResponse
-                data = responseData
-                result = responseResult
-
+            .responseJSON { closureResponse in
+                response = closureResponse
                 expectation.fulfill()
-        }
+            }
 
         waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
 
         // Then
-        XCTAssertNotNil(request, "request should not be nil")
-        XCTAssertNotNil(response, "response should not be nil")
-        XCTAssertNotNil(data, "data should not be nil")
-        XCTAssertNotNil(result, "result should be nil")
-
-        if let
-            JSON = result?.value as? [String: AnyObject],
-            form = JSON["form"] as? [String: String]
-        {
-            XCTAssertEqual(form["email"], parameters["email"], "email parameter value should match form value")
-            XCTAssertEqual(form["png_image"], parameters["png_image"], "png_image parameter value should match form value")
-            XCTAssertEqual(form["jpeg_image"], parameters["jpeg_image"], "jpeg_image parameter value should match form value")
+        if let response = response {
+            XCTAssertNotNil(response.request, "request should not be nil")
+            XCTAssertNotNil(response.response, "response should not be nil")
+            XCTAssertNotNil(response.data, "data should not be nil")
+            XCTAssertTrue(response.result.isSuccess, "result should be success")
+
+            if let
+                JSON = response.result.value as? [String: AnyObject],
+                form = JSON["form"] as? [String: String]
+            {
+                XCTAssertEqual(form["email"], parameters["email"], "email parameter value should match form value")
+                XCTAssertEqual(form["png_image"], parameters["png_image"], "png_image parameter value should match form value")
+                XCTAssertEqual(form["jpeg_image"], parameters["jpeg_image"], "jpeg_image parameter value should match form value")
+            } else {
+                XCTFail("form parameter in JSON should not be nil")
+            }
         } else {
-            XCTFail("form parameter in JSON should not be nil")
+            XCTFail("response should not be nil")
         }
     }
 }

+ 100 - 124
Tests/ResponseTests.swift

@@ -30,29 +30,26 @@ class ResponseDataTestCase: BaseTestCase {
         let URLString = "https://httpbin.org/get"
         let expectation = expectationWithDescription("request should succeed")
 
-        var request: NSURLRequest?
-        var response: NSHTTPURLResponse?
-        var data: NSData?
-        var result: Result<NSData, NSError>?
+        var response: Response<NSData, NSError>?
 
         // When
         Alamofire.request(.GET, URLString, parameters: ["foo": "bar"])
-            .responseData { responseRequest, responseResponse, responseData, responseResult in
-                request = responseRequest
-                response = responseResponse
-                data = responseData
-                result = responseResult
-
+            .responseData { closureResponse in
+                response = closureResponse
                 expectation.fulfill()
             }
 
         waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
 
         // Then
-        XCTAssertNotNil(request, "request should not be nil")
-        XCTAssertNotNil(response, "response should not be nil")
-        XCTAssertNotNil(data, "data should not be nil")
-        XCTAssertTrue(result?.isSuccess ?? false, "result should be success")
+        if let response = response {
+            XCTAssertNotNil(response.request, "request should not be nil")
+            XCTAssertNotNil(response.response, "response should not be nil")
+            XCTAssertNotNil(response.data, "data should not be nil")
+            XCTAssertTrue(response.result.isSuccess, "result should be success")
+        } else {
+            XCTFail("response should not be nil")
+        }
     }
 
     func testThatResponseDataReturnsFailureResultWithOptionalDataAndError() {
@@ -60,29 +57,26 @@ class ResponseDataTestCase: BaseTestCase {
         let URLString = "https://invalid-url-here.org/this/does/not/exist"
         let expectation = expectationWithDescription("request should fail with 404")
 
-        var request: NSURLRequest?
-        var response: NSHTTPURLResponse?
-        var data: NSData?
-        var result: Result<NSData, NSError>?
+        var response: Response<NSData, NSError>?
 
         // When
         Alamofire.request(.GET, URLString, parameters: ["foo": "bar"])
-            .responseData { responseRequest, responseResponse, responseData, responseResult in
-                request = responseRequest
-                response = responseResponse
-                data = responseData
-                result = responseResult
-
+            .responseData { closureResponse in
+                response = closureResponse
                 expectation.fulfill()
             }
 
         waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
 
         // Then
-        XCTAssertNotNil(request, "request should not be nil")
-        XCTAssertNil(response, "response should be nil")
-        XCTAssertNotNil(data, "data should not be nil")
-        XCTAssertTrue(result?.isFailure ?? false, "result should be failure")
+        if let response = response {
+            XCTAssertNotNil(response.request, "request should not be nil")
+            XCTAssertNil(response.response, "response should be nil")
+            XCTAssertNotNil(response.data, "data should not be nil")
+            XCTAssertTrue(response.result.isFailure, "result should be failure")
+        } else {
+            XCTFail("response should not be nil")
+        }
     }
 }
 
@@ -94,29 +88,26 @@ class ResponseStringTestCase: BaseTestCase {
         let URLString = "https://httpbin.org/get"
         let expectation = expectationWithDescription("request should succeed")
 
-        var request: NSURLRequest?
-        var response: NSHTTPURLResponse?
-        var data: NSData?
-        var result: Result<String, NSError>?
+        var response: Response<String, NSError>?
 
         // When
         Alamofire.request(.GET, URLString, parameters: ["foo": "bar"])
-            .responseString { responseRequest, responseResponse, responseData, responseResult in
-                request = responseRequest
-                response = responseResponse
-                data = responseData
-                result = responseResult
-
+            .responseString { closureResponse in
+                response = closureResponse
                 expectation.fulfill()
             }
 
         waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
 
         // Then
-        XCTAssertNotNil(request, "request should not be nil")
-        XCTAssertNotNil(response, "response should not be nil")
-        XCTAssertNotNil(data, "data should not be nil")
-        XCTAssertTrue(result?.isSuccess ?? false, "result should be success")
+        if let response = response {
+            XCTAssertNotNil(response.request, "request should not be nil")
+            XCTAssertNotNil(response.response, "response should not be nil")
+            XCTAssertNotNil(response.data, "data should not be nil")
+            XCTAssertTrue(response.result.isSuccess, "result should be success")
+        } else {
+            XCTFail("response should not be nil")
+        }
     }
 
     func testThatResponseStringReturnsFailureResultWithOptionalDataAndError() {
@@ -124,29 +115,26 @@ class ResponseStringTestCase: BaseTestCase {
         let URLString = "https://invalid-url-here.org/this/does/not/exist"
         let expectation = expectationWithDescription("request should fail with 404")
 
-        var request: NSURLRequest?
-        var response: NSHTTPURLResponse?
-        var data: NSData?
-        var result: Result<String, NSError>?
+        var response: Response<String, NSError>?
 
         // When
         Alamofire.request(.GET, URLString, parameters: ["foo": "bar"])
-            .responseString { responseRequest, responseResponse, responseData, responseResult in
-                request = responseRequest
-                response = responseResponse
-                data = responseData
-                result = responseResult
-
+            .responseString { closureResponse in
+                response = closureResponse
                 expectation.fulfill()
             }
 
         waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
 
         // Then
-        XCTAssertNotNil(request, "request should not be nil")
-        XCTAssertNil(response, "response should be nil")
-        XCTAssertNotNil(data, "data should not be nil")
-        XCTAssertTrue(result?.isFailure ?? false, "result should be failure")
+        if let response = response {
+            XCTAssertNotNil(response.request, "request should not be nil")
+            XCTAssertNil(response.response, "response should be nil")
+            XCTAssertNotNil(response.data, "data should not be nil")
+            XCTAssertTrue(response.result.isFailure, "result should be failure")
+        } else {
+            XCTFail("response should not be nil")
+        }
     }
 }
 
@@ -158,29 +146,26 @@ class ResponseJSONTestCase: BaseTestCase {
         let URLString = "https://httpbin.org/get"
         let expectation = expectationWithDescription("request should succeed")
 
-        var request: NSURLRequest?
-        var response: NSHTTPURLResponse?
-        var data: NSData?
-        var result: Result<AnyObject, NSError>?
+        var response: Response<AnyObject, NSError>?
 
         // When
         Alamofire.request(.GET, URLString, parameters: ["foo": "bar"])
-            .responseJSON { responseRequest, responseResponse, responseData, responseResult in
-                request = responseRequest
-                response = responseResponse
-                data = responseData
-                result = responseResult
-
+            .responseJSON { closureResponse in
+                response = closureResponse
                 expectation.fulfill()
             }
 
         waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
 
         // Then
-        XCTAssertNotNil(request, "request should not be nil")
-        XCTAssertNotNil(response, "response should not be nil")
-        XCTAssertNotNil(data, "data should not be nil")
-        XCTAssertTrue(result?.isSuccess ?? false, "result should be success")
+        if let response = response {
+            XCTAssertNotNil(response.request, "request should not be nil")
+            XCTAssertNotNil(response.response, "response should not be nil")
+            XCTAssertNotNil(response.data, "data should not be nil")
+            XCTAssertTrue(response.result.isSuccess, "result should be success")
+        } else {
+            XCTFail("response should not be nil")
+        }
     }
 
     func testThatResponseStringReturnsFailureResultWithOptionalDataAndError() {
@@ -188,29 +173,26 @@ class ResponseJSONTestCase: BaseTestCase {
         let URLString = "https://invalid-url-here.org/this/does/not/exist"
         let expectation = expectationWithDescription("request should fail with 404")
 
-        var request: NSURLRequest?
-        var response: NSHTTPURLResponse?
-        var data: NSData?
-        var result: Result<AnyObject, NSError>?
+        var response: Response<AnyObject, NSError>?
 
         // When
         Alamofire.request(.GET, URLString, parameters: ["foo": "bar"])
-            .responseJSON { responseRequest, responseResponse, responseData, responseResult in
-                request = responseRequest
-                response = responseResponse
-                data = responseData
-                result = responseResult
-
+            .responseJSON { closureResponse in
+                response = closureResponse
                 expectation.fulfill()
             }
 
         waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
 
         // Then
-        XCTAssertNotNil(request, "request should not be nil")
-        XCTAssertNil(response, "response should be nil")
-        XCTAssertNotNil(data, "data should not be nil")
-        XCTAssertTrue(result?.isFailure ?? false, "result should be failure")
+        if let response = response {
+            XCTAssertNotNil(response.request, "request should not be nil")
+            XCTAssertNil(response.response, "response should be nil")
+            XCTAssertNotNil(response.data, "data should not be nil")
+            XCTAssertTrue(response.result.isFailure, "result should be failure")
+        } else {
+            XCTFail("response should not be nil")
+        }
     }
 
     func testThatResponseJSONReturnsSuccessResultForGETRequest() {
@@ -218,36 +200,33 @@ class ResponseJSONTestCase: BaseTestCase {
         let URLString = "https://httpbin.org/get"
         let expectation = expectationWithDescription("request should succeed")
 
-        var request: NSURLRequest?
-        var response: NSHTTPURLResponse?
-        var data: NSData?
-        var result: Result<AnyObject, NSError>?
+        var response: Response<AnyObject, NSError>?
 
         // When
         Alamofire.request(.GET, URLString, parameters: ["foo": "bar"])
-            .responseJSON { responseRequest, responseResponse, responseData, responseResult in
-                request = responseRequest
-                response = responseResponse
-                data = responseData
-                result = responseResult
-
+            .responseJSON { closureResponse in
+                response = closureResponse
                 expectation.fulfill()
             }
 
         waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
 
         // Then
-        XCTAssertNotNil(request, "request should not be nil")
-        XCTAssertNotNil(response, "response should not be nil")
-        XCTAssertNotNil(data, "data should not be nil")
-        XCTAssertTrue(result?.isSuccess ?? false, "result should be success")
-
-        // The `as NSString` cast is necessary due to a compiler bug. See the following rdar for more info.
-        // - https://openradar.appspot.com/radar?id=5517037090635776
-        if let args = result?.value?["args" as NSString] as? [String: String] {
-            XCTAssertEqual(args, ["foo": "bar"], "args should match parameters")
+        if let response = response {
+            XCTAssertNotNil(response.request, "request should not be nil")
+            XCTAssertNotNil(response.response, "response should not be nil")
+            XCTAssertNotNil(response.data, "data should not be nil")
+            XCTAssertTrue(response.result.isSuccess, "result should be success")
+
+            // The `as NSString` cast is necessary due to a compiler bug. See the following rdar for more info.
+            // - https://openradar.appspot.com/radar?id=5517037090635776
+            if let args = response.result.value?["args" as NSString] as? [String: String] {
+                XCTAssertEqual(args, ["foo": "bar"], "args should match parameters")
+            } else {
+                XCTFail("args should not be nil")
+            }
         } else {
-            XCTFail("args should not be nil")
+            XCTFail("response should not be nil")
         }
     }
 
@@ -256,36 +235,33 @@ class ResponseJSONTestCase: BaseTestCase {
         let URLString = "https://httpbin.org/post"
         let expectation = expectationWithDescription("request should succeed")
 
-        var request: NSURLRequest?
-        var response: NSHTTPURLResponse?
-        var data: NSData?
-        var result: Result<AnyObject, NSError>?
+        var response: Response<AnyObject, NSError>?
 
         // When
         Alamofire.request(.POST, URLString, parameters: ["foo": "bar"])
-            .responseJSON { responseRequest, responseResponse, responseData, responseResult in
-                request = responseRequest
-                response = responseResponse
-                data = responseData
-                result = responseResult
-
+            .responseJSON { closureResponse in
+                response = closureResponse
                 expectation.fulfill()
             }
 
         waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
 
         // Then
-        XCTAssertNotNil(request, "request should not be nil")
-        XCTAssertNotNil(response, "response should not be nil")
-        XCTAssertNotNil(data, "data should not be nil")
-        XCTAssertTrue(result?.isSuccess ?? false, "result should be success")
-
-        // The `as NSString` cast is necessary due to a compiler bug. See the following rdar for more info.
-        // - https://openradar.appspot.com/radar?id=5517037090635776
-        if let form = result?.value?["form" as NSString] as? [String: String] {
-            XCTAssertEqual(form, ["foo": "bar"], "form should match parameters")
+        if let response = response {
+            XCTAssertNotNil(response.request, "request should not be nil")
+            XCTAssertNotNil(response.response, "response should not be nil")
+            XCTAssertNotNil(response.data, "data should not be nil")
+            XCTAssertTrue(response.result.isSuccess, "result should be success")
+
+            // The `as NSString` cast is necessary due to a compiler bug. See the following rdar for more info.
+            // - https://openradar.appspot.com/radar?id=5517037090635776
+            if let form = response.result.value?["form" as NSString] as? [String: String] {
+                XCTAssertEqual(form, ["foo": "bar"], "form should match parameters")
+            } else {
+                XCTFail("form should not be nil")
+            }
         } else {
-            XCTFail("form should not be nil")
+            XCTFail("response should not be nil")
         }
     }
 }