Browse Source

Refactored response serialization to use generics and a ResponseSerializer protocol.

Christian Noon 10 years ago
parent
commit
663fbfde18
2 changed files with 140 additions and 71 deletions
  1. 0 50
      Source/Request.swift
  2. 140 21
      Source/ResponseSerialization.swift

+ 0 - 50
Source/Request.swift

@@ -134,56 +134,6 @@ public class Request {
         return self
     }
 
-    // MARK: - Response
-
-    /**
-        A closure used by response handlers that takes a request, response, and data and returns a serialized object and any error that occured in the process.
-    */
-    public typealias Serializer = (NSURLRequest, NSHTTPURLResponse?, NSData?) -> (AnyObject?, NSError?)
-
-    /**
-        Creates a response serializer that returns the associated data as-is.
-
-        :returns: A data response serializer.
-    */
-    public class func responseDataSerializer() -> Serializer {
-        return { request, response, data in
-            return (data, nil)
-        }
-    }
-
-    /**
-        Adds a handler to be called once the request has finished.
-
-        :param: completionHandler The code to be executed once the request has finished.
-
-        :returns: The request.
-    */
-    public func response(completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {
-        return response(serializer: Request.responseDataSerializer(), completionHandler: completionHandler)
-    }
-
-    /**
-        Adds a handler to be called once the request has finished.
-
-        :param: queue The queue on which the completion handler is dispatched.
-        :param: serializer The closure responsible for serializing the request, response, and data.
-        :param: completionHandler The code to be executed once the request has finished.
-
-        :returns: The request.
-    */
-    public func response(queue: dispatch_queue_t? = nil, serializer: Serializer, completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {
-        self.delegate.queue.addOperationWithBlock {
-            let (responseObject: AnyObject?, serializationError: NSError?) = serializer(self.request, self.response, self.delegate.data)
-
-            dispatch_async(queue ?? dispatch_get_main_queue()) {
-                completionHandler(self.request, self.response, responseObject, self.delegate.error ?? serializationError)
-            }
-        }
-
-        return self
-    }
-
     // MARK: - State
 
     /**

+ 140 - 21
Source/ResponseSerialization.swift

@@ -22,9 +22,100 @@
 
 import Foundation
 
-// MARK: String
+// MARK: - ResponseSerializer
+
+/**
+    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`.
+    typealias SerializedObject
+
+    /// A closure used by response handlers that takes a request, response, and data and returns a serialized object and any error that occured in the process.
+    var serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?) -> (SerializedObject?, NSError?) { get }
+}
+
+/**
+    A generic `ResponseSerializer` used to serialize a request, response, and data into a serialized object.
+*/
+public struct GenericResponseSerializer<T>: ResponseSerializer {
+    /// The type of serialized object to be created by this `ResponseSerializer`.
+    public typealias SerializedObject = T
+
+    /// A closure used by response handlers that takes a request, response, and data and returns a serialized object and any error that occured in the process.
+    public var serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?) -> (SerializedObject?, NSError?)
+}
+
+// MARK: - Default
+
+extension Request {
+
+    /**
+        Adds a handler to be called once the request has finished.
+
+        :param: queue The queue on which the completion handler is dispatched.
+        :param: responseSerializer The response serializer responsible for serializing the request, response, and data.
+        :param: completionHandler The code to be executed once the request has finished.
+
+        :returns: The request.
+    */
+    public func response<T: ResponseSerializer, V where T.SerializedObject == V>(
+        queue: dispatch_queue_t? = nil,
+        responseSerializer: T,
+        completionHandler: (NSURLRequest, NSHTTPURLResponse?, V?, NSError?) -> Void)
+        -> Self
+    {
+        self.delegate.queue.addOperationWithBlock {
+            let result: V?
+            let error: NSError?
+
+            (result, error) = responseSerializer.serializeResponse(self.request, self.response, self.delegate.data)
+
+            dispatch_async(queue ?? dispatch_get_main_queue()) {
+                completionHandler(self.request, self.response, result, self.delegate.error ?? error)
+            }
+        }
+
+        return self
+    }
+}
+
+// MARK: - Data
+
+extension Request {
+
+    /**
+        Creates a response serializer that returns the associated data as-is.
+
+        :returns: A data response serializer.
+    */
+    public static func dataResponseSerializer() -> GenericResponseSerializer<NSData> {
+        return GenericResponseSerializer { request, response, data in
+            return (data, nil)
+        }
+    }
+
+    /**
+        Adds a handler to be called once the request has finished.
+
+        :param: completionHandler The code to be executed once the request has finished.
+
+        :returns: The request.
+    */
+    public func response(completionHandler: (NSURLRequest, NSHTTPURLResponse?, NSData?, NSError?) -> Void) -> Self {
+        return response(
+            responseSerializer: Request.dataResponseSerializer(),
+            completionHandler: { (request, response, data: NSData?, error) in
+                completionHandler(request, response, data, error)
+            }
+        )
+    }
+}
+
+// MARK: - String
 
 extension Request {
+
     /**
         Creates a response serializer that returns a string initialized from the response data with the specified string encoding.
 
@@ -32,8 +123,8 @@ extension Request {
 
         :returns: A string response serializer.
     */
-    public class func stringResponseSerializer(var encoding: NSStringEncoding? = nil) -> Serializer {
-        return { _, response, data in
+    public static func stringResponseSerializer(var encoding: NSStringEncoding? = nil) -> GenericResponseSerializer<String> {
+        return GenericResponseSerializer { _, response, data in
             if data == nil || data?.length == 0 {
                 return (nil, nil)
             }
@@ -42,7 +133,7 @@ extension Request {
                 encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding(encodingName))
             }
 
-            let string = NSString(data: data!, encoding: encoding ?? NSISOLatin1StringEncoding)
+            let string = NSString(data: data!, encoding: encoding ?? NSISOLatin1StringEncoding) as? String
 
             return (string, nil)
         }
@@ -56,16 +147,24 @@ extension Request {
 
         :returns: The request.
     */
-    public func responseString(encoding: NSStringEncoding? = nil, completionHandler: (NSURLRequest, NSHTTPURLResponse?, String?, NSError?) -> Void) -> Self  {
-        return response(serializer: Request.stringResponseSerializer(encoding: encoding)) { request, response, string, error in
-            completionHandler(request, response, string as? String, error)
-        }
+    public func responseString(
+        encoding: NSStringEncoding? = nil,
+        completionHandler: (NSURLRequest, NSHTTPURLResponse?, String?, NSError?) -> Void)
+        -> Self
+    {
+        return response(
+            responseSerializer: Request.stringResponseSerializer(encoding: encoding),
+            completionHandler: { (request, response, string: String?, error) in
+                completionHandler(request, response, string, error)
+            }
+        )
     }
 }
 
 // MARK: - JSON
 
 extension Request {
+
     /**
         Creates a response serializer that returns a JSON object constructed from the response data using `NSJSONSerialization` with the specified reading options.
 
@@ -73,8 +172,8 @@ extension Request {
 
         :returns: A JSON object response serializer.
     */
-    public class func JSONResponseSerializer(options: NSJSONReadingOptions = .AllowFragments) -> Serializer {
-        return { request, response, data in
+    public static func JSONResponseSerializer(options: NSJSONReadingOptions = .AllowFragments) -> GenericResponseSerializer<AnyObject> {
+        return GenericResponseSerializer { request, response, data in
             if data == nil || data?.length == 0 {
                 return (nil, nil)
             }
@@ -94,16 +193,24 @@ extension Request {
 
         :returns: The request.
     */
-    public func responseJSON(options: NSJSONReadingOptions = .AllowFragments, completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {
-        return response(serializer: Request.JSONResponseSerializer(options: options)) { request, response, JSON, error in
-            completionHandler(request, response, JSON, error)
-        }
+    public func responseJSON(
+        options: NSJSONReadingOptions = .AllowFragments,
+        completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void)
+        -> Self
+    {
+        return response(
+            responseSerializer: Request.JSONResponseSerializer(options: options),
+            completionHandler: { (request, response, JSON: AnyObject?, error) in
+                completionHandler(request, response, JSON, error)
+            }
+        )
     }
 }
 
 // MARK: - Property List
 
 extension Request {
+
     /**
         Creates a response serializer that returns an object constructed from the response data using `NSPropertyListSerialization` with the specified reading options.
 
@@ -111,14 +218,19 @@ extension Request {
 
         :returns: A property list object response serializer.
     */
-    public class func propertyListResponseSerializer(options: NSPropertyListReadOptions = 0) -> Serializer {
-        return { request, response, data in
+    public static func propertyListResponseSerializer(options: NSPropertyListReadOptions = 0) -> GenericResponseSerializer<AnyObject> {
+        return GenericResponseSerializer { request, response, data in
             if data == nil || data?.length == 0 {
                 return (nil, nil)
             }
 
             var propertyListSerializationError: NSError?
-            let plist: AnyObject? = NSPropertyListSerialization.propertyListWithData(data!, options: options, format: nil, error: &propertyListSerializationError)
+            let plist: AnyObject? = NSPropertyListSerialization.propertyListWithData(
+                data!,
+                options: options,
+                format: nil,
+                error: &propertyListSerializationError
+            )
 
             return (plist, propertyListSerializationError)
         }
@@ -132,9 +244,16 @@ extension Request {
 
         :returns: The request.
     */
-    public func responsePropertyList(options: NSPropertyListReadOptions = 0, completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {
-        return response(serializer: Request.propertyListResponseSerializer(options: options)) { request, response, plist, error in
-            completionHandler(request, response, plist, error)
-        }
+    public func responsePropertyList(
+        options: NSPropertyListReadOptions = 0,
+        completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void)
+        -> Self
+    {
+        return response(
+            responseSerializer: Request.propertyListResponseSerializer(options: options),
+            completionHandler: { (request, response, plist: AnyObject?, error) in
+                completionHandler(request, response, plist, error)
+            }
+        )
     }
 }