AutorÃa | Ultima modificación | Ver Log |
//// Request+AlamofireImage.swift//// Copyright (c) 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 Alamofireimport Foundation#if os(iOS) || os(tvOS)import UIKit#elseif os(watchOS)import UIKitimport WatchKit#elseif os(macOS)import Cocoa#endifpublic final class ImageResponseSerializer: ResponseSerializer {// MARK: Propertiespublic static var deviceScreenScale: CGFloat { DataRequest.imageScale }public let imageScale: CGFloatpublic let inflateResponseImage: Boolpublic let emptyResponseCodes: Set<Int>public let emptyRequestMethods: Set<HTTPMethod>static var acceptableImageContentTypes: Set<String> = {var contentTypes: Set<String> = ["application/octet-stream","image/tiff","image/jpg","image/jpeg","image/jp2","image/gif","image/png","image/ico","image/x-icon","image/bmp","image/x-bmp","image/x-xbitmap","image/x-ms-bmp","image/x-win-bitmap"]#if os(macOS) || os(iOS) // No WebP support on tvOS or watchOS.if #available(macOS 11, iOS 14, *) {contentTypes.formUnion(["image/webp"])}#endifif #available(macOS 10.13, iOS 11, tvOS 11, watchOS 4, *) {contentTypes.formUnion(["image/heic", "image/heif"])}return contentTypes}()static let streamImageInitialBytePattern = Data([255, 216]) // 0xffd8// MARK: Initializationpublic init(imageScale: CGFloat = ImageResponseSerializer.deviceScreenScale,inflateResponseImage: Bool = true,emptyResponseCodes: Set<Int> = ImageResponseSerializer.defaultEmptyResponseCodes,emptyRequestMethods: Set<HTTPMethod> = ImageResponseSerializer.defaultEmptyRequestMethods) {self.imageScale = imageScaleself.inflateResponseImage = inflateResponseImageself.emptyResponseCodes = emptyResponseCodesself.emptyRequestMethods = emptyRequestMethods}// MARK: Serializationpublic func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Image {guard error == nil else { throw error! }guard let data = data, !data.isEmpty else {guard emptyResponseAllowed(forRequest: request, response: response) else {throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)}print("Returning empty image!")return Image()}try validateContentType(for: request, response: response)let image = try serializeImage(from: data)return image}public func serializeImage(from data: Data) throws -> Image {guard !data.isEmpty else {throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)}#if os(iOS) || os(tvOS) || os(watchOS)guard let image = UIImage.af.threadSafeImage(with: data, scale: imageScale) else {throw AFIError.imageSerializationFailed}if inflateResponseImage { image.af.inflate() }#elseif os(macOS)guard let bitmapImage = NSBitmapImageRep(data: data) else {throw AFIError.imageSerializationFailed}let image = NSImage(size: NSSize(width: bitmapImage.pixelsWide, height: bitmapImage.pixelsHigh))image.addRepresentation(bitmapImage)#endifreturn image}// MARK: Content Type Validation/// Adds the content types specified to the list of acceptable images content types for validation.////// - parameter contentTypes: The additional content types.public class func addAcceptableImageContentTypes(_ contentTypes: Set<String>) {ImageResponseSerializer.acceptableImageContentTypes.formUnion(contentTypes)}public func validateContentType(for request: URLRequest?, response: HTTPURLResponse?) throws {if let url = request?.url, url.isFileURL { return }guard let mimeType = response?.mimeType else {let contentTypes = Array(ImageResponseSerializer.acceptableImageContentTypes)throw AFError.responseValidationFailed(reason: .missingContentType(acceptableContentTypes: contentTypes))}guard ImageResponseSerializer.acceptableImageContentTypes.contains(mimeType) else {let contentTypes = Array(ImageResponseSerializer.acceptableImageContentTypes)throw AFError.responseValidationFailed(reason: .unacceptableContentType(acceptableContentTypes: contentTypes, responseContentType: mimeType))}}}// MARK: - Image Scaleextension DataRequest {public class var imageScale: CGFloat {#if os(iOS) || os(tvOS)return UIScreen.main.scale#elseif os(watchOS)return WKInterfaceDevice.current().screenScale#elseif os(macOS)return 1.0#endif}}// MARK: - iOS, tvOS, and watchOS#if os(iOS) || os(tvOS) || os(watchOS)extension DataRequest {/// Adds a response handler to be called once the request has finished.////// - parameter imageScale: The scale factor used when interpreting the image data to construct/// `responseImage`. Specifying a scale factor of 1.0 results in an image whose/// size matches the pixel-based dimensions of the image. Applying a different/// scale factor changes the size of the image as reported by the size property./// This is set to the value of scale of the main screen by default, which/// automatically scales images for retina displays, for instance./// `Screen.scale` by default./// - parameter inflateResponseImage: Whether to automatically inflate response image data for compressed formats/// (such as PNG or JPEG). Enabling this can significantly improve drawing/// performance as it allows a bitmap representation to be constructed in the/// background rather than on the main thread. `true` by default./// - parameter queue: The queue on which the completion handler is dispatched. `.main` by default./// - parameter completionHandler: A closure to be executed once the request has finished. The closure takes 4/// arguments: the URL request, the URL response, if one was received, the image,/// if one could be created from the URL response and data, and any error produced/// while creating the image.////// - returns: The request.@discardableResultpublic func responseImage(imageScale: CGFloat = DataRequest.imageScale,inflateResponseImage: Bool = true,queue: DispatchQueue = .main,completionHandler: @escaping (AFDataResponse<Image>) -> Void)-> Self {response(queue: queue,responseSerializer: ImageResponseSerializer(imageScale: imageScale,inflateResponseImage: inflateResponseImage),completionHandler: completionHandler)}}// MARK: - macOS#elseif os(macOS)extension DataRequest {/// Adds a response handler to be called once the request has finished.////// - Parameters:/// - queue: The queue on which the completion handler is dispatched. `.main` by default./// - completionHandler: A closure to be executed once the request has finished. The closure takes 4 arguments:/// the URL request, the URL response, if one was received, the image, if one could be/// created from the URL response and data, and any error produced while creating the image.////// - returns: The request.@discardableResultpublic func responseImage(queue: DispatchQueue = .main,completionHandler: @escaping (AFDataResponse<Image>) -> Void)-> Self {response(queue: queue,responseSerializer: ImageResponseSerializer(inflateResponseImage: false),completionHandler: completionHandler)}}#endif