diff --git a/AFNetworking.podspec b/AFNetworking.podspec index f551c63..91be534 100644 --- a/AFNetworking.podspec +++ b/AFNetworking.podspec @@ -1,11 +1,12 @@ Pod::Spec.new do |s| s.name = 'AFNetworking' - s.version = '0.8.0' + s.version = '0.9.0' + s.license = 'MIT' s.summary = 'A delightful iOS and OS X networking framework' s.homepage = 'https://github.com/AFNetworking/AFNetworking' s.authors = {'Mattt Thompson' => 'm@mattt.me', 'Scott Raymond' => 'sco@gowalla.com'} - s.source = { :git => 'https://github.com/AFNetworking/AFNetworking.git', - :tag => '0.8.0' } - + s.source = { :git => 'https://github.com/AFNetworking/AFNetworking.git', :tag => '0.9.0' } s.source_files = 'AFNetworking' + s.clean_paths = ['iOS Example', 'Mac Example', 'AFNetworking.xcworkspace'] + s.framework = 'SystemConfiguration' end diff --git a/AFNetworking/AFHTTPClient.h b/AFNetworking/AFHTTPClient.h index 62fa457..6797d4f 100644 --- a/AFNetworking/AFHTTPClient.h +++ b/AFNetworking/AFHTTPClient.h @@ -160,9 +160,24 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete */ - (id)initWithBaseURL:(NSURL *)url; -///---------------------------------- +///----------------------------------- +/// @name Managing Reachability Status +///----------------------------------- + +/** + Sets a callback to be executed when the network availability of the `baseURL` host changes. + + @param block A block object to be executed when the network availability of the `baseURL` host changes.. This block has no return value and takes a single argument, which is `YES` if the host is available, otherwise `NO`. + + @warning This method requires the `SystemConfiguration` framework. Add it in the active target's "Link Binary With Library" build phase, and add `#import ` to the header prefix of the project (Prefix.pch). + */ +#ifdef _SYSTEMCONFIGURATION_H +- (void)setReachabilityStatusChangeBlock:(void (^)(BOOL isNetworkReachable))block; +#endif + +///------------------------------- /// @name Managing HTTP Operations -///---------------------------------- +///------------------------------- /** Attempts to register a subclass of `AFHTTPRequestOperation`, adding it to a chain to automatically generate request operations from a URL request. @@ -294,12 +309,40 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete - (void)enqueueHTTPRequestOperation:(AFHTTPRequestOperation *)operation; /** - Cancels all operations in the HTTP client's operation queue that match the specified HTTP method and URL. + Cancels all operations in the HTTP client's operation queue whose URLs match the specified HTTP method and path. - @param method The HTTP method to match for the cancelled requests, such as `GET`, `POST`, `PUT`, or `DELETE`. - @param url The URL to match for the cancelled requests. + @param method The HTTP method to match for the cancelled requests, such as `GET`, `POST`, `PUT`, or `DELETE`. If `nil`, all request operations with URLs matching the path will be cancelled. + @param url The path to match for the cancelled requests. */ -- (void)cancelHTTPOperationsWithMethod:(NSString *)method andURL:(NSURL *)url; +- (void)cancelAllHTTPOperationsWithMethod:(NSString *)method path:(NSString *)path; + +///--------------------------------------- +/// @name Batching HTTP Request Operations +///--------------------------------------- + +/** + Creates and enqueues an `AFHTTPRequestOperation` to the HTTP client's operation queue for each specified request object into a batch. When each request operation finishes, the specified progress block is executed, until all of the request operations have finished, at which point the completion block also executes. + + @param requests The `NSURLRequest` objects used to create and enqueue operations. + @param progressBlock A block object to be executed upon the completion of each request operation in the batch. This block has no return value and takes two arguments: the number of operations that have already finished execution, and the total number of operations. + @param completionBlock A block object to be executed upon the completion of all of the request operations in the batch. This block has no return value and takes a single argument: the batched request operations. + + @discussion Operations are created by passing the specified `NSURLRequest` objects in `requests`, using `-HTTPRequestOperationWithRequest:success:failure:`, with `nil` for both the `success` and `failure` parameters. + */ +- (void)enqueueBatchOfHTTPRequestOperationsWithRequests:(NSArray *)requests + progressBlock:(void (^)(NSUInteger numberOfCompletedOperations, NSUInteger totalNumberOfOperations))progressBlock + completionBlock:(void (^)(NSArray *operations))completionBlock; + +/** + Enqueues the specified request operations into a batch. When each request operation finishes, the specified progress block is executed, until all of the request operations have finished, at which point the completion block also executes. + + @param operations The request operations used to be batched and enqueued. + @param progressBlock A block object to be executed upon the completion of each request operation in the batch. This block has no return value and takes two arguments: the number of operations that have already finished execution, and the total number of operations. + @param completionBlock A block object to be executed upon the completion of all of the request operations in the batch. This block has no return value and takes a single argument: the batched request operations. + */ +- (void)enqueueBatchOfHTTPRequestOperations:(NSArray *)operations + progressBlock:(void (^)(NSUInteger numberOfCompletedOperations, NSUInteger totalNumberOfOperations))progressBlock + completionBlock:(void (^)(NSArray *operations))completionBlock; ///--------------------------- /// @name Making HTTP Requests diff --git a/AFNetworking/AFHTTPClient.m b/AFNetworking/AFHTTPClient.m index e7eaa7e..6ddebdc 100644 --- a/AFNetworking/AFHTTPClient.m +++ b/AFNetworking/AFHTTPClient.m @@ -32,6 +32,11 @@ #import #endif +#ifdef _SYSTEMCONFIGURATION_H +#import +#endif + +static NSString * const kAFMultipartFormLineDelimiter = @"\r\n"; // CRLF static NSString * const kAFMultipartFormBoundary = @"Boundary+0xAbCdEfGbOuNdArY"; @interface AFMultipartFormData : NSObject { @@ -48,6 +53,15 @@ static NSString * const kAFMultipartFormBoundary = @"Boundary+0xAbCdEfGbOuNdArY" #pragma mark - +#ifdef _SYSTEMCONFIGURATION_H +typedef SCNetworkReachabilityRef AFNetworkReachabilityRef; +#else +typedef id AFNetworkReachabilityRef; +#endif + +typedef void (^AFNetworkReachabilityStatusBlock)(BOOL isNetworkReachable); +typedef void (^AFCompletionBlock)(void); + static NSUInteger const kAFHTTPClientDefaultMaxConcurrentOperationCount = 4; static NSString * AFBase64EncodedStringFromString(NSString *string) { @@ -132,6 +146,8 @@ static NSString * AFPropertyListStringFromParameters(NSDictionary *parameters) { @property (readwrite, nonatomic, retain) NSMutableArray *registeredHTTPOperationClassNames; @property (readwrite, nonatomic, retain) NSMutableDictionary *defaultHeaders; @property (readwrite, nonatomic, retain) NSOperationQueue *operationQueue; +@property (readwrite, nonatomic, assign) AFNetworkReachabilityRef networkReachability; +@property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock; @end @implementation AFHTTPClient @@ -141,6 +157,8 @@ static NSString * AFPropertyListStringFromParameters(NSDictionary *parameters) { @synthesize registeredHTTPOperationClassNames = _registeredHTTPOperationClassNames; @synthesize defaultHeaders = _defaultHeaders; @synthesize operationQueue = _operationQueue; +@synthesize networkReachability = _networkReachability; +@synthesize networkReachabilityStatusBlock = _networkReachabilityStatusBlock; + (AFHTTPClient *)clientWithBaseURL:(NSURL *)url { return [[[self alloc] initWithBaseURL:url] autorelease]; @@ -186,9 +204,43 @@ static NSString * AFPropertyListStringFromParameters(NSDictionary *parameters) { [_registeredHTTPOperationClassNames release]; [_defaultHeaders release]; [_operationQueue release]; + [_networkReachabilityStatusBlock release]; + if (_networkReachability) { + CFRelease(_networkReachability); + } + [super dealloc]; } +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, defaultHeaders: %@, registeredOperationClasses: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.defaultHeaders, self.registeredHTTPOperationClassNames, self.operationQueue]; +} + +#pragma mark - + +#ifdef _SYSTEMCONFIGURATION_H +static void AFReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) { + if (info) { + AFNetworkReachabilityStatusBlock block = (AFNetworkReachabilityStatusBlock)info; + BOOL isNetworkReachable = (flags & kSCNetworkReachabilityFlagsReachable); + block(isNetworkReachable); + } +} + +- (void)setReachabilityStatusChangeBlock:(void (^)(BOOL isNetworkReachable))block { + if (_networkReachability) { + SCNetworkReachabilityUnscheduleFromRunLoop(_networkReachability, CFRunLoopGetMain(), (CFStringRef)NSRunLoopCommonModes); + CFRelease(_networkReachability); + } + + self.networkReachabilityStatusBlock = block; + self.networkReachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [[self.baseURL host] UTF8String]); + SCNetworkReachabilityContext context = {0, self.networkReachabilityStatusBlock, NULL, NULL, NULL}; + SCNetworkReachabilitySetCallback(self.networkReachability, AFReachabilityCallback, &context); + SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), (CFStringRef)NSRunLoopCommonModes); +} +#endif + #pragma mark - - (BOOL)registerHTTPOperationClass:(Class)operationClass { @@ -326,18 +378,64 @@ static NSString * AFPropertyListStringFromParameters(NSDictionary *parameters) { return operation; } +#pragma mark - + - (void)enqueueHTTPRequestOperation:(AFHTTPRequestOperation *)operation { [self.operationQueue addOperation:operation]; } -- (void)cancelHTTPOperationsWithMethod:(NSString *)method andURL:(NSURL *)url { +- (void)cancelAllHTTPOperationsWithMethod:(NSString *)method path:(NSString *)path { for (AFHTTPRequestOperation *operation in [self.operationQueue operations]) { - if ([[[operation request] HTTPMethod] isEqualToString:method] && [[[operation request] URL] isEqual:url]) { + if ((!method || [method isEqualToString:[[operation request] HTTPMethod]]) && [path isEqualToString:[[[operation request] URL] path]]) { [operation cancel]; } } } +- (void)enqueueBatchOfHTTPRequestOperationsWithRequests:(NSArray *)requests + progressBlock:(void (^)(NSUInteger numberOfCompletedOperations, NSUInteger totalNumberOfOperations))progressBlock + completionBlock:(void (^)(NSArray *operations))completionBlock +{ + NSMutableArray *mutableOperations = [NSMutableArray array]; + for (NSURLRequest *request in requests) { + AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:nil failure:nil]; + [mutableOperations addObject:operation]; + } + + [self enqueueBatchOfHTTPRequestOperations:mutableOperations progressBlock:progressBlock completionBlock:completionBlock]; +} + +- (void)enqueueBatchOfHTTPRequestOperations:(NSArray *)operations + progressBlock:(void (^)(NSUInteger numberOfCompletedOperations, NSUInteger totalNumberOfOperations))progressBlock + completionBlock:(void (^)(NSArray *operations))completionBlock +{ + NSBlockOperation *batchedOperation = [NSBlockOperation blockOperationWithBlock:^{ + if (completionBlock) { + completionBlock(operations); + } + }]; + + [self.operationQueue addOperation:batchedOperation]; + + NSPredicate *finishedOperationPredicate = [NSPredicate predicateWithFormat:@"isFinished == YES"]; + + for (AFHTTPRequestOperation *operation in operations) { + AFCompletionBlock originalCompletionBlock = [[operation.completionBlock copy] autorelease]; + operation.completionBlock = ^{ + if (progressBlock) { + progressBlock([[batchedOperation.dependencies filteredArrayUsingPredicate:finishedOperationPredicate] count], [batchedOperation.dependencies count]); + } + + if (originalCompletionBlock) { + originalCompletionBlock(); + } + }; + + [batchedOperation addDependency:operation]; + [self enqueueHTTPRequestOperation:operation]; + } +} + #pragma mark - - (void)getPath:(NSString *)path diff --git a/AFNetworking/AFHTTPRequestOperation.m b/AFNetworking/AFHTTPRequestOperation.m index c754af7..38802c1 100644 --- a/AFNetworking/AFHTTPRequestOperation.m +++ b/AFNetworking/AFHTTPRequestOperation.m @@ -24,7 +24,6 @@ @interface AFHTTPRequestOperation () @property (readwrite, nonatomic, retain) NSError *HTTPError; -@property (readonly, nonatomic, assign) BOOL hasContent; @end @implementation AFHTTPRequestOperation @@ -62,7 +61,7 @@ [userInfo setValue:[self.request URL] forKey:NSURLErrorFailingURLErrorKey]; self.HTTPError = [[[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorBadServerResponse userInfo:userInfo] autorelease]; - } else if ([self hasContent] && ![self hasAcceptableContentType]) { // Don't invalidate content type if there is no content + } else if ([self.responseData length] > 0 && ![self hasAcceptableContentType]) { // Don't invalidate content type if there is no content NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; [userInfo setValue:[NSString stringWithFormat:NSLocalizedString(@"Expected content type %@, got %@", nil), self.acceptableContentTypes, [self.response MIMEType]] forKey:NSLocalizedDescriptionKey]; [userInfo setValue:[self.request URL] forKey:NSURLErrorFailingURLErrorKey]; @@ -71,17 +70,13 @@ } } - if (_HTTPError) { - return _HTTPError; + if (self.HTTPError) { + return self.HTTPError; } else { return [super error]; } } -- (BOOL)hasContent { - return [self.responseData length] > 0; -} - - (BOOL)hasAcceptableStatusCode { return !self.acceptableStatusCodes || [self.acceptableStatusCodes containsIndex:[self.response statusCode]]; } diff --git a/AFNetworking/AFImageCache.h b/AFNetworking/AFImageCache.h deleted file mode 100644 index d2f15a9..0000000 --- a/AFNetworking/AFImageCache.h +++ /dev/null @@ -1,76 +0,0 @@ -// AFImageCache.h -// -// Copyright (c) 2011 Gowalla (http://gowalla.com/) -// -// 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 -#import "AFImageRequestOperation.h" - -#import - -/** - `AFImageCache` is an `NSCache` that stores and retrieves images from cache. - - @discussion `AFImageCache` is used to cache images for successful `AFImageRequestOperations` with the proper cache policy. - */ -@interface AFImageCache : NSCache - -#if __IPHONE_OS_VERSION_MIN_REQUIRED -/** - The scale factor used when interpreting the cached image data. 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 `[[UIScreen mainScreen] scale]` by default, which automatically scales images for retina displays, for instance. - */ -@property (nonatomic, assign) CGFloat imageScale; -#endif - -/** - Returns the shared image cache object for the system. - - @return The systemwide image cache. - */ -+ (AFImageCache *)sharedImageCache; - -/** - Returns the image associated with a given URL and cache name. - - @param url The URL associated with the image in the cache. - @param cacheName The cache name associated with the image in the cache. This allows for multiple versions of an image to be associated for a single URL, such as image thumbnails, for instance. - - @return The image associated with the URL and cache name, or `nil` if not image exists. - */ -#if __IPHONE_OS_VERSION_MIN_REQUIRED -- (UIImage *)cachedImageForURL:(NSURL *)url - cacheName:(NSString *)cacheName; -#elif __MAC_OS_X_VERSION_MIN_REQUIRED -- (NSImage *)cachedImageForURL:(NSURL *)url - cacheName:(NSString *)cacheName; -#endif - -/** - Stores image data into cache, associated with a given URL and cache name. - - @param imageData The image data to be stored in cache. - @param url The URL to be associated with the image. - @param cacheName The cache name to be associated with the image in the cache. This allows for multiple versions of an image to be associated for a single URL, such as image thumbnails, for instance. - */ -- (void)cacheImageData:(NSData *)imageData - forURL:(NSURL *)url - cacheName:(NSString *)cacheName; - -@end diff --git a/AFNetworking/AFImageCache.m b/AFNetworking/AFImageCache.m deleted file mode 100644 index cd50560..0000000 --- a/AFNetworking/AFImageCache.m +++ /dev/null @@ -1,83 +0,0 @@ -// AFImageCache.m -// -// Copyright (c) 2011 Gowalla (http://gowalla.com/) -// -// 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 "AFImageCache.h" - -static inline NSString * AFImageCacheKeyFromURLAndCacheName(NSURL *url, NSString *cacheName) { - return [[url absoluteString] stringByAppendingFormat:@"#%@", cacheName]; -} - -@implementation AFImageCache - -#if __IPHONE_OS_VERSION_MIN_REQUIRED -@synthesize imageScale = _imageScale; -#endif - -+ (AFImageCache *)sharedImageCache { - static AFImageCache *_sharedImageCache = nil; - static dispatch_once_t oncePredicate; - dispatch_once(&oncePredicate, ^{ - _sharedImageCache = [[self alloc] init]; - }); - - return _sharedImageCache; -} - -- (id)init { - self = [super init]; - if (!self) { - return nil; - } - -#if __IPHONE_OS_VERSION_MIN_REQUIRED - self.imageScale = [[UIScreen mainScreen] scale]; -#endif - - return self; -} - -#if __IPHONE_OS_VERSION_MIN_REQUIRED -- (UIImage *)cachedImageForURL:(NSURL *)url - cacheName:(NSString *)cacheName -{ - UIImage *image = [UIImage imageWithData:[self objectForKey:AFImageCacheKeyFromURLAndCacheName(url, cacheName)]]; - if (image) { - return [UIImage imageWithCGImage:[image CGImage] scale:self.imageScale orientation:image.imageOrientation]; - } - return image; -} -#elif __MAC_OS_X_VERSION_MIN_REQUIRED -- (NSImage *)cachedImageForURL:(NSURL *)url - cacheName:(NSString *)cacheName -{ - return [[[NSImage alloc] initWithData:[self objectForKey:AFImageCacheKeyFromURLAndCacheName(url, cacheName)]] autorelease]; -} -#endif - -- (void)cacheImageData:(NSData *)imageData - forURL:(NSURL *)url - cacheName:(NSString *)cacheName -{ - [self setObject:[NSPurgeableData dataWithData:imageData] forKey:AFImageCacheKeyFromURLAndCacheName(url, cacheName)]; -} - -@end diff --git a/AFNetworking/AFImageRequestOperation.m b/AFNetworking/AFImageRequestOperation.m index 1150b76..e004e9d 100644 --- a/AFNetworking/AFImageRequestOperation.m +++ b/AFNetworking/AFImageRequestOperation.m @@ -21,7 +21,6 @@ // THE SOFTWARE. #import "AFImageRequestOperation.h" -#import "AFImageCache.h" static dispatch_queue_t af_image_request_operation_processing_queue; static dispatch_queue_t image_request_operation_processing_queue() { @@ -157,7 +156,7 @@ static dispatch_queue_t image_request_operation_processing_queue() { #if __IPHONE_OS_VERSION_MIN_REQUIRED - (UIImage *)responseImage { - if (!_responseImage && [self isFinished]) { + if (!_responseImage && [self.responseData length] > 0 && [self isFinished]) { UIImage *image = [UIImage imageWithData:self.responseData]; self.responseImage = [UIImage imageWithCGImage:[image CGImage] scale:self.imageScale orientation:image.imageOrientation]; @@ -179,7 +178,7 @@ static dispatch_queue_t image_request_operation_processing_queue() { } #elif __MAC_OS_X_VERSION_MIN_REQUIRED - (NSImage *)responseImage { - if (!_responseImage && [self isFinished]) { + if (!_responseImage && [self.responseData length] > 0 && [self isFinished]) { // Ensure that the image is set to it's correct pixel width and height NSBitmapImageRep *bitimage = [[NSBitmapImageRep alloc] initWithData:self.responseData]; self.responseImage = [[[NSImage alloc] initWithSize:NSMakeSize([bitimage pixelsWide], [bitimage pixelsHigh])] autorelease]; diff --git a/AFNetworking/AFJSONRequestOperation.m b/AFNetworking/AFJSONRequestOperation.m index 294f724..5c651de 100644 --- a/AFNetworking/AFJSONRequestOperation.m +++ b/AFNetworking/AFJSONRequestOperation.m @@ -92,7 +92,7 @@ static dispatch_queue_t json_request_operation_processing_queue() { } - (id)responseJSON { - if (!_responseJSON && [self isFinished]) { + if (!_responseJSON && [self.responseData length] > 0 && [self isFinished]) { NSError *error = nil; if ([self.responseData length] == 0) { diff --git a/AFNetworking/AFJSONUtilities.h b/AFNetworking/AFJSONUtilities.h index 6ad1adb..ece26a0 100644 --- a/AFNetworking/AFJSONUtilities.h +++ b/AFNetworking/AFJSONUtilities.h @@ -22,148 +22,5 @@ #import -static NSData * AFJSONEncode(id object, NSError **error) { - NSData *data = nil; - - SEL _JSONKitSelector = NSSelectorFromString(@"JSONDataWithOptions:error:"); - SEL _SBJSONSelector = NSSelectorFromString(@"JSONRepresentation"); - SEL _YAJLSelector = NSSelectorFromString(@"yajl_JSONString"); - - id _NSJSONSerializationClass = NSClassFromString(@"NSJSONSerialization"); - SEL _NSJSONSerializationSelector = NSSelectorFromString(@"dataWithJSONObject:options:error:"); - -#ifdef _AFNETWORKING_PREFER_NSJSONSERIALIZATION_ - if (_NSJSONSerializationClass && [_NSJSONSerializationClass respondsToSelector:_NSJSONSerializationSelector]) { - goto _af_nsjson_encode; - } -#endif - - if (_JSONKitSelector && [object respondsToSelector:_JSONKitSelector]) { - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[object methodSignatureForSelector:_JSONKitSelector]]; - invocation.target = object; - invocation.selector = _JSONKitSelector; - - NSUInteger serializeOptionFlags = 0; - [invocation setArgument:&serializeOptionFlags atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation - [invocation setArgument:error atIndex:3]; - - [invocation invoke]; - [invocation getReturnValue:&data]; - } else if (_SBJSONSelector && [object respondsToSelector:_SBJSONSelector]) { - NSString *JSONString = nil; - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[object methodSignatureForSelector:_SBJSONSelector]]; - invocation.target = object; - invocation.selector = _SBJSONSelector; - - [invocation invoke]; - [invocation getReturnValue:&JSONString]; - - data = [JSONString dataUsingEncoding:NSUTF8StringEncoding]; - } else if (_YAJLSelector && [object respondsToSelector:_YAJLSelector]) { - @try { - NSString *JSONString = nil; - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[object methodSignatureForSelector:_YAJLSelector]]; - invocation.target = object; - invocation.selector = _YAJLSelector; - - [invocation invoke]; - [invocation getReturnValue:&JSONString]; - - data = [JSONString dataUsingEncoding:NSUTF8StringEncoding]; - } - @catch (NSException *exception) { - *error = [[[NSError alloc] initWithDomain:NSStringFromClass([exception class]) code:0 userInfo:[exception userInfo]] autorelease]; - } - } else if (_NSJSONSerializationClass && [_NSJSONSerializationClass respondsToSelector:_NSJSONSerializationSelector]) { -#ifdef _AFNETWORKING_PREFER_NSJSONSERIALIZATION_ - _af_nsjson_encode: -#endif - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[_NSJSONSerializationClass methodSignatureForSelector:_NSJSONSerializationSelector]]; - invocation.target = _NSJSONSerializationClass; - invocation.selector = _NSJSONSerializationSelector; - - [invocation setArgument:&object atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation - NSUInteger writeOptions = 0; - [invocation setArgument:&writeOptions atIndex:3]; - [invocation setArgument:error atIndex:4]; - - [invocation invoke]; - [invocation getReturnValue:&data]; - } else { - NSDictionary *userInfo = [NSDictionary dictionaryWithObject:NSLocalizedString(@"Please either target a platform that supports NSJSONSerialization or add one of the following libraries to your project: JSONKit, SBJSON, or YAJL", nil) forKey:NSLocalizedRecoverySuggestionErrorKey]; - [[NSException exceptionWithName:NSInternalInconsistencyException reason:NSLocalizedString(@"No JSON generation functionality available", nil) userInfo:userInfo] raise]; - } - - return data; -} - -static id AFJSONDecode(NSData *data, NSError **error) { - id JSON = nil; - - SEL _JSONKitSelector = NSSelectorFromString(@"objectFromJSONDataWithParseOptions:error:"); - SEL _SBJSONSelector = NSSelectorFromString(@"JSONValue"); - SEL _YAJLSelector = NSSelectorFromString(@"yajl_JSONWithOptions:error:"); - - id _NSJSONSerializationClass = NSClassFromString(@"NSJSONSerialization"); - SEL _NSJSONSerializationSelector = NSSelectorFromString(@"JSONObjectWithData:options:error:"); - - -#ifdef _AFNETWORKING_PREFER_NSJSONSERIALIZATION_ - if (_NSJSONSerializationClass && [_NSJSONSerializationClass respondsToSelector:_NSJSONSerializationSelector]) { - goto _af_nsjson_decode; - } -#endif - - if (_JSONKitSelector && [data respondsToSelector:_JSONKitSelector]) { - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[data methodSignatureForSelector:_JSONKitSelector]]; - invocation.target = data; - invocation.selector = _JSONKitSelector; - - NSUInteger parseOptionFlags = 0; - [invocation setArgument:&parseOptionFlags atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation - [invocation setArgument:error atIndex:3]; - - [invocation invoke]; - [invocation getReturnValue:&JSON]; - } else if (_SBJSONSelector && [NSString instancesRespondToSelector:_SBJSONSelector]) { - // Create a string representation of JSON, to use SBJSON -`JSONValue` category method - NSString *string = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[string methodSignatureForSelector:_SBJSONSelector]]; - invocation.target = string; - invocation.selector = _SBJSONSelector; - - [invocation invoke]; - [invocation getReturnValue:&JSON]; - } else if (_YAJLSelector && [data respondsToSelector:_YAJLSelector]) { - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[data methodSignatureForSelector:_YAJLSelector]]; - invocation.target = data; - invocation.selector = _YAJLSelector; - - NSUInteger yajlParserOptions = 0; - [invocation setArgument:&yajlParserOptions atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation - [invocation setArgument:error atIndex:3]; - - [invocation invoke]; - [invocation getReturnValue:&JSON]; - } else if (_NSJSONSerializationClass && [_NSJSONSerializationClass respondsToSelector:_NSJSONSerializationSelector]) { -#ifdef _AFNETWORKING_PREFER_NSJSONSERIALIZATION_ - _af_nsjson_decode: -#endif - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[_NSJSONSerializationClass methodSignatureForSelector:_NSJSONSerializationSelector]]; - invocation.target = _NSJSONSerializationClass; - invocation.selector = _NSJSONSerializationSelector; - - [invocation setArgument:&data atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation - NSUInteger readOptions = 0; - [invocation setArgument:&readOptions atIndex:3]; - [invocation setArgument:error atIndex:4]; - - [invocation invoke]; - [invocation getReturnValue:&JSON]; - } else { - NSDictionary *userInfo = [NSDictionary dictionaryWithObject:NSLocalizedString(@"Please either target a platform that supports NSJSONSerialization or add one of the following libraries to your project: JSONKit, SBJSON, or YAJL", nil) forKey:NSLocalizedRecoverySuggestionErrorKey]; - [[NSException exceptionWithName:NSInternalInconsistencyException reason:NSLocalizedString(@"No JSON parsing functionality available", nil) userInfo:userInfo] raise]; - } - - return JSON; -} +extern NSData * AFJSONEncode(id object, NSError **error); +extern id AFJSONDecode(NSData *data, NSError **error); diff --git a/AFNetworking/AFJSONUtilities.m b/AFNetworking/AFJSONUtilities.m new file mode 100644 index 0000000..3b6ff25 --- /dev/null +++ b/AFNetworking/AFJSONUtilities.m @@ -0,0 +1,169 @@ +// AFJSONUtilities.m +// +// Copyright (c) 2011 Gowalla (http://gowalla.com/) +// +// 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 "AFJSONUtilities.h" + +NSData * AFJSONEncode(id object, NSError **error) { + NSData *data = nil; + + SEL _JSONKitSelector = NSSelectorFromString(@"JSONDataWithOptions:error:"); + SEL _SBJSONSelector = NSSelectorFromString(@"JSONRepresentation"); + SEL _YAJLSelector = NSSelectorFromString(@"yajl_JSONString"); + + id _NSJSONSerializationClass = NSClassFromString(@"NSJSONSerialization"); + SEL _NSJSONSerializationSelector = NSSelectorFromString(@"dataWithJSONObject:options:error:"); + +#ifdef _AFNETWORKING_PREFER_NSJSONSERIALIZATION_ + if (_NSJSONSerializationClass && [_NSJSONSerializationClass respondsToSelector:_NSJSONSerializationSelector]) { + goto _af_nsjson_encode; + } +#endif + + if (_JSONKitSelector && [object respondsToSelector:_JSONKitSelector]) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[object methodSignatureForSelector:_JSONKitSelector]]; + invocation.target = object; + invocation.selector = _JSONKitSelector; + + NSUInteger serializeOptionFlags = 0; + [invocation setArgument:&serializeOptionFlags atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation + [invocation setArgument:error atIndex:3]; + + [invocation invoke]; + [invocation getReturnValue:&data]; + } else if (_SBJSONSelector && [object respondsToSelector:_SBJSONSelector]) { + NSString *JSONString = nil; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[object methodSignatureForSelector:_SBJSONSelector]]; + invocation.target = object; + invocation.selector = _SBJSONSelector; + + [invocation invoke]; + [invocation getReturnValue:&JSONString]; + + data = [JSONString dataUsingEncoding:NSUTF8StringEncoding]; + } else if (_YAJLSelector && [object respondsToSelector:_YAJLSelector]) { + @try { + NSString *JSONString = nil; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[object methodSignatureForSelector:_YAJLSelector]]; + invocation.target = object; + invocation.selector = _YAJLSelector; + + [invocation invoke]; + [invocation getReturnValue:&JSONString]; + + data = [JSONString dataUsingEncoding:NSUTF8StringEncoding]; + } + @catch (NSException *exception) { + *error = [[[NSError alloc] initWithDomain:NSStringFromClass([exception class]) code:0 userInfo:[exception userInfo]] autorelease]; + } + } else if (_NSJSONSerializationClass && [_NSJSONSerializationClass respondsToSelector:_NSJSONSerializationSelector]) { +#ifdef _AFNETWORKING_PREFER_NSJSONSERIALIZATION_ + _af_nsjson_encode: +#endif + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[_NSJSONSerializationClass methodSignatureForSelector:_NSJSONSerializationSelector]]; + invocation.target = _NSJSONSerializationClass; + invocation.selector = _NSJSONSerializationSelector; + + [invocation setArgument:&object atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation + NSUInteger writeOptions = 0; + [invocation setArgument:&writeOptions atIndex:3]; + [invocation setArgument:error atIndex:4]; + + [invocation invoke]; + [invocation getReturnValue:&data]; + } else { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:NSLocalizedString(@"Please either target a platform that supports NSJSONSerialization or add one of the following libraries to your project: JSONKit, SBJSON, or YAJL", nil) forKey:NSLocalizedRecoverySuggestionErrorKey]; + [[NSException exceptionWithName:NSInternalInconsistencyException reason:NSLocalizedString(@"No JSON generation functionality available", nil) userInfo:userInfo] raise]; + } + + return data; +} + +id AFJSONDecode(NSData *data, NSError **error) { + id JSON = nil; + + SEL _JSONKitSelector = NSSelectorFromString(@"objectFromJSONDataWithParseOptions:error:"); + SEL _SBJSONSelector = NSSelectorFromString(@"JSONValue"); + SEL _YAJLSelector = NSSelectorFromString(@"yajl_JSONWithOptions:error:"); + + id _NSJSONSerializationClass = NSClassFromString(@"NSJSONSerialization"); + SEL _NSJSONSerializationSelector = NSSelectorFromString(@"JSONObjectWithData:options:error:"); + + +#ifdef _AFNETWORKING_PREFER_NSJSONSERIALIZATION_ + if (_NSJSONSerializationClass && [_NSJSONSerializationClass respondsToSelector:_NSJSONSerializationSelector]) { + goto _af_nsjson_decode; + } +#endif + + if (_JSONKitSelector && [data respondsToSelector:_JSONKitSelector]) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[data methodSignatureForSelector:_JSONKitSelector]]; + invocation.target = data; + invocation.selector = _JSONKitSelector; + + NSUInteger parseOptionFlags = 0; + [invocation setArgument:&parseOptionFlags atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation + [invocation setArgument:error atIndex:3]; + + [invocation invoke]; + [invocation getReturnValue:&JSON]; + } else if (_SBJSONSelector && [NSString instancesRespondToSelector:_SBJSONSelector]) { + // Create a string representation of JSON, to use SBJSON -`JSONValue` category method + NSString *string = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[string methodSignatureForSelector:_SBJSONSelector]]; + invocation.target = string; + invocation.selector = _SBJSONSelector; + + [invocation invoke]; + [invocation getReturnValue:&JSON]; + } else if (_YAJLSelector && [data respondsToSelector:_YAJLSelector]) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[data methodSignatureForSelector:_YAJLSelector]]; + invocation.target = data; + invocation.selector = _YAJLSelector; + + NSUInteger yajlParserOptions = 0; + [invocation setArgument:&yajlParserOptions atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation + [invocation setArgument:error atIndex:3]; + + [invocation invoke]; + [invocation getReturnValue:&JSON]; + } else if (_NSJSONSerializationClass && [_NSJSONSerializationClass respondsToSelector:_NSJSONSerializationSelector]) { +#ifdef _AFNETWORKING_PREFER_NSJSONSERIALIZATION_ + _af_nsjson_decode: +#endif + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[_NSJSONSerializationClass methodSignatureForSelector:_NSJSONSerializationSelector]]; + invocation.target = _NSJSONSerializationClass; + invocation.selector = _NSJSONSerializationSelector; + + [invocation setArgument:&data atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation + NSUInteger readOptions = 0; + [invocation setArgument:&readOptions atIndex:3]; + [invocation setArgument:error atIndex:4]; + + [invocation invoke]; + [invocation getReturnValue:&JSON]; + } else { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:NSLocalizedString(@"Please either target a platform that supports NSJSONSerialization or add one of the following libraries to your project: JSONKit, SBJSON, or YAJL", nil) forKey:NSLocalizedRecoverySuggestionErrorKey]; + [[NSException exceptionWithName:NSInternalInconsistencyException reason:NSLocalizedString(@"No JSON parsing functionality available", nil) userInfo:userInfo] raise]; + } + + return JSON; +} diff --git a/AFNetworking/AFNetworking.h b/AFNetworking/AFNetworking.h index e25799d..6913289 100644 --- a/AFNetworking/AFNetworking.h +++ b/AFNetworking/AFNetworking.h @@ -35,7 +35,6 @@ #import "AFHTTPClient.h" #import "AFImageRequestOperation.h" -#import "AFImagecache.h" #if __IPHONE_OS_VERSION_MIN_REQUIRED #import "AFNetworkActivityIndicatorManager.h" diff --git a/AFNetworking/AFPropertyListRequestOperation.m b/AFNetworking/AFPropertyListRequestOperation.m index 7d84620..bb7a41b 100644 --- a/AFNetworking/AFPropertyListRequestOperation.m +++ b/AFNetworking/AFPropertyListRequestOperation.m @@ -92,7 +92,7 @@ static dispatch_queue_t property_list_request_operation_processing_queue() { } - (id)responsePropertyList { - if (!_responsePropertyList && [self isFinished]) { + if (!_responsePropertyList && [self.responseData length] > 0 && [self isFinished]) { NSPropertyListFormat format; NSError *error = nil; self.responsePropertyList = [NSPropertyListSerialization propertyListWithData:self.responseData options:self.propertyListReadOptions format:&format error:&error]; diff --git a/AFNetworking/AFURLConnectionOperation.h b/AFNetworking/AFURLConnectionOperation.h index abd2495..aba0e0b 100644 --- a/AFNetworking/AFURLConnectionOperation.h +++ b/AFNetworking/AFURLConnectionOperation.h @@ -75,6 +75,10 @@ extern NSString * const AFNetworkingOperationDidFinishNotification; */ @interface AFURLConnectionOperation : NSOperation { @private + unsigned short _state; + BOOL _cancelled; + NSRecursiveLock *_lock; + NSSet *_runLoopModes; NSURLConnection *_connection; diff --git a/AFNetworking/AFURLConnectionOperation.m b/AFNetworking/AFURLConnectionOperation.m index 5339da2..f9b7211 100644 --- a/AFNetworking/AFURLConnectionOperation.m +++ b/AFNetworking/AFURLConnectionOperation.m @@ -22,14 +22,18 @@ #import "AFURLConnectionOperation.h" -static NSUInteger const kAFHTTPMinimumInitialDataCapacity = 1024; -static NSUInteger const kAFHTTPMaximumInitialDataCapacity = 1024 * 1024 * 8; - typedef enum { AFHTTPOperationReadyState = 1, AFHTTPOperationExecutingState = 2, AFHTTPOperationFinishedState = 3, -} AFOperationState; +} _AFOperationState; + +typedef unsigned short AFOperationState; + +static NSUInteger const kAFHTTPMinimumInitialDataCapacity = 1024; +static NSUInteger const kAFHTTPMaximumInitialDataCapacity = 1024 * 1024 * 8; + +static NSString * const kAFNetworkingLockName = @"com.alamofire.networking.operation.lock"; NSString * const AFNetworkingErrorDomain = @"com.alamofire.networking.error"; @@ -52,9 +56,34 @@ static inline NSString * AFKeyPathFromOperationState(AFOperationState state) { } } +static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperationState toState, BOOL isCancelled) { + switch (fromState) { + case AFHTTPOperationReadyState: + switch (toState) { + case AFHTTPOperationExecutingState: + return YES; + case AFHTTPOperationFinishedState: + return isCancelled; + default: + return NO; + } + case AFHTTPOperationExecutingState: + switch (toState) { + case AFHTTPOperationFinishedState: + return YES; + default: + return NO; + } + case AFHTTPOperationFinishedState: + return NO; + default: + return YES; + } +} + @interface AFURLConnectionOperation () @property (readwrite, nonatomic, assign) AFOperationState state; -@property (readwrite, nonatomic, assign, getter = isCancelled) BOOL cancelled; +@property (readwrite, nonatomic, retain) NSRecursiveLock *lock; @property (readwrite, nonatomic, assign) NSURLConnection *connection; @property (readwrite, nonatomic, retain) NSURLRequest *request; @property (readwrite, nonatomic, retain) NSURLResponse *response; @@ -67,14 +96,12 @@ static inline NSString * AFKeyPathFromOperationState(AFOperationState state) { @property (readwrite, nonatomic, copy) AFURLConnectionOperationProgressBlock downloadProgress; @property (readwrite, nonatomic, copy) AFURLConnectionOperationAuthenticationChallengeBlock authenticationBlock; -- (BOOL)shouldTransitionToState:(AFOperationState)state; - (void)operationDidStart; - (void)finish; @end @implementation AFURLConnectionOperation @synthesize state = _state; -@synthesize cancelled = _cancelled; @synthesize connection = _connection; @synthesize runLoopModes = _runLoopModes; @synthesize request = _request; @@ -89,12 +116,22 @@ static inline NSString * AFKeyPathFromOperationState(AFOperationState state) { @synthesize uploadProgress = _uploadProgress; @synthesize downloadProgress = _downloadProgress; @synthesize authenticationBlock = _authenticationBlock; +@synthesize lock = _lock; + (void)networkRequestThreadEntryPoint:(id)__unused object { do { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - [[NSRunLoop currentRunLoop] run]; - [pool drain]; + NSAutoreleasePool *exceptionPool = [[NSAutoreleasePool alloc] init]; + NSException *caughtException = nil; + @try { + NSAutoreleasePool *runLoopPool = [[NSAutoreleasePool alloc] init]; + [[NSRunLoop currentRunLoop] run]; + [runLoopPool drain]; + } + @catch(NSException *e) { caughtException = e; } + if(caughtException) { + NSLog(NSLocalizedString(@"Unhandled exception on %@ networking thread: %@, userInfo: %@", nil), NSStringFromClass([self class]), caughtException, [caughtException userInfo]); + } + [exceptionPool drain]; } while (YES); } @@ -116,16 +153,21 @@ static inline NSString * AFKeyPathFromOperationState(AFOperationState state) { return nil; } + self.lock = [[[NSRecursiveLock alloc] init] autorelease]; + self.lock.name = kAFNetworkingLockName; + self.runLoopModes = [NSSet setWithObject:NSRunLoopCommonModes]; self.request = urlRequest; - + self.state = AFHTTPOperationReadyState; return self; } - (void)dealloc { + [_lock release]; + [_runLoopModes release]; [_request release]; @@ -135,7 +177,12 @@ static inline NSString * AFKeyPathFromOperationState(AFOperationState state) { [_responseData release]; [_responseString release]; [_dataAccumulator release]; - [_outputStream release]; _outputStream = nil; + + if (_outputStream) { + [_outputStream close]; + [_outputStream release]; + _outputStream = nil; + } [_uploadProgress release]; [_downloadProgress release]; @@ -144,7 +191,12 @@ static inline NSString * AFKeyPathFromOperationState(AFOperationState state) { [super dealloc]; } +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, state: %@, cancelled: %@ request: %@, response: %@>", NSStringFromClass([self class]), self, AFKeyPathFromOperationState(self.state), ([self isCancelled] ? @"YES" : @"NO"), self.request, self.response]; +} + - (void)setCompletionBlock:(void (^)(void))block { + [self.lock lock]; if (!block) { [super setCompletionBlock:nil]; } else { @@ -154,6 +206,7 @@ static inline NSString * AFKeyPathFromOperationState(AFOperationState state) { [_blockSelf setCompletionBlock:nil]; }]; } + [self.lock unlock]; } - (NSInputStream *)inputStream { @@ -179,65 +232,33 @@ static inline NSString * AFKeyPathFromOperationState(AFOperationState state) { } - (void)setState:(AFOperationState)state { - if (![self shouldTransitionToState:state]) { - return; - } - - NSString *oldStateKey = AFKeyPathFromOperationState(self.state); - NSString *newStateKey = AFKeyPathFromOperationState(state); - - [self willChangeValueForKey:newStateKey]; - [self willChangeValueForKey:oldStateKey]; - _state = state; - [self didChangeValueForKey:oldStateKey]; - [self didChangeValueForKey:newStateKey]; - - switch (state) { - case AFHTTPOperationExecutingState: - [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self]; - break; - case AFHTTPOperationFinishedState: - [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self]; - break; - default: - break; - } -} - -- (BOOL)shouldTransitionToState:(AFOperationState)state { - switch (self.state) { - case AFHTTPOperationReadyState: - switch (state) { - case AFHTTPOperationExecutingState: - return YES; - default: - return NO; - } - case AFHTTPOperationExecutingState: - switch (state) { - case AFHTTPOperationFinishedState: - return YES; - default: - return NO; - } - case AFHTTPOperationFinishedState: - return NO; - default: - return YES; - } -} - -- (void)setCancelled:(BOOL)cancelled { - [self willChangeValueForKey:@"isCancelled"]; - _cancelled = cancelled; - [self didChangeValueForKey:@"isCancelled"]; - - if ([self isCancelled]) { - self.state = AFHTTPOperationFinishedState; + [self.lock lock]; + if (AFStateTransitionIsValid(self.state, state, [self isCancelled])) { + NSString *oldStateKey = AFKeyPathFromOperationState(self.state); + NSString *newStateKey = AFKeyPathFromOperationState(state); + + [self willChangeValueForKey:newStateKey]; + [self willChangeValueForKey:oldStateKey]; + _state = state; + [self didChangeValueForKey:oldStateKey]; + [self didChangeValueForKey:newStateKey]; + + switch (state) { + case AFHTTPOperationExecutingState: + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self]; + break; + case AFHTTPOperationFinishedState: + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self]; + break; + default: + break; + } } + [self.lock unlock]; } - (NSString *)responseString { + [self.lock lock]; if (!_responseString && self.response && self.responseData) { NSStringEncoding textEncoding = NSUTF8StringEncoding; if (self.response.textEncodingName) { @@ -246,6 +267,7 @@ static inline NSString * AFKeyPathFromOperationState(AFOperationState state) { self.responseString = [[[NSString alloc] initWithData:self.responseData encoding:textEncoding] autorelease]; } + [self.lock unlock]; return _responseString; } @@ -264,35 +286,40 @@ static inline NSString * AFKeyPathFromOperationState(AFOperationState state) { return self.state == AFHTTPOperationFinishedState; } +- (BOOL)isCancelled { + return _cancelled; +} + - (BOOL)isConcurrent { return YES; } -- (void)start { - if (![self isReady]) { - return; +- (void)start { + [self.lock lock]; + if ([self isReady]) { + self.state = AFHTTPOperationExecutingState; + + [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; } - - self.state = AFHTTPOperationExecutingState; - - [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:YES modes:[self.runLoopModes allObjects]]; + [self.lock unlock]; } - (void)operationDidStart { + [self.lock lock]; if ([self isCancelled]) { [self finish]; - return; + } else { + self.connection = [[[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO] autorelease]; + + NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; + for (NSString *runLoopMode in self.runLoopModes) { + [self.connection scheduleInRunLoop:runLoop forMode:runLoopMode]; + [self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode]; + } + + [self.connection start]; } - - self.connection = [[[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO] autorelease]; - - NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; - for (NSString *runLoopMode in self.runLoopModes) { - [self.connection scheduleInRunLoop:runLoop forMode:runLoopMode]; - [self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode]; - } - - [self.connection start]; + [self.lock unlock]; } - (void)finish { @@ -300,15 +327,22 @@ static inline NSString * AFKeyPathFromOperationState(AFOperationState state) { } - (void)cancel { - if ([self isFinished]) { - return; + [self.lock lock]; + if (![self isFinished] && ![self isCancelled]) { + [super cancel]; + + [self willChangeValueForKey:@"isCancelled"]; + _cancelled = YES; + if (self.connection) { + [self.connection cancel]; + + // We must send this delegate protcol message ourselves since the above [self.connection cancel] causes the connection to never send another message to its delegate. + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[self.request URL] forKey:NSURLErrorFailingURLErrorKey]; + [self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:userInfo]]; + } + [self didChangeValueForKey:@"isCancelled"]; } - - [super cancel]; - - self.cancelled = YES; - - [self.connection cancel]; + [self.lock unlock]; } #pragma mark - NSURLConnectionDelegate @@ -400,7 +434,7 @@ didReceiveResponse:(NSURLResponse *)response - (void)connection:(NSURLConnection *)__unused connection didFailWithError:(NSError *)error -{ +{ self.error = error; if (self.outputStream) { diff --git a/AFNetworking/AFXMLRequestOperation.m b/AFNetworking/AFXMLRequestOperation.m index 48a6783..b6b3a2b 100644 --- a/AFNetworking/AFXMLRequestOperation.m +++ b/AFNetworking/AFXMLRequestOperation.m @@ -139,7 +139,7 @@ static dispatch_queue_t xml_request_operation_processing_queue() { } - (NSXMLParser *)responseXMLParser { - if (!_responseXMLParser && [self isFinished]) { + if (!_responseXMLParser && [self.responseData length] > 0 && [self isFinished]) { self.responseXMLParser = [[[NSXMLParser alloc] initWithData:self.responseData] autorelease]; } @@ -148,7 +148,7 @@ static dispatch_queue_t xml_request_operation_processing_queue() { #if __MAC_OS_X_VERSION_MIN_REQUIRED - (NSXMLDocument *)responseXMLDocument { - if (!_responseXMLDocument && [self isFinished]) { + if (!_responseXMLDocument && [self.responseData length] > 0 && [self isFinished]) { NSError *error = nil; self.responseXMLDocument = [[[NSXMLDocument alloc] initWithData:self.responseData options:0 error:&error] autorelease]; self.error = error; diff --git a/AFNetworking/UIImageView+AFNetworking.m b/AFNetworking/UIImageView+AFNetworking.m index 820ccd4..838ab20 100644 --- a/AFNetworking/UIImageView+AFNetworking.m +++ b/AFNetworking/UIImageView+AFNetworking.m @@ -24,10 +24,20 @@ #import #if __IPHONE_OS_VERSION_MIN_REQUIRED - #import "UIImageView+AFNetworking.h" -#import "AFImageCache.h" +@interface AFImageCache : NSCache +@property (nonatomic, assign) CGFloat imageScale; + +- (UIImage *)cachedImageForURL:(NSURL *)url + cacheName:(NSString *)cacheName; + +- (void)cacheImageData:(NSData *)imageData + forURL:(NSURL *)url + cacheName:(NSString *)cacheName; +@end + +#pragma mark - static char kAFImageRequestOperationObjectKey; @@ -52,14 +62,24 @@ static char kAFImageRequestOperationObjectKey; } + (NSOperationQueue *)af_sharedImageRequestOperationQueue { - static NSOperationQueue *_imageRequestOperationQueue = nil; + static NSOperationQueue *_af_imageRequestOperationQueue = nil; - if (!_imageRequestOperationQueue) { - _imageRequestOperationQueue = [[NSOperationQueue alloc] init]; - [_imageRequestOperationQueue setMaxConcurrentOperationCount:8]; + if (!_af_imageRequestOperationQueue) { + _af_imageRequestOperationQueue = [[NSOperationQueue alloc] init]; + [_af_imageRequestOperationQueue setMaxConcurrentOperationCount:8]; } - return _imageRequestOperationQueue; + return _af_imageRequestOperationQueue; +} + ++ (AFImageCache *)af_sharedImageCache { + static AFImageCache *_af_imageCache = nil; + static dispatch_once_t oncePredicate; + dispatch_once(&oncePredicate, ^{ + _af_imageCache = [[AFImageCache alloc] init]; + }); + + return _af_imageCache; } #pragma mark - @@ -89,7 +109,7 @@ static char kAFImageRequestOperationObjectKey; [self cancelImageRequestOperation]; } - UIImage *cachedImage = [[AFImageCache sharedImageCache] cachedImageForURL:[urlRequest URL] cacheName:nil]; + UIImage *cachedImage = [[[self class] af_sharedImageCache] cachedImageForURL:[urlRequest URL] cacheName:nil]; if (cachedImage) { self.image = cachedImage; self.af_imageRequestOperation = nil; @@ -104,15 +124,13 @@ static char kAFImageRequestOperationObjectKey; [requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { if ([[urlRequest URL] isEqual:[[self.af_imageRequestOperation request] URL]]) { self.image = responseObject; - } else { - self.image = placeholderImage; } if (success) { success(operation.request, operation.response, responseObject); } - [[AFImageCache sharedImageCache] cacheImageData:operation.responseData forURL:[urlRequest URL] cacheName:nil]; + [[[self class] af_sharedImageCache] cacheImageData:operation.responseData forURL:[urlRequest URL] cacheName:nil]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { if (failure) { failure(operation.request, operation.response, error); @@ -131,4 +149,43 @@ static char kAFImageRequestOperationObjectKey; @end +#pragma mark - + +static inline NSString * AFImageCacheKeyFromURLAndCacheName(NSURL *url, NSString *cacheName) { + return [[url absoluteString] stringByAppendingFormat:@"#%@", cacheName]; +} + +@implementation AFImageCache +@synthesize imageScale = _imageScale; + +- (id)init { + self = [super init]; + if (!self) { + return nil; + } + + self.imageScale = [[UIScreen mainScreen] scale]; + + return self; +} + +- (UIImage *)cachedImageForURL:(NSURL *)url + cacheName:(NSString *)cacheName +{ + UIImage *image = [UIImage imageWithData:[self objectForKey:AFImageCacheKeyFromURLAndCacheName(url, cacheName)]]; + if (image) { + return [UIImage imageWithCGImage:[image CGImage] scale:self.imageScale orientation:image.imageOrientation]; + } + return image; +} + +- (void)cacheImageData:(NSData *)imageData + forURL:(NSURL *)url + cacheName:(NSString *)cacheName +{ + [self setObject:[NSPurgeableData dataWithData:imageData] forKey:AFImageCacheKeyFromURLAndCacheName(url, cacheName)]; +} + +@end + #endif diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..7cd582c --- /dev/null +++ b/CHANGES @@ -0,0 +1,57 @@ += 0.9.0 / 2012-01-23 + + * Add thread-safe behavior to `AFURLConnectionOperation` (Mattt Thompson) + + * Add batching of operations for `AFHTTPClient` (Mattt Thompson) + + * Add authentication challenge callback block to override default implementation of `connection:didReceiveAuthenticationChallenge:` in `AFURLConnectionOperation` (Mattt Thompson) + + * Add `_AFNETWORKING_PREFER_NSJSONSERIALIZATION_`, which, when defined, short-circuits the standard preference ordering used in `AFJSONEncode` and `AFJSONDecode` to use `NSJSONSerialization` when available, falling back on third-party-libraries. (Mattt Thompson, Shane Vitarana) + + * Add custom `description` for `AFURLConnectionOperation` and `AFHTTPClient` (Mattt Thompson) + + * Add `text/javascript` to default acceptable content types for `AFJSONRequestOperation` (Jake Boxer) + + * Add `imageScale` property to change resolution of images constructed from cached data (Štěpán Petrů) + + * Add note about third party JSON libraries in README (David Keegan) + + * `AFQueryStringFromParametersWithEncoding` formats `NSArray` values in the form `key[]=value1&key[]=value2` instead of `key=(value1,value2)` (Dan Thorpe) + + * `AFImageRequestOperation -responseImage` on OS X uses `NSBitmapImageRep` to determine the correct pixel dimensions of the image (David Keegan) + + * `AFURLConnectionOperation` `connection` has memory management policy `assign` to avoid retain cycles caused by `NSURLConnection` retaining its delegate (Mattt Thompson) + + * `AFURLConnectionOperation` calls super implementation for `-isReady`, following the guidelines for `NSOperation` subclasses (Mattt Thompson) + + * `UIImageView -setImageWithURL:` and related methods call success callback after setting image (Cameron Boehmer) + + * Cancel request if an authentication challenge has no suitable credentials in `AFURLConnectionOperation -connection:didReceiveAuthenticationChallenge:` (Jorge Bernal) + + * Remove exception from `multipartFormRequestWithMethod:path:parameters:constructingBodyWithBlock:` raised when certain HTTP methods are used. (Mattt Thompson) + + * Remove `AFImageCache` from public API, moving it into private implementation of `UIImageView+AFNetworking` (Mattt Thompson) + + * Mac example application makes better use of AppKit technologies and conventions (Mattt Thompson) + + * Fix issue with multipart form boundaries in `AFHTTPClient -multipartFormRequestWithMethod:path:parameters:constructingBodyWithBlock:` (Ray Morgan, Mattt Thompson, Sam Soffes) + + * Fix "File Upload with Progress Callback" code snippet in README (Larry Legend) + + * Fix to SBJSON invocations in `AFJSONEncode` and `AFJSONDecode` (Matthias Tretter, James Frye) + + * Fix documentation for `AFHTTPClient requestWithMethod:path:parameters:` (Michael Parker) + + * Fix `Content-Disposition` headers used for multipart form construction (Michael Parker) + + * Add network reachability status change callback property to `AFHTTPClient`. (Mattt Thompson, Kevin Harwood) + + * Fix exception handling in `AFJSONEncode` and `AFJSONDecode` (David Keegan) + + * Fix `NSData` initialization with string in `AFBase64EncodedStringFromString` (Adam Ernst, Mattt Thompson) + + * Fix error check in `appendPartWithFileURL:name:error:` (Warren Moore, Baldoph, Mattt Thompson) + + * Fix compiler warnings for certain configurations (Charlie Williams) + + * Fix bug caused by passing zero-length `responseData` to response object initializers (Mattt Thompson, Serge Paquet) diff --git a/Mac Example/AFNetworking Mac Example.xcodeproj/project.pbxproj b/Mac Example/AFNetworking Mac Example.xcodeproj/project.pbxproj index bd4688d..3d5d956 100644 --- a/Mac Example/AFNetworking Mac Example.xcodeproj/project.pbxproj +++ b/Mac Example/AFNetworking Mac Example.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + F8689A9C14CC9F780087F357 /* AFJSONUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = F8689A9B14CC9F780087F357 /* AFJSONUtilities.m */; }; F87A159F1444926300318955 /* AFURLConnectionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F87A159E1444926300318955 /* AFURLConnectionOperation.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; F87A15A21444926A00318955 /* AFXMLRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F87A15A11444926900318955 /* AFXMLRequestOperation.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; F87A15A51444927300318955 /* AFPropertyListRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F87A15A41444927300318955 /* AFPropertyListRequestOperation.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; @@ -17,7 +18,6 @@ F87A15DD1444A86600318955 /* placeholder-stamp.png in Resources */ = {isa = PBXBuildFile; fileRef = F87A15DB1444A86600318955 /* placeholder-stamp.png */; }; F897DE78142CFEDC000DDD35 /* AFHTTPClient.m in Sources */ = {isa = PBXBuildFile; fileRef = F897DE6A142CFEDC000DDD35 /* AFHTTPClient.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; F897DE79142CFEDC000DDD35 /* AFHTTPRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F897DE6C142CFEDC000DDD35 /* AFHTTPRequestOperation.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - F897DE7A142CFEDC000DDD35 /* AFImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = F897DE6E142CFEDC000DDD35 /* AFImageCache.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; F897DE7B142CFEDC000DDD35 /* AFImageRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F897DE70142CFEDC000DDD35 /* AFImageRequestOperation.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; F897DE7C142CFEDC000DDD35 /* AFJSONRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F897DE72142CFEDC000DDD35 /* AFJSONRequestOperation.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; F8A27AC7142CFE1300F5E0D6 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F8A27AB2142CFE1300F5E0D6 /* AppDelegate.m */; }; @@ -30,6 +30,7 @@ /* Begin PBXFileReference section */ F8323C901455B4FE00190CCB /* AFJSONUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFJSONUtilities.h; sourceTree = ""; }; + F8689A9B14CC9F780087F357 /* AFJSONUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFJSONUtilities.m; sourceTree = ""; }; F87A159D1444926300318955 /* AFURLConnectionOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFURLConnectionOperation.h; sourceTree = ""; }; F87A159E1444926300318955 /* AFURLConnectionOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFURLConnectionOperation.m; sourceTree = ""; }; F87A15A01444926900318955 /* AFXMLRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFXMLRequestOperation.h; sourceTree = ""; }; @@ -48,8 +49,6 @@ F897DE6A142CFEDC000DDD35 /* AFHTTPClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFHTTPClient.m; sourceTree = ""; }; F897DE6B142CFEDC000DDD35 /* AFHTTPRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFHTTPRequestOperation.h; sourceTree = ""; }; F897DE6C142CFEDC000DDD35 /* AFHTTPRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFHTTPRequestOperation.m; sourceTree = ""; }; - F897DE6D142CFEDC000DDD35 /* AFImageCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFImageCache.h; sourceTree = ""; }; - F897DE6E142CFEDC000DDD35 /* AFImageCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFImageCache.m; sourceTree = ""; }; F897DE6F142CFEDC000DDD35 /* AFImageRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFImageRequestOperation.h; sourceTree = ""; }; F897DE70142CFEDC000DDD35 /* AFImageRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFImageRequestOperation.m; sourceTree = ""; }; F897DE71142CFEDC000DDD35 /* AFJSONRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFJSONRequestOperation.h; sourceTree = ""; }; @@ -139,9 +138,8 @@ F897DE6A142CFEDC000DDD35 /* AFHTTPClient.m */, F897DE6F142CFEDC000DDD35 /* AFImageRequestOperation.h */, F897DE70142CFEDC000DDD35 /* AFImageRequestOperation.m */, - F897DE6D142CFEDC000DDD35 /* AFImageCache.h */, - F897DE6E142CFEDC000DDD35 /* AFImageCache.m */, F8323C901455B4FE00190CCB /* AFJSONUtilities.h */, + F8689A9B14CC9F780087F357 /* AFJSONUtilities.m */, ); name = AFNetworking; path = ../../AFNetworking; @@ -288,7 +286,6 @@ F8A27ACB142CFE1300F5E0D6 /* JSONKit.m in Sources */, F897DE78142CFEDC000DDD35 /* AFHTTPClient.m in Sources */, F897DE79142CFEDC000DDD35 /* AFHTTPRequestOperation.m in Sources */, - F897DE7A142CFEDC000DDD35 /* AFImageCache.m in Sources */, F897DE7B142CFEDC000DDD35 /* AFImageRequestOperation.m in Sources */, F897DE7C142CFEDC000DDD35 /* AFJSONRequestOperation.m in Sources */, F87A159F1444926300318955 /* AFURLConnectionOperation.m in Sources */, @@ -297,6 +294,7 @@ F87A15CD1444A30800318955 /* AFGowallaAPIClient.m in Sources */, F87A15CE1444A30800318955 /* NearbySpotsController.m in Sources */, F87A15CF1444A30800318955 /* Spot.m in Sources */, + F8689A9C14CC9F780087F357 /* AFJSONUtilities.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Mac Example/en.lproj/MainMenu.xib b/Mac Example/en.lproj/MainMenu.xib index bddf394..7e71ce6 100644 --- a/Mac Example/en.lproj/MainMenu.xib +++ b/Mac Example/en.lproj/MainMenu.xib @@ -251,7 +251,7 @@ 15 2 - {{50, 538}, {375, 500}} + {{60, 295}, {375, 500}} 1954021376 AFNetworking Mac Example NSWindow @@ -439,7 +439,6 @@ {{1, 526}, {374, 15}} - _NS:1216 1 diff --git a/iOS Example/AFNetworking iOS Example.xcodeproj/project.pbxproj b/iOS Example/AFNetworking iOS Example.xcodeproj/project.pbxproj index f8ed529..fc0af51 100644 --- a/iOS Example/AFNetworking iOS Example.xcodeproj/project.pbxproj +++ b/iOS Example/AFNetworking iOS Example.xcodeproj/project.pbxproj @@ -7,9 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + F8689A9914CC9F5E0087F357 /* AFJSONUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = F8689A9814CC9F5E0087F357 /* AFJSONUtilities.m */; }; F86E5529143A28F3002B438C /* AFURLConnectionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F86E5528143A28F3002B438C /* AFURLConnectionOperation.m */; }; F874B5D913E0AA6500B28E3E /* AFHTTPRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F874B5C913E0AA6500B28E3E /* AFHTTPRequestOperation.m */; }; - F874B5DA13E0AA6500B28E3E /* AFImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = F874B5CA13E0AA6500B28E3E /* AFImageCache.m */; }; F874B5DB13E0AA6500B28E3E /* AFImageRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F874B5CB13E0AA6500B28E3E /* AFImageRequestOperation.m */; }; F874B5DC13E0AA6500B28E3E /* AFJSONRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F874B5CC13E0AA6500B28E3E /* AFJSONRequestOperation.m */; }; F874B5DD13E0AA6500B28E3E /* AFNetworkActivityIndicatorManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F874B5CD13E0AA6500B28E3E /* AFNetworkActivityIndicatorManager.m */; }; @@ -41,16 +41,15 @@ /* Begin PBXFileReference section */ F8323C941455C03400190CCB /* AFJSONUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFJSONUtilities.h; path = ../AFNetworking/AFJSONUtilities.h; sourceTree = ""; }; + F8689A9814CC9F5E0087F357 /* AFJSONUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFJSONUtilities.m; path = ../AFNetworking/AFJSONUtilities.m; sourceTree = ""; }; F86E5527143A28F3002B438C /* AFURLConnectionOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFURLConnectionOperation.h; path = ../AFNetworking/AFURLConnectionOperation.h; sourceTree = ""; }; F86E5528143A28F3002B438C /* AFURLConnectionOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFURLConnectionOperation.m; path = ../AFNetworking/AFURLConnectionOperation.m; sourceTree = ""; }; F874B5C913E0AA6500B28E3E /* AFHTTPRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFHTTPRequestOperation.m; path = ../AFNetworking/AFHTTPRequestOperation.m; sourceTree = ""; }; - F874B5CA13E0AA6500B28E3E /* AFImageCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFImageCache.m; path = ../AFNetworking/AFImageCache.m; sourceTree = ""; }; F874B5CB13E0AA6500B28E3E /* AFImageRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFImageRequestOperation.m; path = ../AFNetworking/AFImageRequestOperation.m; sourceTree = ""; }; F874B5CC13E0AA6500B28E3E /* AFJSONRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFJSONRequestOperation.m; path = ../AFNetworking/AFJSONRequestOperation.m; sourceTree = ""; }; F874B5CD13E0AA6500B28E3E /* AFNetworkActivityIndicatorManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFNetworkActivityIndicatorManager.m; path = ../AFNetworking/AFNetworkActivityIndicatorManager.m; sourceTree = ""; }; F874B5D013E0AA6500B28E3E /* UIImageView+AFNetworking.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIImageView+AFNetworking.m"; path = "../AFNetworking/UIImageView+AFNetworking.m"; sourceTree = ""; }; F874B5D113E0AA6500B28E3E /* AFHTTPRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFHTTPRequestOperation.h; path = ../AFNetworking/AFHTTPRequestOperation.h; sourceTree = ""; }; - F874B5D213E0AA6500B28E3E /* AFImageCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFImageCache.h; path = ../AFNetworking/AFImageCache.h; sourceTree = ""; }; F874B5D313E0AA6500B28E3E /* AFImageRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFImageRequestOperation.h; path = ../AFNetworking/AFImageRequestOperation.h; sourceTree = ""; }; F874B5D413E0AA6500B28E3E /* AFJSONRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFJSONRequestOperation.h; path = ../AFNetworking/AFJSONRequestOperation.h; sourceTree = ""; }; F874B5D513E0AA6500B28E3E /* AFNetworkActivityIndicatorManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFNetworkActivityIndicatorManager.h; path = ../AFNetworking/AFNetworkActivityIndicatorManager.h; sourceTree = ""; }; @@ -246,13 +245,12 @@ F8FBFA97142AA238001409DB /* AFHTTPClient.m */, F874B5D313E0AA6500B28E3E /* AFImageRequestOperation.h */, F874B5CB13E0AA6500B28E3E /* AFImageRequestOperation.m */, - F874B5D213E0AA6500B28E3E /* AFImageCache.h */, - F874B5CA13E0AA6500B28E3E /* AFImageCache.m */, F874B5D813E0AA6500B28E3E /* UIImageView+AFNetworking.h */, F874B5D013E0AA6500B28E3E /* UIImageView+AFNetworking.m */, F874B5D513E0AA6500B28E3E /* AFNetworkActivityIndicatorManager.h */, F874B5CD13E0AA6500B28E3E /* AFNetworkActivityIndicatorManager.m */, F8323C941455C03400190CCB /* AFJSONUtilities.h */, + F8689A9814CC9F5E0087F357 /* AFJSONUtilities.m */, ); name = AFNetworking; sourceTree = ""; @@ -356,7 +354,6 @@ F8DA09E41396AC040057D0CC /* main.m in Sources */, F8DA09E81396AC220057D0CC /* AppDelegate.m in Sources */, F874B5D913E0AA6500B28E3E /* AFHTTPRequestOperation.m in Sources */, - F874B5DA13E0AA6500B28E3E /* AFImageCache.m in Sources */, F874B5DB13E0AA6500B28E3E /* AFImageRequestOperation.m in Sources */, F874B5DC13E0AA6500B28E3E /* AFJSONRequestOperation.m in Sources */, F874B5DD13E0AA6500B28E3E /* AFNetworkActivityIndicatorManager.m in Sources */, @@ -365,6 +362,7 @@ F86E5529143A28F3002B438C /* AFURLConnectionOperation.m in Sources */, F8F4B16E143CD1420064C9E6 /* AFPropertyListRequestOperation.m in Sources */, F8F4B17F143E07030064C9E6 /* AFXMLRequestOperation.m in Sources */, + F8689A9914CC9F5E0087F357 /* AFJSONUtilities.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/iOS Example/Classes/Controllers/NearbySpotsViewController.m b/iOS Example/Classes/Controllers/NearbySpotsViewController.m index c21c654..7021115 100644 --- a/iOS Example/Classes/Controllers/NearbySpotsViewController.m +++ b/iOS Example/Classes/Controllers/NearbySpotsViewController.m @@ -27,7 +27,6 @@ #import "SpotTableViewCell.h" #import "TTTLocationFormatter.h" -#import "AFImageCache.h" #import "UIImageView+AFNetworking.h" @interface NearbySpotsViewController () @@ -123,7 +122,6 @@ self.nearbySpots = [NSArray array]; [self.tableView reloadData]; [[NSURLCache sharedURLCache] removeAllCachedResponses]; - [[AFImageCache sharedImageCache] removeAllObjects]; if (self.locationManager.location) { [self loadSpotsForLocation:self.locationManager.location]; diff --git a/iOS Example/Prefix.pch b/iOS Example/Prefix.pch index 47d76c1..0cb5c10 100644 --- a/iOS Example/Prefix.pch +++ b/iOS Example/Prefix.pch @@ -7,4 +7,5 @@ #ifdef __OBJC__ #import #import + #import #endif