Skip to content

Commit

Permalink
Refactored all response serialization to leverage the new Response st…
Browse files Browse the repository at this point in the history
…ruct.
  • Loading branch information
cnoon committed Sep 19, 2015
1 parent ed89a6a commit 8a6ee84
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 247 deletions.
76 changes: 38 additions & 38 deletions Source/ResponseSerialization.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,42 +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
typealias Error: ErrorType

/// The type of error to be created by this `ResponseSerializer` if serialization fails.
typealias ErrorObject: ErrorType

/**
A closure used by response handlers that takes a request, response, and data and returns a result.
*/
var serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?) -> Result<SerializedObject, Error> { get }
var serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?) -> 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, and data and returns a result.
*/
public var serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?) -> Result<T, E>
public var serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?) -> 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?) -> Result<T, E>) {
public init(serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?) -> Result<Value, Error>) {
self.serializeResponse = serializeResponse
}
}
Expand Down Expand Up @@ -103,17 +105,24 @@ 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 {
let result = responseSerializer.serializeResponse(self.request, self.response, self.delegate.data)

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)
}
}

Expand All @@ -130,8 +139,8 @@ extension Request {

- returns: A data response serializer.
*/
public static func dataResponseSerializer() -> GenericResponseSerializer<NSData, NSError> {
return GenericResponseSerializer { _, _, data in
public static func dataResponseSerializer() -> ResponseSerializer<NSData, NSError> {
return ResponseSerializer { _, _, data in
guard let validData = data where validData.length > 0 else {
let failureReason = "Data could not be serialized. Input data was nil or zero length."
let error = Error.errorWithCode(.DataSerializationFailed, failureReason: failureReason)
Expand All @@ -145,16 +154,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)
}
}
Expand All @@ -174,9 +178,9 @@ extension Request {
*/
public static func stringResponseSerializer(
var encoding encoding: NSStringEncoding? = nil)
-> GenericResponseSerializer<String, NSError>
-> ResponseSerializer<String, NSError>
{
return GenericResponseSerializer { _, response, data in
return ResponseSerializer { _, response, data in
guard let validData = data where validData.length > 0 else {
let failureReason = "String could not be serialized. Input data was nil or zero length."
let error = Error.errorWithCode(.StringSerializationFailed, failureReason: failureReason)
Expand Down Expand Up @@ -207,15 +211,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(
Expand All @@ -239,9 +241,9 @@ extension Request {
*/
public static func JSONResponseSerializer(
options options: NSJSONReadingOptions = .AllowFragments)
-> GenericResponseSerializer<AnyObject, NSError>
-> ResponseSerializer<AnyObject, NSError>
{
return GenericResponseSerializer { _, _, data in
return ResponseSerializer { _, _, data in
guard let validData = data where validData.length > 0 else {
let failureReason = "JSON could not be serialized. Input data was nil or zero length."
let error = Error.errorWithCode(.JSONSerializationFailed, failureReason: failureReason)
Expand All @@ -261,15 +263,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(
Expand All @@ -293,9 +293,9 @@ extension Request {
*/
public static func propertyListResponseSerializer(
options options: NSPropertyListReadOptions = NSPropertyListReadOptions())
-> GenericResponseSerializer<AnyObject, NSError>
-> ResponseSerializer<AnyObject, NSError>
{
return GenericResponseSerializer { _, _, data in
return ResponseSerializer { _, _, data in
guard let validData = data where validData.length > 0 else {
let failureReason = "Property list could not be serialized. Input data was nil or zero length."
let error = Error.errorWithCode(.PropertyListSerializationFailed, failureReason: failureReason)
Expand Down Expand Up @@ -323,7 +323,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(
Expand Down
29 changes: 10 additions & 19 deletions Tests/DownloadTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
28 changes: 10 additions & 18 deletions Tests/ManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -139,42 +139,34 @@ 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")
} else {
XCTFail("failed to extract authorization header value")
}
} else {
XCTFail("result should not be nil")
XCTFail("response should not be nil")
}
}
}
Loading

0 comments on commit 8a6ee84

Please sign in to comment.