2 // ResponseSerialization.swift
4 // Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to deal
8 // in the Software without restriction, including without limitation the rights
9 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 // copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 /// The type in which all data response serializers must conform to in order to serialize a response.
28 public protocol DataResponseSerializerProtocol {
29 /// The type of serialized object to be created by this `DataResponseSerializerType`.
30 associatedtype SerializedObject
32 /// A closure used by response handlers that takes a request, response, data and error and returns a result.
33 var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<SerializedObject> { get }
38 /// A generic `DataResponseSerializerType` used to serialize a request, response, and data into a serialized object.
39 public struct DataResponseSerializer<Value>: DataResponseSerializerProtocol {
40 /// The type of serialized object to be created by this `DataResponseSerializer`.
41 public typealias SerializedObject = Value
43 /// A closure used by response handlers that takes a request, response, data and error and returns a result.
44 public var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<Value>
46 /// Initializes the `ResponseSerializer` instance with the given serialize response closure.
48 /// - parameter serializeResponse: The closure used to serialize the response.
50 /// - returns: The new generic response serializer instance.
51 public init(serializeResponse: @escaping (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<Value>) {
52 self.serializeResponse = serializeResponse
58 /// The type in which all download response serializers must conform to in order to serialize a response.
59 public protocol DownloadResponseSerializerProtocol {
60 /// The type of serialized object to be created by this `DownloadResponseSerializerType`.
61 associatedtype SerializedObject
63 /// A closure used by response handlers that takes a request, response, url and error and returns a result.
64 var serializeResponse: (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<SerializedObject> { get }
69 /// A generic `DownloadResponseSerializerType` used to serialize a request, response, and data into a serialized object.
70 public struct DownloadResponseSerializer<Value>: DownloadResponseSerializerProtocol {
71 /// The type of serialized object to be created by this `DownloadResponseSerializer`.
72 public typealias SerializedObject = Value
74 /// A closure used by response handlers that takes a request, response, url and error and returns a result.
75 public var serializeResponse: (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<Value>
77 /// Initializes the `ResponseSerializer` instance with the given serialize response closure.
79 /// - parameter serializeResponse: The closure used to serialize the response.
81 /// - returns: The new generic response serializer instance.
82 public init(serializeResponse: @escaping (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<Value>) {
83 self.serializeResponse = serializeResponse
90 var timeline: Timeline {
91 let requestStartTime = self.startTime ?? CFAbsoluteTimeGetCurrent()
92 let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent()
93 let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime
96 requestStartTime: requestStartTime,
97 initialResponseTime: initialResponseTime,
98 requestCompletedTime: requestCompletedTime,
99 serializationCompletedTime: CFAbsoluteTimeGetCurrent()
106 extension DataRequest {
107 /// Adds a handler to be called once the request has finished.
109 /// - parameter queue: The queue on which the completion handler is dispatched.
110 /// - parameter completionHandler: The code to be executed once the request has finished.
112 /// - returns: The request.
114 public func response(queue: DispatchQueue? = nil, completionHandler: @escaping (DefaultDataResponse) -> Void) -> Self {
115 delegate.queue.addOperation {
116 (queue ?? DispatchQueue.main).async {
117 var dataResponse = DefaultDataResponse(
118 request: self.request,
119 response: self.response,
120 data: self.delegate.data,
121 error: self.delegate.error,
122 timeline: self.timeline
125 dataResponse.add(self.delegate.metrics)
127 completionHandler(dataResponse)
134 /// Adds a handler to be called once the request has finished.
136 /// - parameter queue: The queue on which the completion handler is dispatched.
137 /// - parameter responseSerializer: The response serializer responsible for serializing the request, response,
139 /// - parameter completionHandler: The code to be executed once the request has finished.
141 /// - returns: The request.
143 public func response<T: DataResponseSerializerProtocol>(
144 queue: DispatchQueue? = nil,
145 responseSerializer: T,
146 completionHandler: @escaping (DataResponse<T.SerializedObject>) -> Void)
149 delegate.queue.addOperation {
150 let result = responseSerializer.serializeResponse(
157 var dataResponse = DataResponse<T.SerializedObject>(
158 request: self.request,
159 response: self.response,
160 data: self.delegate.data,
162 timeline: self.timeline
165 dataResponse.add(self.delegate.metrics)
167 (queue ?? DispatchQueue.main).async { completionHandler(dataResponse) }
174 extension DownloadRequest {
175 /// Adds a handler to be called once the request has finished.
177 /// - parameter queue: The queue on which the completion handler is dispatched.
178 /// - parameter completionHandler: The code to be executed once the request has finished.
180 /// - returns: The request.
182 public func response(
183 queue: DispatchQueue? = nil,
184 completionHandler: @escaping (DefaultDownloadResponse) -> Void)
187 delegate.queue.addOperation {
188 (queue ?? DispatchQueue.main).async {
189 var downloadResponse = DefaultDownloadResponse(
190 request: self.request,
191 response: self.response,
192 temporaryURL: self.downloadDelegate.temporaryURL,
193 destinationURL: self.downloadDelegate.destinationURL,
194 resumeData: self.downloadDelegate.resumeData,
195 error: self.downloadDelegate.error,
196 timeline: self.timeline
199 downloadResponse.add(self.delegate.metrics)
201 completionHandler(downloadResponse)
208 /// Adds a handler to be called once the request has finished.
210 /// - parameter queue: The queue on which the completion handler is dispatched.
211 /// - parameter responseSerializer: The response serializer responsible for serializing the request, response,
212 /// and data contained in the destination url.
213 /// - parameter completionHandler: The code to be executed once the request has finished.
215 /// - returns: The request.
217 public func response<T: DownloadResponseSerializerProtocol>(
218 queue: DispatchQueue? = nil,
219 responseSerializer: T,
220 completionHandler: @escaping (DownloadResponse<T.SerializedObject>) -> Void)
223 delegate.queue.addOperation {
224 let result = responseSerializer.serializeResponse(
227 self.downloadDelegate.fileURL,
228 self.downloadDelegate.error
231 var downloadResponse = DownloadResponse<T.SerializedObject>(
232 request: self.request,
233 response: self.response,
234 temporaryURL: self.downloadDelegate.temporaryURL,
235 destinationURL: self.downloadDelegate.destinationURL,
236 resumeData: self.downloadDelegate.resumeData,
238 timeline: self.timeline
241 downloadResponse.add(self.delegate.metrics)
243 (queue ?? DispatchQueue.main).async { completionHandler(downloadResponse) }
253 /// Returns a result data type that contains the response data as-is.
255 /// - parameter response: The response from the server.
256 /// - parameter data: The data returned from the server.
257 /// - parameter error: The error already encountered if it exists.
259 /// - returns: The result data type.
260 public static func serializeResponseData(response: HTTPURLResponse?, data: Data?, error: Error?) -> Result<Data> {
261 guard error == nil else { return .failure(error!) }
263 if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(Data()) }
265 guard let validData = data else {
266 return .failure(AFError.responseSerializationFailed(reason: .inputDataNil))
269 return .success(validData)
273 extension DataRequest {
274 /// Creates a response serializer that returns the associated data as-is.
276 /// - returns: A data response serializer.
277 public static func dataResponseSerializer() -> DataResponseSerializer<Data> {
278 return DataResponseSerializer { _, response, data, error in
279 return Request.serializeResponseData(response: response, data: data, error: error)
283 /// Adds a handler to be called once the request has finished.
285 /// - parameter completionHandler: The code to be executed once the request has finished.
287 /// - returns: The request.
289 public func responseData(
290 queue: DispatchQueue? = nil,
291 completionHandler: @escaping (DataResponse<Data>) -> Void)
296 responseSerializer: DataRequest.dataResponseSerializer(),
297 completionHandler: completionHandler
302 extension DownloadRequest {
303 /// Creates a response serializer that returns the associated data as-is.
305 /// - returns: A data response serializer.
306 public static func dataResponseSerializer() -> DownloadResponseSerializer<Data> {
307 return DownloadResponseSerializer { _, response, fileURL, error in
308 guard error == nil else { return .failure(error!) }
310 guard let fileURL = fileURL else {
311 return .failure(AFError.responseSerializationFailed(reason: .inputFileNil))
315 let data = try Data(contentsOf: fileURL)
316 return Request.serializeResponseData(response: response, data: data, error: error)
318 return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)))
323 /// Adds a handler to be called once the request has finished.
325 /// - parameter completionHandler: The code to be executed once the request has finished.
327 /// - returns: The request.
329 public func responseData(
330 queue: DispatchQueue? = nil,
331 completionHandler: @escaping (DownloadResponse<Data>) -> Void)
336 responseSerializer: DownloadRequest.dataResponseSerializer(),
337 completionHandler: completionHandler
345 /// Returns a result string type initialized from the response data with the specified string encoding.
347 /// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server
348 /// response, falling back to the default HTTP default character set, ISO-8859-1.
349 /// - parameter response: The response from the server.
350 /// - parameter data: The data returned from the server.
351 /// - parameter error: The error already encountered if it exists.
353 /// - returns: The result data type.
354 public static func serializeResponseString(
355 encoding: String.Encoding?,
356 response: HTTPURLResponse?,
361 guard error == nil else { return .failure(error!) }
363 if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success("") }
365 guard let validData = data else {
366 return .failure(AFError.responseSerializationFailed(reason: .inputDataNil))
369 var convertedEncoding = encoding
371 if let encodingName = response?.textEncodingName as CFString!, convertedEncoding == nil {
372 convertedEncoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(
373 CFStringConvertIANACharSetNameToEncoding(encodingName))
377 let actualEncoding = convertedEncoding ?? String.Encoding.isoLatin1
379 if let string = String(data: validData, encoding: actualEncoding) {
380 return .success(string)
382 return .failure(AFError.responseSerializationFailed(reason: .stringSerializationFailed(encoding: actualEncoding)))
387 extension DataRequest {
388 /// Creates a response serializer that returns a result string type initialized from the response data with
389 /// the specified string encoding.
391 /// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server
392 /// response, falling back to the default HTTP default character set, ISO-8859-1.
394 /// - returns: A string response serializer.
395 public static func stringResponseSerializer(encoding: String.Encoding? = nil) -> DataResponseSerializer<String> {
396 return DataResponseSerializer { _, response, data, error in
397 return Request.serializeResponseString(encoding: encoding, response: response, data: data, error: error)
401 /// Adds a handler to be called once the request has finished.
403 /// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the
404 /// server response, falling back to the default HTTP default character set,
406 /// - parameter completionHandler: A closure to be executed once the request has finished.
408 /// - returns: The request.
410 public func responseString(
411 queue: DispatchQueue? = nil,
412 encoding: String.Encoding? = nil,
413 completionHandler: @escaping (DataResponse<String>) -> Void)
418 responseSerializer: DataRequest.stringResponseSerializer(encoding: encoding),
419 completionHandler: completionHandler
424 extension DownloadRequest {
425 /// Creates a response serializer that returns a result string type initialized from the response data with
426 /// the specified string encoding.
428 /// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server
429 /// response, falling back to the default HTTP default character set, ISO-8859-1.
431 /// - returns: A string response serializer.
432 public static func stringResponseSerializer(encoding: String.Encoding? = nil) -> DownloadResponseSerializer<String> {
433 return DownloadResponseSerializer { _, response, fileURL, error in
434 guard error == nil else { return .failure(error!) }
436 guard let fileURL = fileURL else {
437 return .failure(AFError.responseSerializationFailed(reason: .inputFileNil))
441 let data = try Data(contentsOf: fileURL)
442 return Request.serializeResponseString(encoding: encoding, response: response, data: data, error: error)
444 return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)))
449 /// Adds a handler to be called once the request has finished.
451 /// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the
452 /// server response, falling back to the default HTTP default character set,
454 /// - parameter completionHandler: A closure to be executed once the request has finished.
456 /// - returns: The request.
458 public func responseString(
459 queue: DispatchQueue? = nil,
460 encoding: String.Encoding? = nil,
461 completionHandler: @escaping (DownloadResponse<String>) -> Void)
466 responseSerializer: DownloadRequest.stringResponseSerializer(encoding: encoding),
467 completionHandler: completionHandler
475 /// Returns a JSON object contained in a result type constructed from the response data using `JSONSerialization`
476 /// with the specified reading options.
478 /// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
479 /// - parameter response: The response from the server.
480 /// - parameter data: The data returned from the server.
481 /// - parameter error: The error already encountered if it exists.
483 /// - returns: The result data type.
484 public static func serializeResponseJSON(
485 options: JSONSerialization.ReadingOptions,
486 response: HTTPURLResponse?,
491 guard error == nil else { return .failure(error!) }
493 if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(NSNull()) }
495 guard let validData = data, validData.count > 0 else {
496 return .failure(AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength))
500 let json = try JSONSerialization.jsonObject(with: validData, options: options)
501 return .success(json)
503 return .failure(AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error)))
508 extension DataRequest {
509 /// Creates a response serializer that returns a JSON object result type constructed from the response data using
510 /// `JSONSerialization` with the specified reading options.
512 /// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
514 /// - returns: A JSON object response serializer.
515 public static func jsonResponseSerializer(
516 options: JSONSerialization.ReadingOptions = .allowFragments)
517 -> DataResponseSerializer<Any>
519 return DataResponseSerializer { _, response, data, error in
520 return Request.serializeResponseJSON(options: options, response: response, data: data, error: error)
524 /// Adds a handler to be called once the request has finished.
526 /// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
527 /// - parameter completionHandler: A closure to be executed once the request has finished.
529 /// - returns: The request.
531 public func responseJSON(
532 queue: DispatchQueue? = nil,
533 options: JSONSerialization.ReadingOptions = .allowFragments,
534 completionHandler: @escaping (DataResponse<Any>) -> Void)
539 responseSerializer: DataRequest.jsonResponseSerializer(options: options),
540 completionHandler: completionHandler
545 extension DownloadRequest {
546 /// Creates a response serializer that returns a JSON object result type constructed from the response data using
547 /// `JSONSerialization` with the specified reading options.
549 /// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
551 /// - returns: A JSON object response serializer.
552 public static func jsonResponseSerializer(
553 options: JSONSerialization.ReadingOptions = .allowFragments)
554 -> DownloadResponseSerializer<Any>
556 return DownloadResponseSerializer { _, response, fileURL, error in
557 guard error == nil else { return .failure(error!) }
559 guard let fileURL = fileURL else {
560 return .failure(AFError.responseSerializationFailed(reason: .inputFileNil))
564 let data = try Data(contentsOf: fileURL)
565 return Request.serializeResponseJSON(options: options, response: response, data: data, error: error)
567 return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)))
572 /// Adds a handler to be called once the request has finished.
574 /// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
575 /// - parameter completionHandler: A closure to be executed once the request has finished.
577 /// - returns: The request.
579 public func responseJSON(
580 queue: DispatchQueue? = nil,
581 options: JSONSerialization.ReadingOptions = .allowFragments,
582 completionHandler: @escaping (DownloadResponse<Any>) -> Void)
587 responseSerializer: DownloadRequest.jsonResponseSerializer(options: options),
588 completionHandler: completionHandler
593 // MARK: - Property List
596 /// Returns a plist object contained in a result type constructed from the response data using
597 /// `PropertyListSerialization` with the specified reading options.
599 /// - parameter options: The property list reading options. Defaults to `[]`.
600 /// - parameter response: The response from the server.
601 /// - parameter data: The data returned from the server.
602 /// - parameter error: The error already encountered if it exists.
604 /// - returns: The result data type.
605 public static func serializeResponsePropertyList(
606 options: PropertyListSerialization.ReadOptions,
607 response: HTTPURLResponse?,
612 guard error == nil else { return .failure(error!) }
614 if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(NSNull()) }
616 guard let validData = data, validData.count > 0 else {
617 return .failure(AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength))
621 let plist = try PropertyListSerialization.propertyList(from: validData, options: options, format: nil)
622 return .success(plist)
624 return .failure(AFError.responseSerializationFailed(reason: .propertyListSerializationFailed(error: error)))
629 extension DataRequest {
630 /// Creates a response serializer that returns an object constructed from the response data using
631 /// `PropertyListSerialization` with the specified reading options.
633 /// - parameter options: The property list reading options. Defaults to `[]`.
635 /// - returns: A property list object response serializer.
636 public static func propertyListResponseSerializer(
637 options: PropertyListSerialization.ReadOptions = [])
638 -> DataResponseSerializer<Any>
640 return DataResponseSerializer { _, response, data, error in
641 return Request.serializeResponsePropertyList(options: options, response: response, data: data, error: error)
645 /// Adds a handler to be called once the request has finished.
647 /// - parameter options: The property list reading options. Defaults to `[]`.
648 /// - parameter completionHandler: A closure to be executed once the request has finished.
650 /// - returns: The request.
652 public func responsePropertyList(
653 queue: DispatchQueue? = nil,
654 options: PropertyListSerialization.ReadOptions = [],
655 completionHandler: @escaping (DataResponse<Any>) -> Void)
660 responseSerializer: DataRequest.propertyListResponseSerializer(options: options),
661 completionHandler: completionHandler
666 extension DownloadRequest {
667 /// Creates a response serializer that returns an object constructed from the response data using
668 /// `PropertyListSerialization` with the specified reading options.
670 /// - parameter options: The property list reading options. Defaults to `[]`.
672 /// - returns: A property list object response serializer.
673 public static func propertyListResponseSerializer(
674 options: PropertyListSerialization.ReadOptions = [])
675 -> DownloadResponseSerializer<Any>
677 return DownloadResponseSerializer { _, response, fileURL, error in
678 guard error == nil else { return .failure(error!) }
680 guard let fileURL = fileURL else {
681 return .failure(AFError.responseSerializationFailed(reason: .inputFileNil))
685 let data = try Data(contentsOf: fileURL)
686 return Request.serializeResponsePropertyList(options: options, response: response, data: data, error: error)
688 return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)))
693 /// Adds a handler to be called once the request has finished.
695 /// - parameter options: The property list reading options. Defaults to `[]`.
696 /// - parameter completionHandler: A closure to be executed once the request has finished.
698 /// - returns: The request.
700 public func responsePropertyList(
701 queue: DispatchQueue? = nil,
702 options: PropertyListSerialization.ReadOptions = [],
703 completionHandler: @escaping (DownloadResponse<Any>) -> Void)
708 responseSerializer: DownloadRequest.propertyListResponseSerializer(options: options),
709 completionHandler: completionHandler
714 /// A set of HTTP response status code that do not contain response data.
715 private let emptyDataStatusCodes: Set<Int> = [204, 205]