New file |
| | |
| | | // AFURLResponseSerialization.m |
| | | // Copyright (c) 2011–2016 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 "AFURLResponseSerialization.h" |
| | | |
| | | #import <TargetConditionals.h> |
| | | |
| | | #if TARGET_OS_IOS |
| | | #import <UIKit/UIKit.h> |
| | | #elif TARGET_OS_WATCH |
| | | #import <WatchKit/WatchKit.h> |
| | | #elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) |
| | | #import <Cocoa/Cocoa.h> |
| | | #endif |
| | | |
| | | NSString * const AFURLResponseSerializationErrorDomain = @"com.alamofire.error.serialization.response"; |
| | | NSString * const AFNetworkingOperationFailingURLResponseErrorKey = @"com.alamofire.serialization.response.error.response"; |
| | | NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey = @"com.alamofire.serialization.response.error.data"; |
| | | |
| | | static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) { |
| | | if (!error) { |
| | | return underlyingError; |
| | | } |
| | | |
| | | if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) { |
| | | return error; |
| | | } |
| | | |
| | | NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy]; |
| | | mutableUserInfo[NSUnderlyingErrorKey] = underlyingError; |
| | | |
| | | return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo]; |
| | | } |
| | | |
| | | static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) { |
| | | if ([error.domain isEqualToString:domain] && error.code == code) { |
| | | return YES; |
| | | } else if (error.userInfo[NSUnderlyingErrorKey]) { |
| | | return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain); |
| | | } |
| | | |
| | | return NO; |
| | | } |
| | | |
| | | static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) { |
| | | if ([JSONObject isKindOfClass:[NSArray class]]) { |
| | | NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]]; |
| | | for (id value in (NSArray *)JSONObject) { |
| | | [mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)]; |
| | | } |
| | | |
| | | return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray]; |
| | | } else if ([JSONObject isKindOfClass:[NSDictionary class]]) { |
| | | NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject]; |
| | | for (id <NSCopying> key in [(NSDictionary *)JSONObject allKeys]) { |
| | | id value = (NSDictionary *)JSONObject[key]; |
| | | if (!value || [value isEqual:[NSNull null]]) { |
| | | [mutableDictionary removeObjectForKey:key]; |
| | | } else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) { |
| | | mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions); |
| | | } |
| | | } |
| | | |
| | | return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary]; |
| | | } |
| | | |
| | | return JSONObject; |
| | | } |
| | | |
| | | @implementation AFHTTPResponseSerializer |
| | | |
| | | + (instancetype)serializer { |
| | | return [[self alloc] init]; |
| | | } |
| | | |
| | | - (instancetype)init { |
| | | self = [super init]; |
| | | if (!self) { |
| | | return nil; |
| | | } |
| | | |
| | | self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)]; |
| | | self.acceptableContentTypes = nil; |
| | | |
| | | return self; |
| | | } |
| | | |
| | | #pragma mark - |
| | | |
| | | - (BOOL)validateResponse:(NSHTTPURLResponse *)response |
| | | data:(NSData *)data |
| | | error:(NSError * __autoreleasing *)error |
| | | { |
| | | BOOL responseIsValid = YES; |
| | | NSError *validationError = nil; |
| | | |
| | | if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) { |
| | | if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] && |
| | | !([response MIMEType] == nil && [data length] == 0)) { |
| | | |
| | | if ([data length] > 0 && [response URL]) { |
| | | NSMutableDictionary *mutableUserInfo = [@{ |
| | | NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]], |
| | | NSURLErrorFailingURLErrorKey:[response URL], |
| | | AFNetworkingOperationFailingURLResponseErrorKey: response, |
| | | } mutableCopy]; |
| | | if (data) { |
| | | mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data; |
| | | } |
| | | |
| | | validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError); |
| | | } |
| | | |
| | | responseIsValid = NO; |
| | | } |
| | | |
| | | if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) { |
| | | NSMutableDictionary *mutableUserInfo = [@{ |
| | | NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode], |
| | | NSURLErrorFailingURLErrorKey:[response URL], |
| | | AFNetworkingOperationFailingURLResponseErrorKey: response, |
| | | } mutableCopy]; |
| | | |
| | | if (data) { |
| | | mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data; |
| | | } |
| | | |
| | | validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError); |
| | | |
| | | responseIsValid = NO; |
| | | } |
| | | } |
| | | |
| | | if (error && !responseIsValid) { |
| | | *error = validationError; |
| | | } |
| | | |
| | | return responseIsValid; |
| | | } |
| | | |
| | | #pragma mark - AFURLResponseSerialization |
| | | |
| | | - (id)responseObjectForResponse:(NSURLResponse *)response |
| | | data:(NSData *)data |
| | | error:(NSError *__autoreleasing *)error |
| | | { |
| | | [self validateResponse:(NSHTTPURLResponse *)response data:data error:error]; |
| | | |
| | | return data; |
| | | } |
| | | |
| | | #pragma mark - NSSecureCoding |
| | | |
| | | + (BOOL)supportsSecureCoding { |
| | | return YES; |
| | | } |
| | | |
| | | - (instancetype)initWithCoder:(NSCoder *)decoder { |
| | | self = [self init]; |
| | | if (!self) { |
| | | return nil; |
| | | } |
| | | |
| | | self.acceptableStatusCodes = [decoder decodeObjectOfClass:[NSIndexSet class] forKey:NSStringFromSelector(@selector(acceptableStatusCodes))]; |
| | | self.acceptableContentTypes = [decoder decodeObjectOfClass:[NSIndexSet class] forKey:NSStringFromSelector(@selector(acceptableContentTypes))]; |
| | | |
| | | return self; |
| | | } |
| | | |
| | | - (void)encodeWithCoder:(NSCoder *)coder { |
| | | [coder encodeObject:self.acceptableStatusCodes forKey:NSStringFromSelector(@selector(acceptableStatusCodes))]; |
| | | [coder encodeObject:self.acceptableContentTypes forKey:NSStringFromSelector(@selector(acceptableContentTypes))]; |
| | | } |
| | | |
| | | #pragma mark - NSCopying |
| | | |
| | | - (instancetype)copyWithZone:(NSZone *)zone { |
| | | AFHTTPResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; |
| | | serializer.acceptableStatusCodes = [self.acceptableStatusCodes copyWithZone:zone]; |
| | | serializer.acceptableContentTypes = [self.acceptableContentTypes copyWithZone:zone]; |
| | | |
| | | return serializer; |
| | | } |
| | | |
| | | @end |
| | | |
| | | #pragma mark - |
| | | |
| | | @implementation AFJSONResponseSerializer |
| | | |
| | | + (instancetype)serializer { |
| | | return [self serializerWithReadingOptions:(NSJSONReadingOptions)0]; |
| | | } |
| | | |
| | | + (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions { |
| | | AFJSONResponseSerializer *serializer = [[self alloc] init]; |
| | | serializer.readingOptions = readingOptions; |
| | | |
| | | return serializer; |
| | | } |
| | | |
| | | - (instancetype)init { |
| | | self = [super init]; |
| | | if (!self) { |
| | | return nil; |
| | | } |
| | | |
| | | self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil]; |
| | | |
| | | return self; |
| | | } |
| | | |
| | | #pragma mark - AFURLResponseSerialization |
| | | |
| | | - (id)responseObjectForResponse:(NSURLResponse *)response |
| | | data:(NSData *)data |
| | | error:(NSError *__autoreleasing *)error |
| | | { |
| | | if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { |
| | | if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { |
| | | return nil; |
| | | } |
| | | } |
| | | |
| | | // Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization. |
| | | // See https://github.com/rails/rails/issues/1742 |
| | | BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]]; |
| | | |
| | | if (data.length == 0 || isSpace) { |
| | | return nil; |
| | | } |
| | | |
| | | NSError *serializationError = nil; |
| | | |
| | | id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError]; |
| | | |
| | | if (!responseObject) |
| | | { |
| | | if (error) { |
| | | *error = AFErrorWithUnderlyingError(serializationError, *error); |
| | | } |
| | | return nil; |
| | | } |
| | | |
| | | if (self.removesKeysWithNullValues) { |
| | | return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions); |
| | | } |
| | | |
| | | return responseObject; |
| | | } |
| | | |
| | | #pragma mark - NSSecureCoding |
| | | |
| | | - (instancetype)initWithCoder:(NSCoder *)decoder { |
| | | self = [super initWithCoder:decoder]; |
| | | if (!self) { |
| | | return nil; |
| | | } |
| | | |
| | | self.readingOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(readingOptions))] unsignedIntegerValue]; |
| | | self.removesKeysWithNullValues = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(removesKeysWithNullValues))] boolValue]; |
| | | |
| | | return self; |
| | | } |
| | | |
| | | - (void)encodeWithCoder:(NSCoder *)coder { |
| | | [super encodeWithCoder:coder]; |
| | | |
| | | [coder encodeObject:@(self.readingOptions) forKey:NSStringFromSelector(@selector(readingOptions))]; |
| | | [coder encodeObject:@(self.removesKeysWithNullValues) forKey:NSStringFromSelector(@selector(removesKeysWithNullValues))]; |
| | | } |
| | | |
| | | #pragma mark - NSCopying |
| | | |
| | | - (instancetype)copyWithZone:(NSZone *)zone { |
| | | AFJSONResponseSerializer *serializer = [super copyWithZone:zone]; |
| | | serializer.readingOptions = self.readingOptions; |
| | | serializer.removesKeysWithNullValues = self.removesKeysWithNullValues; |
| | | |
| | | return serializer; |
| | | } |
| | | |
| | | @end |
| | | |
| | | #pragma mark - |
| | | |
| | | @implementation AFXMLParserResponseSerializer |
| | | |
| | | + (instancetype)serializer { |
| | | AFXMLParserResponseSerializer *serializer = [[self alloc] init]; |
| | | |
| | | return serializer; |
| | | } |
| | | |
| | | - (instancetype)init { |
| | | self = [super init]; |
| | | if (!self) { |
| | | return nil; |
| | | } |
| | | |
| | | self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml", nil]; |
| | | |
| | | return self; |
| | | } |
| | | |
| | | #pragma mark - AFURLResponseSerialization |
| | | |
| | | - (id)responseObjectForResponse:(NSHTTPURLResponse *)response |
| | | data:(NSData *)data |
| | | error:(NSError *__autoreleasing *)error |
| | | { |
| | | if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { |
| | | if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { |
| | | return nil; |
| | | } |
| | | } |
| | | |
| | | return [[NSXMLParser alloc] initWithData:data]; |
| | | } |
| | | |
| | | @end |
| | | |
| | | #pragma mark - |
| | | |
| | | #ifdef __MAC_OS_X_VERSION_MIN_REQUIRED |
| | | |
| | | @implementation AFXMLDocumentResponseSerializer |
| | | |
| | | + (instancetype)serializer { |
| | | return [self serializerWithXMLDocumentOptions:0]; |
| | | } |
| | | |
| | | + (instancetype)serializerWithXMLDocumentOptions:(NSUInteger)mask { |
| | | AFXMLDocumentResponseSerializer *serializer = [[self alloc] init]; |
| | | serializer.options = mask; |
| | | |
| | | return serializer; |
| | | } |
| | | |
| | | - (instancetype)init { |
| | | self = [super init]; |
| | | if (!self) { |
| | | return nil; |
| | | } |
| | | |
| | | self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml", nil]; |
| | | |
| | | return self; |
| | | } |
| | | |
| | | #pragma mark - AFURLResponseSerialization |
| | | |
| | | - (id)responseObjectForResponse:(NSURLResponse *)response |
| | | data:(NSData *)data |
| | | error:(NSError *__autoreleasing *)error |
| | | { |
| | | if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { |
| | | if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { |
| | | return nil; |
| | | } |
| | | } |
| | | |
| | | NSError *serializationError = nil; |
| | | NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:data options:self.options error:&serializationError]; |
| | | |
| | | if (!document) |
| | | { |
| | | if (error) { |
| | | *error = AFErrorWithUnderlyingError(serializationError, *error); |
| | | } |
| | | return nil; |
| | | } |
| | | |
| | | return document; |
| | | } |
| | | |
| | | #pragma mark - NSSecureCoding |
| | | |
| | | - (instancetype)initWithCoder:(NSCoder *)decoder { |
| | | self = [super initWithCoder:decoder]; |
| | | if (!self) { |
| | | return nil; |
| | | } |
| | | |
| | | self.options = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(options))] unsignedIntegerValue]; |
| | | |
| | | return self; |
| | | } |
| | | |
| | | - (void)encodeWithCoder:(NSCoder *)coder { |
| | | [super encodeWithCoder:coder]; |
| | | |
| | | [coder encodeObject:@(self.options) forKey:NSStringFromSelector(@selector(options))]; |
| | | } |
| | | |
| | | #pragma mark - NSCopying |
| | | |
| | | - (instancetype)copyWithZone:(NSZone *)zone { |
| | | AFXMLDocumentResponseSerializer *serializer = [super copyWithZone:zone]; |
| | | serializer.options = self.options; |
| | | |
| | | return serializer; |
| | | } |
| | | |
| | | @end |
| | | |
| | | #endif |
| | | |
| | | #pragma mark - |
| | | |
| | | @implementation AFPropertyListResponseSerializer |
| | | |
| | | + (instancetype)serializer { |
| | | return [self serializerWithFormat:NSPropertyListXMLFormat_v1_0 readOptions:0]; |
| | | } |
| | | |
| | | + (instancetype)serializerWithFormat:(NSPropertyListFormat)format |
| | | readOptions:(NSPropertyListReadOptions)readOptions |
| | | { |
| | | AFPropertyListResponseSerializer *serializer = [[self alloc] init]; |
| | | serializer.format = format; |
| | | serializer.readOptions = readOptions; |
| | | |
| | | return serializer; |
| | | } |
| | | |
| | | - (instancetype)init { |
| | | self = [super init]; |
| | | if (!self) { |
| | | return nil; |
| | | } |
| | | |
| | | self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/x-plist", nil]; |
| | | |
| | | return self; |
| | | } |
| | | |
| | | #pragma mark - AFURLResponseSerialization |
| | | |
| | | - (id)responseObjectForResponse:(NSURLResponse *)response |
| | | data:(NSData *)data |
| | | error:(NSError *__autoreleasing *)error |
| | | { |
| | | if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { |
| | | if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { |
| | | return nil; |
| | | } |
| | | } |
| | | |
| | | if (!data) { |
| | | return nil; |
| | | } |
| | | |
| | | NSError *serializationError = nil; |
| | | |
| | | id responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError]; |
| | | |
| | | if (!responseObject) |
| | | { |
| | | if (error) { |
| | | *error = AFErrorWithUnderlyingError(serializationError, *error); |
| | | } |
| | | return nil; |
| | | } |
| | | |
| | | return responseObject; |
| | | } |
| | | |
| | | #pragma mark - NSSecureCoding |
| | | |
| | | - (instancetype)initWithCoder:(NSCoder *)decoder { |
| | | self = [super initWithCoder:decoder]; |
| | | if (!self) { |
| | | return nil; |
| | | } |
| | | |
| | | self.format = (NSPropertyListFormat)[[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(format))] unsignedIntegerValue]; |
| | | self.readOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(readOptions))] unsignedIntegerValue]; |
| | | |
| | | return self; |
| | | } |
| | | |
| | | - (void)encodeWithCoder:(NSCoder *)coder { |
| | | [super encodeWithCoder:coder]; |
| | | |
| | | [coder encodeObject:@(self.format) forKey:NSStringFromSelector(@selector(format))]; |
| | | [coder encodeObject:@(self.readOptions) forKey:NSStringFromSelector(@selector(readOptions))]; |
| | | } |
| | | |
| | | #pragma mark - NSCopying |
| | | |
| | | - (instancetype)copyWithZone:(NSZone *)zone { |
| | | AFPropertyListResponseSerializer *serializer = [super copyWithZone:zone]; |
| | | serializer.format = self.format; |
| | | serializer.readOptions = self.readOptions; |
| | | |
| | | return serializer; |
| | | } |
| | | |
| | | @end |
| | | |
| | | #pragma mark - |
| | | |
| | | #if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH |
| | | #import <CoreGraphics/CoreGraphics.h> |
| | | #import <UIKit/UIKit.h> |
| | | |
| | | @interface UIImage (AFNetworkingSafeImageLoading) |
| | | + (UIImage *)af_safeImageWithData:(NSData *)data; |
| | | @end |
| | | |
| | | static NSLock* imageLock = nil; |
| | | |
| | | @implementation UIImage (AFNetworkingSafeImageLoading) |
| | | |
| | | + (UIImage *)af_safeImageWithData:(NSData *)data { |
| | | UIImage* image = nil; |
| | | static dispatch_once_t onceToken; |
| | | dispatch_once(&onceToken, ^{ |
| | | imageLock = [[NSLock alloc] init]; |
| | | }); |
| | | |
| | | [imageLock lock]; |
| | | image = [UIImage imageWithData:data]; |
| | | [imageLock unlock]; |
| | | return image; |
| | | } |
| | | |
| | | @end |
| | | |
| | | static UIImage * AFImageWithDataAtScale(NSData *data, CGFloat scale) { |
| | | UIImage *image = [UIImage af_safeImageWithData:data]; |
| | | if (image.images) { |
| | | return image; |
| | | } |
| | | |
| | | return [[UIImage alloc] initWithCGImage:[image CGImage] scale:scale orientation:image.imageOrientation]; |
| | | } |
| | | |
| | | static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response, NSData *data, CGFloat scale) { |
| | | if (!data || [data length] == 0) { |
| | | return nil; |
| | | } |
| | | |
| | | CGImageRef imageRef = NULL; |
| | | CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); |
| | | |
| | | if ([response.MIMEType isEqualToString:@"image/png"]) { |
| | | imageRef = CGImageCreateWithPNGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault); |
| | | } else if ([response.MIMEType isEqualToString:@"image/jpeg"]) { |
| | | imageRef = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault); |
| | | |
| | | if (imageRef) { |
| | | CGColorSpaceRef imageColorSpace = CGImageGetColorSpace(imageRef); |
| | | CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(imageColorSpace); |
| | | |
| | | // CGImageCreateWithJPEGDataProvider does not properly handle CMKY, so fall back to AFImageWithDataAtScale |
| | | if (imageColorSpaceModel == kCGColorSpaceModelCMYK) { |
| | | CGImageRelease(imageRef); |
| | | imageRef = NULL; |
| | | } |
| | | } |
| | | } |
| | | |
| | | CGDataProviderRelease(dataProvider); |
| | | |
| | | UIImage *image = AFImageWithDataAtScale(data, scale); |
| | | if (!imageRef) { |
| | | if (image.images || !image) { |
| | | return image; |
| | | } |
| | | |
| | | imageRef = CGImageCreateCopy([image CGImage]); |
| | | if (!imageRef) { |
| | | return nil; |
| | | } |
| | | } |
| | | |
| | | size_t width = CGImageGetWidth(imageRef); |
| | | size_t height = CGImageGetHeight(imageRef); |
| | | size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef); |
| | | |
| | | if (width * height > 1024 * 1024 || bitsPerComponent > 8) { |
| | | CGImageRelease(imageRef); |
| | | |
| | | return image; |
| | | } |
| | | |
| | | // CGImageGetBytesPerRow() calculates incorrectly in iOS 5.0, so defer to CGBitmapContextCreate |
| | | size_t bytesPerRow = 0; |
| | | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); |
| | | CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace); |
| | | CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); |
| | | |
| | | if (colorSpaceModel == kCGColorSpaceModelRGB) { |
| | | uint32_t alpha = (bitmapInfo & kCGBitmapAlphaInfoMask); |
| | | #pragma clang diagnostic push |
| | | #pragma clang diagnostic ignored "-Wassign-enum" |
| | | if (alpha == kCGImageAlphaNone) { |
| | | bitmapInfo &= ~kCGBitmapAlphaInfoMask; |
| | | bitmapInfo |= kCGImageAlphaNoneSkipFirst; |
| | | } else if (!(alpha == kCGImageAlphaNoneSkipFirst || alpha == kCGImageAlphaNoneSkipLast)) { |
| | | bitmapInfo &= ~kCGBitmapAlphaInfoMask; |
| | | bitmapInfo |= kCGImageAlphaPremultipliedFirst; |
| | | } |
| | | #pragma clang diagnostic pop |
| | | } |
| | | |
| | | CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo); |
| | | |
| | | CGColorSpaceRelease(colorSpace); |
| | | |
| | | if (!context) { |
| | | CGImageRelease(imageRef); |
| | | |
| | | return image; |
| | | } |
| | | |
| | | CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), imageRef); |
| | | CGImageRef inflatedImageRef = CGBitmapContextCreateImage(context); |
| | | |
| | | CGContextRelease(context); |
| | | |
| | | UIImage *inflatedImage = [[UIImage alloc] initWithCGImage:inflatedImageRef scale:scale orientation:image.imageOrientation]; |
| | | |
| | | CGImageRelease(inflatedImageRef); |
| | | CGImageRelease(imageRef); |
| | | |
| | | return inflatedImage; |
| | | } |
| | | #endif |
| | | |
| | | |
| | | @implementation AFImageResponseSerializer |
| | | |
| | | - (instancetype)init { |
| | | self = [super init]; |
| | | if (!self) { |
| | | return nil; |
| | | } |
| | | |
| | | self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"image/tiff", @"image/jpeg", @"image/gif", @"image/png", @"image/ico", @"image/x-icon", @"image/bmp", @"image/x-bmp", @"image/x-xbitmap", @"image/x-win-bitmap", nil]; |
| | | |
| | | #if TARGET_OS_IOS || TARGET_OS_TV |
| | | self.imageScale = [[UIScreen mainScreen] scale]; |
| | | self.automaticallyInflatesResponseImage = YES; |
| | | #elif TARGET_OS_WATCH |
| | | self.imageScale = [[WKInterfaceDevice currentDevice] screenScale]; |
| | | self.automaticallyInflatesResponseImage = YES; |
| | | #endif |
| | | |
| | | return self; |
| | | } |
| | | |
| | | #pragma mark - AFURLResponseSerializer |
| | | |
| | | - (id)responseObjectForResponse:(NSURLResponse *)response |
| | | data:(NSData *)data |
| | | error:(NSError *__autoreleasing *)error |
| | | { |
| | | if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { |
| | | if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { |
| | | return nil; |
| | | } |
| | | } |
| | | |
| | | #if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH |
| | | if (self.automaticallyInflatesResponseImage) { |
| | | return AFInflatedImageFromResponseWithDataAtScale((NSHTTPURLResponse *)response, data, self.imageScale); |
| | | } else { |
| | | return AFImageWithDataAtScale(data, self.imageScale); |
| | | } |
| | | #else |
| | | // Ensure that the image is set to it's correct pixel width and height |
| | | NSBitmapImageRep *bitimage = [[NSBitmapImageRep alloc] initWithData:data]; |
| | | NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize([bitimage pixelsWide], [bitimage pixelsHigh])]; |
| | | [image addRepresentation:bitimage]; |
| | | |
| | | return image; |
| | | #endif |
| | | |
| | | return nil; |
| | | } |
| | | |
| | | #pragma mark - NSSecureCoding |
| | | |
| | | - (instancetype)initWithCoder:(NSCoder *)decoder { |
| | | self = [super initWithCoder:decoder]; |
| | | if (!self) { |
| | | return nil; |
| | | } |
| | | |
| | | #if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH |
| | | NSNumber *imageScale = [decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(imageScale))]; |
| | | #if CGFLOAT_IS_DOUBLE |
| | | self.imageScale = [imageScale doubleValue]; |
| | | #else |
| | | self.imageScale = [imageScale floatValue]; |
| | | #endif |
| | | |
| | | self.automaticallyInflatesResponseImage = [decoder decodeBoolForKey:NSStringFromSelector(@selector(automaticallyInflatesResponseImage))]; |
| | | #endif |
| | | |
| | | return self; |
| | | } |
| | | |
| | | - (void)encodeWithCoder:(NSCoder *)coder { |
| | | [super encodeWithCoder:coder]; |
| | | |
| | | #if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH |
| | | [coder encodeObject:@(self.imageScale) forKey:NSStringFromSelector(@selector(imageScale))]; |
| | | [coder encodeBool:self.automaticallyInflatesResponseImage forKey:NSStringFromSelector(@selector(automaticallyInflatesResponseImage))]; |
| | | #endif |
| | | } |
| | | |
| | | #pragma mark - NSCopying |
| | | |
| | | - (instancetype)copyWithZone:(NSZone *)zone { |
| | | AFImageResponseSerializer *serializer = [super copyWithZone:zone]; |
| | | |
| | | #if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH |
| | | serializer.imageScale = self.imageScale; |
| | | serializer.automaticallyInflatesResponseImage = self.automaticallyInflatesResponseImage; |
| | | #endif |
| | | |
| | | return serializer; |
| | | } |
| | | |
| | | @end |
| | | |
| | | #pragma mark - |
| | | |
| | | @interface AFCompoundResponseSerializer () |
| | | @property (readwrite, nonatomic, copy) NSArray *responseSerializers; |
| | | @end |
| | | |
| | | @implementation AFCompoundResponseSerializer |
| | | |
| | | + (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSerializers { |
| | | AFCompoundResponseSerializer *serializer = [[self alloc] init]; |
| | | serializer.responseSerializers = responseSerializers; |
| | | |
| | | return serializer; |
| | | } |
| | | |
| | | #pragma mark - AFURLResponseSerialization |
| | | |
| | | - (id)responseObjectForResponse:(NSURLResponse *)response |
| | | data:(NSData *)data |
| | | error:(NSError *__autoreleasing *)error |
| | | { |
| | | for (id <AFURLResponseSerialization> serializer in self.responseSerializers) { |
| | | if (![serializer isKindOfClass:[AFHTTPResponseSerializer class]]) { |
| | | continue; |
| | | } |
| | | |
| | | NSError *serializerError = nil; |
| | | id responseObject = [serializer responseObjectForResponse:response data:data error:&serializerError]; |
| | | if (responseObject) { |
| | | if (error) { |
| | | *error = AFErrorWithUnderlyingError(serializerError, *error); |
| | | } |
| | | |
| | | return responseObject; |
| | | } |
| | | } |
| | | |
| | | return [super responseObjectForResponse:response data:data error:error]; |
| | | } |
| | | |
| | | #pragma mark - NSSecureCoding |
| | | |
| | | - (instancetype)initWithCoder:(NSCoder *)decoder { |
| | | self = [super initWithCoder:decoder]; |
| | | if (!self) { |
| | | return nil; |
| | | } |
| | | |
| | | self.responseSerializers = [decoder decodeObjectOfClass:[NSArray class] forKey:NSStringFromSelector(@selector(responseSerializers))]; |
| | | |
| | | return self; |
| | | } |
| | | |
| | | - (void)encodeWithCoder:(NSCoder *)coder { |
| | | [super encodeWithCoder:coder]; |
| | | |
| | | [coder encodeObject:self.responseSerializers forKey:NSStringFromSelector(@selector(responseSerializers))]; |
| | | } |
| | | |
| | | #pragma mark - NSCopying |
| | | |
| | | - (instancetype)copyWithZone:(NSZone *)zone { |
| | | AFCompoundResponseSerializer *serializer = [super copyWithZone:zone]; |
| | | serializer.responseSerializers = self.responseSerializers; |
| | | |
| | | return serializer; |
| | | } |
| | | |
| | | @end |