Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
//
2
//  Request+AlamofireImage.swift
3
//
4
//  Copyright (c) 2015 Alamofire Software Foundation (http://alamofire.org/)
5
//
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:
12
//
13
//  The above copyright notice and this permission notice shall be included in
14
//  all copies or substantial portions of the Software.
15
//
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
22
//  THE SOFTWARE.
23
//
24
 
25
import Alamofire
26
import Foundation
27
 
28
#if os(iOS) || os(tvOS)
29
import UIKit
30
#elseif os(watchOS)
31
import UIKit
32
import WatchKit
33
#elseif os(macOS)
34
import Cocoa
35
#endif
36
 
37
public final class ImageResponseSerializer: ResponseSerializer {
38
    // MARK: Properties
39
 
40
    public static var deviceScreenScale: CGFloat { DataRequest.imageScale }
41
 
42
    public let imageScale: CGFloat
43
    public let inflateResponseImage: Bool
44
    public let emptyResponseCodes: Set<Int>
45
    public let emptyRequestMethods: Set<HTTPMethod>
46
 
47
    static var acceptableImageContentTypes: Set<String> = {
48
        var contentTypes: Set<String> = ["application/octet-stream",
49
                                         "image/tiff",
50
                                         "image/jpg",
51
                                         "image/jpeg",
52
                                         "image/jp2",
53
                                         "image/gif",
54
                                         "image/png",
55
                                         "image/ico",
56
                                         "image/x-icon",
57
                                         "image/bmp",
58
                                         "image/x-bmp",
59
                                         "image/x-xbitmap",
60
                                         "image/x-ms-bmp",
61
                                         "image/x-win-bitmap"]
62
 
63
        #if os(macOS) || os(iOS) // No WebP support on tvOS or watchOS.
64
        if #available(macOS 11, iOS 14, *) {
65
            contentTypes.formUnion(["image/webp"])
66
        }
67
        #endif
68
 
69
        if #available(macOS 10.13, iOS 11, tvOS 11, watchOS 4, *) {
70
            contentTypes.formUnion(["image/heic", "image/heif"])
71
        }
72
 
73
        return contentTypes
74
    }()
75
 
76
    static let streamImageInitialBytePattern = Data([255, 216]) // 0xffd8
77
 
78
    // MARK: Initialization
79
 
80
    public init(imageScale: CGFloat = ImageResponseSerializer.deviceScreenScale,
81
                inflateResponseImage: Bool = true,
82
                emptyResponseCodes: Set<Int> = ImageResponseSerializer.defaultEmptyResponseCodes,
83
                emptyRequestMethods: Set<HTTPMethod> = ImageResponseSerializer.defaultEmptyRequestMethods) {
84
        self.imageScale = imageScale
85
        self.inflateResponseImage = inflateResponseImage
86
        self.emptyResponseCodes = emptyResponseCodes
87
        self.emptyRequestMethods = emptyRequestMethods
88
    }
89
 
90
    // MARK: Serialization
91
 
92
    public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Image {
93
        guard error == nil else { throw error! }
94
 
95
        guard let data = data, !data.isEmpty else {
96
            guard emptyResponseAllowed(forRequest: request, response: response) else {
97
                throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)
98
            }
99
 
100
            print("Returning empty image!")
101
            return Image()
102
        }
103
 
104
        try validateContentType(for: request, response: response)
105
        let image = try serializeImage(from: data)
106
 
107
        return image
108
    }
109
 
110
    public func serializeImage(from data: Data) throws -> Image {
111
        guard !data.isEmpty else {
112
            throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)
113
        }
114
 
115
        #if os(iOS) || os(tvOS) || os(watchOS)
116
        guard let image = UIImage.af.threadSafeImage(with: data, scale: imageScale) else {
117
            throw AFIError.imageSerializationFailed
118
        }
119
 
120
        if inflateResponseImage { image.af.inflate() }
121
        #elseif os(macOS)
122
        guard let bitmapImage = NSBitmapImageRep(data: data) else {
123
            throw AFIError.imageSerializationFailed
124
        }
125
 
126
        let image = NSImage(size: NSSize(width: bitmapImage.pixelsWide, height: bitmapImage.pixelsHigh))
127
        image.addRepresentation(bitmapImage)
128
        #endif
129
 
130
        return image
131
    }
132
 
133
    // MARK: Content Type Validation
134
 
135
    /// Adds the content types specified to the list of acceptable images content types for validation.
136
    ///
137
    /// - parameter contentTypes: The additional content types.
138
    public class func addAcceptableImageContentTypes(_ contentTypes: Set<String>) {
139
        ImageResponseSerializer.acceptableImageContentTypes.formUnion(contentTypes)
140
    }
141
 
142
    public func validateContentType(for request: URLRequest?, response: HTTPURLResponse?) throws {
143
        if let url = request?.url, url.isFileURL { return }
144
 
145
        guard let mimeType = response?.mimeType else {
146
            let contentTypes = Array(ImageResponseSerializer.acceptableImageContentTypes)
147
            throw AFError.responseValidationFailed(reason: .missingContentType(acceptableContentTypes: contentTypes))
148
        }
149
 
150
        guard ImageResponseSerializer.acceptableImageContentTypes.contains(mimeType) else {
151
            let contentTypes = Array(ImageResponseSerializer.acceptableImageContentTypes)
152
 
153
            throw AFError.responseValidationFailed(
154
                reason: .unacceptableContentType(acceptableContentTypes: contentTypes, responseContentType: mimeType)
155
            )
156
        }
157
    }
158
}
159
 
160
// MARK: - Image Scale
161
 
162
extension DataRequest {
163
    public class var imageScale: CGFloat {
164
        #if os(iOS) || os(tvOS)
165
        return UIScreen.main.scale
166
        #elseif os(watchOS)
167
        return WKInterfaceDevice.current().screenScale
168
        #elseif os(macOS)
169
        return 1.0
170
        #endif
171
    }
172
}
173
 
174
// MARK: - iOS, tvOS, and watchOS
175
 
176
#if os(iOS) || os(tvOS) || os(watchOS)
177
 
178
extension DataRequest {
179
    /// Adds a response handler to be called once the request has finished.
180
    ///
181
    /// - parameter imageScale:           The scale factor used when interpreting the image data to construct
182
    ///                                   `responseImage`. Specifying a scale factor of 1.0 results in an image whose
183
    ///                                   size matches the pixel-based dimensions of the image. Applying a different
184
    ///                                   scale factor changes the size of the image as reported by the size property.
185
    ///                                   This is set to the value of scale of the main screen by default, which
186
    ///                                   automatically scales images for retina displays, for instance.
187
    ///                                   `Screen.scale` by default.
188
    /// - parameter inflateResponseImage: Whether to automatically inflate response image data for compressed formats
189
    ///                                   (such as PNG or JPEG). Enabling this can significantly improve drawing
190
    ///                                   performance as it allows a bitmap representation to be constructed in the
191
    ///                                   background rather than on the main thread. `true` by default.
192
    /// - parameter queue:                The queue on which the completion handler is dispatched. `.main` by default.
193
    /// - parameter completionHandler:    A closure to be executed once the request has finished. The closure takes 4
194
    ///                                   arguments: the URL request, the URL response, if one was received, the image,
195
    ///                                   if one could be created from the URL response and data, and any error produced
196
    ///                                   while creating the image.
197
    ///
198
    /// - returns: The request.
199
    @discardableResult
200
    public func responseImage(imageScale: CGFloat = DataRequest.imageScale,
201
                              inflateResponseImage: Bool = true,
202
                              queue: DispatchQueue = .main,
203
                              completionHandler: @escaping (AFDataResponse<Image>) -> Void)
204
        -> Self {
205
        response(queue: queue,
206
                 responseSerializer: ImageResponseSerializer(imageScale: imageScale,
207
                                                             inflateResponseImage: inflateResponseImage),
208
                 completionHandler: completionHandler)
209
    }
210
}
211
 
212
// MARK: - macOS
213
 
214
#elseif os(macOS)
215
 
216
extension DataRequest {
217
    /// Adds a response handler to be called once the request has finished.
218
    ///
219
    /// - Parameters:
220
    ///   - queue:             The queue on which the completion handler is dispatched. `.main` by default.
221
    ///   - completionHandler: A closure to be executed once the request has finished. The closure takes 4 arguments:
222
    ///                        the URL request, the URL response, if one was received, the image, if one could be
223
    ///                        created from the URL response and data, and any error produced while creating the image.
224
    ///
225
    /// - returns: The request.
226
    @discardableResult
227
    public func responseImage(queue: DispatchQueue = .main,
228
                              completionHandler: @escaping (AFDataResponse<Image>) -> Void)
229
        -> Self {
230
        response(queue: queue,
231
                 responseSerializer: ImageResponseSerializer(inflateResponseImage: false),
232
                 completionHandler: completionHandler)
233
    }
234
}
235
 
236
#endif