Porting over changes for 0.3.0 release
This commit is contained in:
parent
2e32312d17
commit
d87b783814
33 changed files with 12164 additions and 10252 deletions
|
|
@ -1,62 +0,0 @@
|
||||||
// AFHTTPOperation.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 <Foundation/Foundation.h>
|
|
||||||
#import "QHTTPOperation.h"
|
|
||||||
#import "AFCallback.h"
|
|
||||||
|
|
||||||
extern NSString * const AFHTTPOperationDidStartNotification;
|
|
||||||
extern NSString * const AFHTTPOperationDidSucceedNotification;
|
|
||||||
extern NSString * const AFHTTPOperationDidFailNotification;
|
|
||||||
|
|
||||||
extern NSString * const AFHTTPOperationParsedDataErrorKey;
|
|
||||||
|
|
||||||
@class AFHTTPOperationCallback;
|
|
||||||
|
|
||||||
@interface AFHTTPOperation : QHTTPOperation {
|
|
||||||
@private
|
|
||||||
AFHTTPOperationCallback *_callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property (nonatomic, retain) AFHTTPOperationCallback *callback;
|
|
||||||
@property (readonly) NSString *responseString;
|
|
||||||
|
|
||||||
+ (id)operationWithRequest:(NSURLRequest *)urlRequest callback:(AFHTTPOperationCallback *)callback;
|
|
||||||
- (id)initWithRequest:(NSURLRequest *)urlRequest callback:(AFHTTPOperationCallback *)callback;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#pragma mark - AFHTTPOperationCallback
|
|
||||||
|
|
||||||
typedef void (^AFHTTPOperationSuccessBlock)(NSURLRequest *request, NSHTTPURLResponse *response, NSDictionary *data);
|
|
||||||
typedef void (^AFHTTPOperationErrorBlock)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error);
|
|
||||||
|
|
||||||
@protocol AFHTTPOperationCallback <NSObject>
|
|
||||||
@optional
|
|
||||||
+ (id)callbackWithSuccess:(AFHTTPOperationSuccessBlock)success;
|
|
||||||
+ (id)callbackWithSuccess:(AFHTTPOperationSuccessBlock)success error:(AFHTTPOperationErrorBlock)error;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface AFHTTPOperationCallback : AFCallback <AFHTTPOperationCallback>
|
|
||||||
@property (readwrite, nonatomic, copy) AFHTTPOperationSuccessBlock successBlock;
|
|
||||||
@property (readwrite, nonatomic, copy) AFHTTPOperationErrorBlock errorBlock;
|
|
||||||
@end
|
|
||||||
|
|
@ -1,106 +0,0 @@
|
||||||
// AFHTTPOperation.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 "AFHTTPOperation.h"
|
|
||||||
#import "JSONKit.h"
|
|
||||||
|
|
||||||
NSString * const AFHTTPOperationDidStartNotification = @"com.alamofire.http-operation.start";
|
|
||||||
NSString * const AFHTTPOperationDidSucceedNotification = @"com.alamofire.http-operation.success";
|
|
||||||
NSString * const AFHTTPOperationDidFailNotification = @"com.alamofire.http-operation.failure";
|
|
||||||
|
|
||||||
NSString * const AFHTTPOperationParsedDataErrorKey = @"com.alamofire.http-operation.error.parsed-data";
|
|
||||||
|
|
||||||
@implementation AFHTTPOperation
|
|
||||||
@synthesize callback = _callback;
|
|
||||||
|
|
||||||
+ (id)operationWithRequest:(NSURLRequest *)urlRequest callback:(AFHTTPOperationCallback *)callback {
|
|
||||||
return [[[self alloc] initWithRequest:urlRequest callback:callback] autorelease];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithRequest:(NSURLRequest *)urlRequest callback:(AFHTTPOperationCallback *)callback {
|
|
||||||
self = [super initWithRequest:urlRequest];
|
|
||||||
if (!self) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/plain", nil];
|
|
||||||
self.callback = callback;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc {
|
|
||||||
[_callback release];
|
|
||||||
[super dealloc];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)responseString {
|
|
||||||
return [[[NSString alloc] initWithData:self.responseBody encoding:NSUTF8StringEncoding] autorelease];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - QRunLoopOperation
|
|
||||||
|
|
||||||
- (void)operationDidStart {
|
|
||||||
[super operationDidStart];
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:AFHTTPOperationDidStartNotification object:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)finishWithError:(NSError *)error {
|
|
||||||
[super finishWithError:error];
|
|
||||||
|
|
||||||
NSDictionary *data = nil;
|
|
||||||
if (self.contentTypeAcceptable) {
|
|
||||||
if ([[self.lastResponse MIMEType] isEqualToString:@"application/json"]) {
|
|
||||||
NSError *jsonError = nil;
|
|
||||||
data = [[JSONDecoder decoder] parseJSONData:self.responseBody error:&jsonError];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.statusCodeAcceptable) {
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:AFHTTPOperationDidSucceedNotification object:self];
|
|
||||||
|
|
||||||
if(self.callback.successBlock) {
|
|
||||||
self.callback.successBlock(self.lastRequest, self.lastResponse, data);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:AFHTTPOperationDidFailNotification object:self];
|
|
||||||
|
|
||||||
NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:[error userInfo]];
|
|
||||||
[userInfo setValue:[NSHTTPURLResponse localizedStringForStatusCode:[self.lastResponse statusCode]] forKey:NSLocalizedDescriptionKey];
|
|
||||||
[userInfo setValue:[[self.lastRequest URL] absoluteString] forKey:NSURLErrorFailingURLStringErrorKey];
|
|
||||||
[userInfo setValue:data forKey:AFHTTPOperationParsedDataErrorKey];
|
|
||||||
|
|
||||||
error = [[[NSError alloc] initWithDomain:NSURLErrorDomain code:[self.lastResponse statusCode] userInfo:userInfo] autorelease];
|
|
||||||
|
|
||||||
if (self.callback.errorBlock) {
|
|
||||||
self.callback.errorBlock(self.lastRequest, self.lastResponse, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#pragma mark - AFHTTPOperationCallback
|
|
||||||
|
|
||||||
@implementation AFHTTPOperationCallback
|
|
||||||
@dynamic successBlock, errorBlock;
|
|
||||||
@end
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
// AFImageRequestOperation.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 <Foundation/Foundation.h>
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
#import "QHTTPOperation.h"
|
|
||||||
#import "AFCallback.h"
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
AFImageRequestResize = 1 << 1,
|
|
||||||
AFImageRequestRoundCorners = 1 << 2,
|
|
||||||
AFImageCacheProcessedImage = 1 << 0xA,
|
|
||||||
AFImageRequestDefaultOptions = AFImageRequestResize,
|
|
||||||
} AFImageRequestOptions;
|
|
||||||
|
|
||||||
@class AFImageRequestOperationCallback;
|
|
||||||
|
|
||||||
@interface AFImageRequestOperation : QHTTPOperation {
|
|
||||||
@private
|
|
||||||
AFImageRequestOperationCallback *_callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property (nonatomic, retain) AFImageRequestOperationCallback *callback;
|
|
||||||
|
|
||||||
+ (id)operationWithRequest:(NSURLRequest *)urlRequest callback:(AFImageRequestOperationCallback *)callback;
|
|
||||||
- (id)initWithRequest:(NSURLRequest *)urlRequest callback:(AFImageRequestOperationCallback *)callback;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#pragma mark - AFHTTPOperationCallback
|
|
||||||
|
|
||||||
typedef void (^AFImageRequestOperationSuccessBlock)(UIImage *image);
|
|
||||||
typedef void (^AFImageRequestOperationErrorBlock)(NSError *error);
|
|
||||||
|
|
||||||
@protocol AFImageRequestOperationCallback <NSObject>
|
|
||||||
@optional
|
|
||||||
+ (id)callbackWithSuccess:(AFImageRequestOperationSuccessBlock)success;
|
|
||||||
+ (id)callbackWithSuccess:(AFImageRequestOperationSuccessBlock)success error:(AFImageRequestOperationErrorBlock)error;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface AFImageRequestOperationCallback : AFCallback <AFImageRequestOperationCallback> {
|
|
||||||
@private
|
|
||||||
CGSize _imageSize;
|
|
||||||
AFImageRequestOptions _options;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property (readwrite, nonatomic, assign) CGSize imageSize;
|
|
||||||
@property (readwrite, nonatomic, assign) AFImageRequestOptions options;
|
|
||||||
|
|
||||||
@property (readwrite, nonatomic, copy) AFImageRequestOperationSuccessBlock successBlock;
|
|
||||||
@property (readwrite, nonatomic, copy) AFImageRequestOperationErrorBlock errorBlock;
|
|
||||||
|
|
||||||
+ (id)callbackWithSuccess:(AFImageRequestOperationSuccessBlock)success imageSize:(CGSize)imageSize options:(AFImageRequestOptions)options;
|
|
||||||
@end
|
|
||||||
|
|
@ -1,143 +0,0 @@
|
||||||
// AFImageRequestOperation.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 "AFImageRequestOperation.h"
|
|
||||||
|
|
||||||
#import "UIImage+AFNetworking.h"
|
|
||||||
|
|
||||||
const CGFloat kAFImageRequestJPEGQuality = 0.8;
|
|
||||||
const NSUInteger kAFImageRequestMaximumResponseSize = 8 * 1024 * 1024;
|
|
||||||
static inline CGSize kAFImageRequestRoundedCornerRadii(CGSize imageSize) {
|
|
||||||
CGFloat dimension = fmaxf(imageSize.width, imageSize.height) * 0.1;
|
|
||||||
return CGSizeMake(dimension, dimension);
|
|
||||||
}
|
|
||||||
|
|
||||||
@implementation AFImageRequestOperation
|
|
||||||
@synthesize callback = _callback;
|
|
||||||
|
|
||||||
+ (id)operationWithRequest:(NSURLRequest *)urlRequest callback:(AFImageRequestOperationCallback *)callback {
|
|
||||||
return [[[self alloc] initWithRequest:urlRequest callback:callback] autorelease];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithRequest:(NSURLRequest *)urlRequest callback:(AFImageRequestOperationCallback *)callback {
|
|
||||||
self = [super initWithRequest:urlRequest];
|
|
||||||
if (!self) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.maximumResponseSize = kAFImageRequestMaximumResponseSize;
|
|
||||||
|
|
||||||
NSMutableIndexSet *statusCodes = [NSMutableIndexSet indexSetWithIndex:0];
|
|
||||||
[statusCodes addIndexesInRange:NSMakeRange(200, 100)];
|
|
||||||
self.acceptableStatusCodes = statusCodes;
|
|
||||||
self.acceptableContentTypes = [NSSet setWithObjects:@"image/png", @"image/jpeg", @"image/pjpeg", @"image/gif", @"application/x-0", nil];
|
|
||||||
self.callback = callback;
|
|
||||||
|
|
||||||
if (self.callback) {
|
|
||||||
self.runLoopModes = [NSSet setWithObjects:NSRunLoopCommonModes, NSDefaultRunLoopMode, nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - QHTTPRequestOperation
|
|
||||||
|
|
||||||
// QHTTPRequestOperation requires this to return an NSHTTPURLResponse, but in certain circumstances,
|
|
||||||
// this method would otherwise return an instance of its superclass, NSURLResponse
|
|
||||||
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
|
|
||||||
if([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
|
||||||
[super connection:connection didReceiveResponse:response];
|
|
||||||
} else {
|
|
||||||
[super connection:connection didReceiveResponse:[[[NSHTTPURLResponse alloc] initWithURL:[response URL] MIMEType:[response MIMEType] expectedContentLength:[response expectedContentLength] textEncodingName:[response textEncodingName]] autorelease]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - QRunLoopOperation
|
|
||||||
|
|
||||||
- (void)finishWithError:(NSError *)error {
|
|
||||||
[super finishWithError:error];
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
if (self.callback.errorBlock) {
|
|
||||||
self.callback.errorBlock(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
UIImage *image = nil;
|
|
||||||
if ([[UIScreen mainScreen] scale] == 2.0) {
|
|
||||||
CGImageRef imageRef = [UIImage imageWithData:self.responseBody].CGImage;
|
|
||||||
image = [UIImage imageWithCGImage:imageRef scale:2.0 orientation:UIImageOrientationUp];
|
|
||||||
} else {
|
|
||||||
image = [UIImage imageWithData:self.responseBody];
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL didProcessingOnImage = NO;
|
|
||||||
|
|
||||||
if ((self.callback.options & AFImageRequestResize) && !(CGSizeEqualToSize(image.size, self.callback.imageSize) || CGSizeEqualToSize(self.callback.imageSize, CGSizeZero))) {
|
|
||||||
image = [UIImage imageByScalingAndCroppingImage:image size:self.callback.imageSize];
|
|
||||||
didProcessingOnImage = YES;
|
|
||||||
}
|
|
||||||
if ((self.callback.options & AFImageRequestRoundCorners)) {
|
|
||||||
image = [UIImage imageByRoundingCornersOfImage:image corners:UIRectCornerAllCorners cornerRadii:kAFImageRequestRoundedCornerRadii(image.size)];
|
|
||||||
didProcessingOnImage = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (self.callback.successBlock) {
|
|
||||||
self.callback.successBlock(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((self.callback.options & AFImageCacheProcessedImage) && didProcessingOnImage) {
|
|
||||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
||||||
NSData *processedImageData = nil;
|
|
||||||
if ((self.callback.options & AFImageRequestRoundCorners) || [[[[self.lastRequest URL] path] pathExtension] isEqualToString:@"png"]) {
|
|
||||||
processedImageData = UIImagePNGRepresentation(image);
|
|
||||||
} else {
|
|
||||||
processedImageData = UIImageJPEGRepresentation(image, kAFImageRequestJPEGQuality);
|
|
||||||
}
|
|
||||||
NSURLResponse *response = [[[NSURLResponse alloc] initWithURL:[self.lastRequest URL] MIMEType:[self.lastResponse MIMEType] expectedContentLength:[processedImageData length] textEncodingName:[self.lastResponse textEncodingName]] autorelease];
|
|
||||||
NSCachedURLResponse *cachedResponse = [[[NSCachedURLResponse alloc] initWithResponse:response data:processedImageData] autorelease];
|
|
||||||
[[NSURLCache sharedURLCache] storeCachedResponse:cachedResponse forRequest:self.lastRequest];
|
|
||||||
[pool drain];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#pragma mark - AFHTTPOperationCallback
|
|
||||||
|
|
||||||
@implementation AFImageRequestOperationCallback : AFCallback
|
|
||||||
@synthesize options = _options;
|
|
||||||
@synthesize imageSize = _imageSize;
|
|
||||||
@dynamic successBlock, errorBlock;
|
|
||||||
|
|
||||||
+ (id)callbackWithSuccess:(AFImageRequestOperationSuccessBlock)success imageSize:(CGSize)imageSize options:(AFImageRequestOptions)options {
|
|
||||||
id callback = [self callbackWithSuccess:success];
|
|
||||||
[callback setImageSize:imageSize];
|
|
||||||
[callback setOptions:options];
|
|
||||||
|
|
||||||
return callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
@ -1,245 +0,0 @@
|
||||||
/*
|
|
||||||
File: QHTTPOperation.h
|
|
||||||
|
|
||||||
Contains: An NSOperation that runs an HTTP request.
|
|
||||||
|
|
||||||
Written by: DTS
|
|
||||||
|
|
||||||
Copyright: Copyright (c) 2010 Apple Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
|
|
||||||
("Apple") in consideration of your agreement to the following
|
|
||||||
terms, and your use, installation, modification or
|
|
||||||
redistribution of this Apple software constitutes acceptance of
|
|
||||||
these terms. If you do not agree with these terms, please do
|
|
||||||
not use, install, modify or redistribute this Apple software.
|
|
||||||
|
|
||||||
In consideration of your agreement to abide by the following
|
|
||||||
terms, and subject to these terms, Apple grants you a personal,
|
|
||||||
non-exclusive license, under Apple's copyrights in this
|
|
||||||
original Apple software (the "Apple Software"), to use,
|
|
||||||
reproduce, modify and redistribute the Apple Software, with or
|
|
||||||
without modifications, in source and/or binary forms; provided
|
|
||||||
that if you redistribute the Apple Software in its entirety and
|
|
||||||
without modifications, you must retain this notice and the
|
|
||||||
following text and disclaimers in all such redistributions of
|
|
||||||
the Apple Software. Neither the name, trademarks, service marks
|
|
||||||
or logos of Apple Inc. may be used to endorse or promote
|
|
||||||
products derived from the Apple Software without specific prior
|
|
||||||
written permission from Apple. Except as expressly stated in
|
|
||||||
this notice, no other rights or licenses, express or implied,
|
|
||||||
are granted by Apple herein, including but not limited to any
|
|
||||||
patent rights that may be infringed by your derivative works or
|
|
||||||
by other works in which the Apple Software may be incorporated.
|
|
||||||
|
|
||||||
The Apple Software is provided by Apple on an "AS IS" basis.
|
|
||||||
APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
|
|
||||||
WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
|
|
||||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING
|
|
||||||
THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
|
|
||||||
COMBINATION WITH YOUR PRODUCTS.
|
|
||||||
|
|
||||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT,
|
|
||||||
INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
||||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY
|
|
||||||
OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
|
|
||||||
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY
|
|
||||||
OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
|
|
||||||
OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF
|
|
||||||
SUCH DAMAGE.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import "QRunLoopOperation.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
QHTTPOperation is a general purpose NSOperation that runs an HTTP request.
|
|
||||||
You initialise it with an HTTP request and then, when you run the operation,
|
|
||||||
it sends the request and gathers the response. It is quite a complex
|
|
||||||
object because it handles a wide variety of edge cases, but it's very
|
|
||||||
easy to use in simple cases:
|
|
||||||
|
|
||||||
1. create the operation with the URL you want to get
|
|
||||||
|
|
||||||
op = [[[QHTTPOperation alloc] initWithURL:url] autorelease];
|
|
||||||
|
|
||||||
2. set up any non-default parameters, for example, set which HTTP
|
|
||||||
content types are acceptable
|
|
||||||
|
|
||||||
op.acceptableContentTypes = [NSSet setWithObject:@"text/html"];
|
|
||||||
|
|
||||||
3. enqueue the operation
|
|
||||||
|
|
||||||
[queue addOperation:op];
|
|
||||||
|
|
||||||
4. finally, when the operation is done, use the lastResponse and
|
|
||||||
error properties to find out how things went
|
|
||||||
|
|
||||||
As mentioned above, QHTTPOperation is very general purpose. There are a
|
|
||||||
large number of configuration and result options available to you.
|
|
||||||
|
|
||||||
o You can specify a NSURLRequest rather than just a URL.
|
|
||||||
|
|
||||||
o You can configure the run loop and modes on which the NSURLConnection is
|
|
||||||
scheduled.
|
|
||||||
|
|
||||||
o You can specify what HTTP status codes and content types are OK.
|
|
||||||
|
|
||||||
o You can set an authentication delegate to handle authentication challenges.
|
|
||||||
|
|
||||||
o You can accumulate responses in memory or in an NSOutputStream.
|
|
||||||
|
|
||||||
o For in-memory responses, you can specify a default response size
|
|
||||||
(used to size the response buffer) and a maximum response size
|
|
||||||
(to prevent unbounded memory use).
|
|
||||||
|
|
||||||
o You can get at the last request and the last response, to track
|
|
||||||
redirects.
|
|
||||||
|
|
||||||
o There are a variety of funky debugging options to simulator errors
|
|
||||||
and delays.
|
|
||||||
|
|
||||||
Finally, it's perfectly reasonable to subclass QHTTPOperation to meet you
|
|
||||||
own specific needs. Specifically, it's common for the subclass to
|
|
||||||
override -connection:didReceiveResponse: in order to setup the output
|
|
||||||
stream based on the specific details of the response.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@protocol QHTTPOperationAuthenticationDelegate;
|
|
||||||
|
|
||||||
@interface QHTTPOperation : QRunLoopOperation /* <NSURLConnectionDelegate> */
|
|
||||||
{
|
|
||||||
NSURLRequest * _request;
|
|
||||||
NSIndexSet * _acceptableStatusCodes;
|
|
||||||
NSSet * _acceptableContentTypes;
|
|
||||||
id<QHTTPOperationAuthenticationDelegate> _authenticationDelegate;
|
|
||||||
NSOutputStream * _responseOutputStream;
|
|
||||||
NSUInteger _defaultResponseSize;
|
|
||||||
NSUInteger _maximumResponseSize;
|
|
||||||
NSURLConnection * _connection;
|
|
||||||
BOOL _firstData;
|
|
||||||
NSMutableData * _dataAccumulator;
|
|
||||||
NSURLRequest * _lastRequest;
|
|
||||||
NSHTTPURLResponse * _lastResponse;
|
|
||||||
NSData * _responseBody;
|
|
||||||
#if ! defined(NDEBUG)
|
|
||||||
NSError * _debugError;
|
|
||||||
NSTimeInterval _debugDelay;
|
|
||||||
NSTimer * _debugDelayTimer;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithRequest:(NSURLRequest *)request; // designated
|
|
||||||
- (id)initWithURL:(NSURL *)url; // convenience, calls +[NSURLRequest requestWithURL:]
|
|
||||||
|
|
||||||
// Things that are configured by the init method and can't be changed.
|
|
||||||
|
|
||||||
@property (copy, readonly) NSURLRequest * request;
|
|
||||||
@property (copy, readonly) NSURL * URL;
|
|
||||||
|
|
||||||
// Things you can configure before queuing the operation.
|
|
||||||
|
|
||||||
// runLoopThread and runLoopModes inherited from QRunLoopOperation
|
|
||||||
@property (copy, readwrite) NSIndexSet * acceptableStatusCodes; // default is nil, implying 200..299
|
|
||||||
@property (copy, readwrite) NSSet * acceptableContentTypes; // default is nil, implying anything is acceptable
|
|
||||||
@property (assign, readwrite) id<QHTTPOperationAuthenticationDelegate> authenticationDelegate;
|
|
||||||
|
|
||||||
#if ! defined(NDEBUG)
|
|
||||||
@property (copy, readwrite) NSError * debugError; // default is nil
|
|
||||||
@property (assign, readwrite) NSTimeInterval debugDelay; // default is none
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Things you can configure up to the point where you start receiving data.
|
|
||||||
// Typically you would change these in -connection:didReceiveResponse:, but
|
|
||||||
// it is possible to change them up to the point where -connection:didReceiveData:
|
|
||||||
// is called for the first time (that is, you could override -connection:didReceiveData:
|
|
||||||
// and change these before calling super).
|
|
||||||
|
|
||||||
// IMPORTANT: If you set a response stream, QHTTPOperation calls the response
|
|
||||||
// stream synchronously. This is fine for file and memory streams, but it would
|
|
||||||
// not work well for other types of streams (like a bound pair).
|
|
||||||
|
|
||||||
@property (retain, readwrite) NSOutputStream * responseOutputStream; // defaults to nil, which puts response into responseBody
|
|
||||||
@property (assign, readwrite) NSUInteger defaultResponseSize; // default is 1 MB, ignored if responseOutputStream is set
|
|
||||||
@property (assign, readwrite) NSUInteger maximumResponseSize; // default is 4 MB, ignored if responseOutputStream is set
|
|
||||||
// defaults are 1/4 of the above on embedded
|
|
||||||
|
|
||||||
// Things that are only meaningful after a response has been received;
|
|
||||||
|
|
||||||
@property (assign, readonly, getter=isStatusCodeAcceptable) BOOL statusCodeAcceptable;
|
|
||||||
@property (assign, readonly, getter=isContentTypeAcceptable) BOOL contentTypeAcceptable;
|
|
||||||
|
|
||||||
// Things that are only meaningful after the operation is finished.
|
|
||||||
|
|
||||||
// error property inherited from QRunLoopOperation
|
|
||||||
@property (copy, readonly) NSURLRequest * lastRequest;
|
|
||||||
@property (copy, readonly) NSHTTPURLResponse * lastResponse;
|
|
||||||
|
|
||||||
@property (copy, readonly) NSData * responseBody;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface QHTTPOperation (NSURLConnectionDelegate)
|
|
||||||
|
|
||||||
// QHTTPOperation implements all of these methods, so if you override them
|
|
||||||
// you must consider whether or not to call super.
|
|
||||||
//
|
|
||||||
// These will be called on the operation's run loop thread.
|
|
||||||
|
|
||||||
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace;
|
|
||||||
// Routes the request to the authentication delegate if it exists, otherwise
|
|
||||||
// just returns NO.
|
|
||||||
|
|
||||||
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
|
|
||||||
// Routes the request to the authentication delegate if it exists, otherwise
|
|
||||||
// just cancels the challenge.
|
|
||||||
|
|
||||||
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response;
|
|
||||||
// Latches the request and response in lastRequest and lastResponse.
|
|
||||||
|
|
||||||
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
|
|
||||||
// Latches the response in lastResponse.
|
|
||||||
|
|
||||||
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
|
|
||||||
// If this is the first chunk of data, it decides whether the data is going to be
|
|
||||||
// routed to memory (responseBody) or a stream (responseOutputStream) and makes the
|
|
||||||
// appropriate preparations. For this and subsequent data it then actually shuffles
|
|
||||||
// the data to its destination.
|
|
||||||
|
|
||||||
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
|
|
||||||
// Completes the operation with either no error (if the response status code is acceptable)
|
|
||||||
// or an error (otherwise).
|
|
||||||
|
|
||||||
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
|
|
||||||
// Completes the operation with the error.
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@protocol QHTTPOperationAuthenticationDelegate <NSObject>
|
|
||||||
@required
|
|
||||||
|
|
||||||
// These are called on the operation's run loop thread and have the same semantics as their
|
|
||||||
// NSURLConnection equivalents. It's important to realise that there is no
|
|
||||||
// didCancelAuthenticationChallenge callback (because NSURLConnection doesn't issue one to us).
|
|
||||||
// Rather, an authentication delegate is expected to observe the operation and cancel itself
|
|
||||||
// if the operation completes while the challenge is running.
|
|
||||||
|
|
||||||
- (BOOL)httpOperation:(QHTTPOperation *)operation canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace;
|
|
||||||
- (void)httpOperation:(QHTTPOperation *)operation didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
extern NSString * kQHTTPOperationErrorDomain;
|
|
||||||
|
|
||||||
// positive error codes are HTML status codes (when they are not allowed via acceptableStatusCodes)
|
|
||||||
//
|
|
||||||
// 0 is, of course, not a valid error code
|
|
||||||
//
|
|
||||||
// negative error codes are errors from the module
|
|
||||||
|
|
||||||
enum {
|
|
||||||
kQHTTPOperationErrorResponseTooLarge = -1,
|
|
||||||
kQHTTPOperationErrorOnOutputStream = -2,
|
|
||||||
kQHTTPOperationErrorBadContentType = -3
|
|
||||||
};
|
|
||||||
|
|
@ -1,653 +0,0 @@
|
||||||
/*
|
|
||||||
File: QHTTPOperation.m
|
|
||||||
|
|
||||||
Contains: An NSOperation that runs an HTTP request.
|
|
||||||
|
|
||||||
Written by: DTS
|
|
||||||
|
|
||||||
Copyright: Copyright (c) 2010 Apple Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
|
|
||||||
("Apple") in consideration of your agreement to the following
|
|
||||||
terms, and your use, installation, modification or
|
|
||||||
redistribution of this Apple software constitutes acceptance of
|
|
||||||
these terms. If you do not agree with these terms, please do
|
|
||||||
not use, install, modify or redistribute this Apple software.
|
|
||||||
|
|
||||||
In consideration of your agreement to abide by the following
|
|
||||||
terms, and subject to these terms, Apple grants you a personal,
|
|
||||||
non-exclusive license, under Apple's copyrights in this
|
|
||||||
original Apple software (the "Apple Software"), to use,
|
|
||||||
reproduce, modify and redistribute the Apple Software, with or
|
|
||||||
without modifications, in source and/or binary forms; provided
|
|
||||||
that if you redistribute the Apple Software in its entirety and
|
|
||||||
without modifications, you must retain this notice and the
|
|
||||||
following text and disclaimers in all such redistributions of
|
|
||||||
the Apple Software. Neither the name, trademarks, service marks
|
|
||||||
or logos of Apple Inc. may be used to endorse or promote
|
|
||||||
products derived from the Apple Software without specific prior
|
|
||||||
written permission from Apple. Except as expressly stated in
|
|
||||||
this notice, no other rights or licenses, express or implied,
|
|
||||||
are granted by Apple herein, including but not limited to any
|
|
||||||
patent rights that may be infringed by your derivative works or
|
|
||||||
by other works in which the Apple Software may be incorporated.
|
|
||||||
|
|
||||||
The Apple Software is provided by Apple on an "AS IS" basis.
|
|
||||||
APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
|
|
||||||
WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
|
|
||||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING
|
|
||||||
THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
|
|
||||||
COMBINATION WITH YOUR PRODUCTS.
|
|
||||||
|
|
||||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT,
|
|
||||||
INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
||||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY
|
|
||||||
OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
|
|
||||||
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY
|
|
||||||
OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
|
|
||||||
OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF
|
|
||||||
SUCH DAMAGE.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import "QHTTPOperation.h"
|
|
||||||
|
|
||||||
@interface QHTTPOperation ()
|
|
||||||
|
|
||||||
// Read/write versions of public properties
|
|
||||||
|
|
||||||
@property (copy, readwrite) NSURLRequest * lastRequest;
|
|
||||||
@property (copy, readwrite) NSHTTPURLResponse * lastResponse;
|
|
||||||
|
|
||||||
// Internal properties
|
|
||||||
|
|
||||||
@property (retain, readwrite) NSURLConnection * connection;
|
|
||||||
@property (assign, readwrite) BOOL firstData;
|
|
||||||
@property (retain, readwrite) NSMutableData * dataAccumulator;
|
|
||||||
|
|
||||||
#if ! defined(NDEBUG)
|
|
||||||
@property (retain, readwrite) NSTimer * debugDelayTimer;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation QHTTPOperation
|
|
||||||
|
|
||||||
#pragma mark * Initialise and finalise
|
|
||||||
|
|
||||||
- (id)initWithRequest:(NSURLRequest *)request
|
|
||||||
// See comment in header.
|
|
||||||
{
|
|
||||||
// any thread
|
|
||||||
assert(request != nil);
|
|
||||||
assert([request URL] != nil);
|
|
||||||
// Because we require an NSHTTPURLResponse, we only support HTTP and HTTPS URLs.
|
|
||||||
assert([[[[request URL] scheme] lowercaseString] isEqual:@"http"] || [[[[request URL] scheme] lowercaseString] isEqual:@"https"]);
|
|
||||||
self = [super init];
|
|
||||||
if (self != nil) {
|
|
||||||
#if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
|
|
||||||
static const NSUInteger kPlatformReductionFactor = 4;
|
|
||||||
#else
|
|
||||||
static const NSUInteger kPlatformReductionFactor = 1;
|
|
||||||
#endif
|
|
||||||
self->_request = [request copy];
|
|
||||||
self->_defaultResponseSize = 1 * 1024 * 1024 / kPlatformReductionFactor;
|
|
||||||
self->_maximumResponseSize = 4 * 1024 * 1024 / kPlatformReductionFactor;
|
|
||||||
self->_firstData = YES;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithURL:(NSURL *)url
|
|
||||||
// See comment in header.
|
|
||||||
{
|
|
||||||
assert(url != nil);
|
|
||||||
return [self initWithRequest:[NSURLRequest requestWithURL:url]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc
|
|
||||||
{
|
|
||||||
#if ! defined(NDEBUG)
|
|
||||||
[self->_debugError release];
|
|
||||||
[self->_debugDelayTimer invalidate];
|
|
||||||
[self->_debugDelayTimer release];
|
|
||||||
#endif
|
|
||||||
// any thread
|
|
||||||
[self->_request release];
|
|
||||||
[self->_acceptableStatusCodes release];
|
|
||||||
[self->_acceptableContentTypes release];
|
|
||||||
[self->_responseOutputStream release];
|
|
||||||
assert(self->_connection == nil); // should have been shut down by now
|
|
||||||
[self->_dataAccumulator release];
|
|
||||||
[self->_lastRequest release];
|
|
||||||
[self->_lastResponse release];
|
|
||||||
[self->_responseBody release];
|
|
||||||
[super dealloc];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark * Properties
|
|
||||||
|
|
||||||
// We write our own settings for many properties because we want to bounce
|
|
||||||
// sets that occur in the wrong state. And, given that we've written the
|
|
||||||
// setter anyway, we also avoid KVO notifications when the value doesn't change.
|
|
||||||
|
|
||||||
@synthesize request = _request;
|
|
||||||
|
|
||||||
@synthesize authenticationDelegate = _authenticationDelegate;
|
|
||||||
|
|
||||||
+ (BOOL)automaticallyNotifiesObserversOfAuthenticationDelegate
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id<QHTTPOperationAuthenticationDelegate>)authenticationDelegate
|
|
||||||
{
|
|
||||||
return self->_authenticationDelegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setAuthenticationDelegate:(id<QHTTPOperationAuthenticationDelegate>)newValue
|
|
||||||
{
|
|
||||||
if (self.state != kQRunLoopOperationStateInited) {
|
|
||||||
assert(NO);
|
|
||||||
} else {
|
|
||||||
if (newValue != self->_authenticationDelegate) {
|
|
||||||
[self willChangeValueForKey:@"authenticationDelegate"];
|
|
||||||
self->_authenticationDelegate = newValue;
|
|
||||||
[self didChangeValueForKey:@"authenticationDelegate"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@synthesize acceptableStatusCodes = _acceptableStatusCodes;
|
|
||||||
|
|
||||||
+ (BOOL)automaticallyNotifiesObserversOfAcceptableStatusCodes
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSIndexSet *)acceptableStatusCodes
|
|
||||||
{
|
|
||||||
return [[self->_acceptableStatusCodes retain] autorelease];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setAcceptableStatusCodes:(NSIndexSet *)newValue
|
|
||||||
{
|
|
||||||
if (self.state != kQRunLoopOperationStateInited) {
|
|
||||||
assert(NO);
|
|
||||||
} else {
|
|
||||||
if (newValue != self->_acceptableStatusCodes) {
|
|
||||||
[self willChangeValueForKey:@"acceptableStatusCodes"];
|
|
||||||
[self->_acceptableStatusCodes autorelease];
|
|
||||||
self->_acceptableStatusCodes = [newValue copy];
|
|
||||||
[self didChangeValueForKey:@"acceptableStatusCodes"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@synthesize acceptableContentTypes = _acceptableContentTypes;
|
|
||||||
|
|
||||||
+ (BOOL)automaticallyNotifiesObserversOfAcceptableContentTypes
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSSet *)acceptableContentTypes
|
|
||||||
{
|
|
||||||
return [[self->_acceptableContentTypes retain] autorelease];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setAcceptableContentTypes:(NSSet *)newValue
|
|
||||||
{
|
|
||||||
if (self.state != kQRunLoopOperationStateInited) {
|
|
||||||
assert(NO);
|
|
||||||
} else {
|
|
||||||
if (newValue != self->_acceptableContentTypes) {
|
|
||||||
[self willChangeValueForKey:@"acceptableContentTypes"];
|
|
||||||
[self->_acceptableContentTypes autorelease];
|
|
||||||
self->_acceptableContentTypes = [newValue copy];
|
|
||||||
[self didChangeValueForKey:@"acceptableContentTypes"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@synthesize responseOutputStream = _responseOutputStream;
|
|
||||||
|
|
||||||
+ (BOOL)automaticallyNotifiesObserversOfResponseOutputStream
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSOutputStream *)responseOutputStream
|
|
||||||
{
|
|
||||||
return [[self->_responseOutputStream retain] autorelease];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setResponseOutputStream:(NSOutputStream *)newValue
|
|
||||||
{
|
|
||||||
if (self.dataAccumulator != nil) {
|
|
||||||
assert(NO);
|
|
||||||
} else {
|
|
||||||
if (newValue != self->_responseOutputStream) {
|
|
||||||
[self willChangeValueForKey:@"responseOutputStream"];
|
|
||||||
[self->_responseOutputStream autorelease];
|
|
||||||
self->_responseOutputStream = [newValue retain];
|
|
||||||
[self didChangeValueForKey:@"responseOutputStream"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@synthesize defaultResponseSize = _defaultResponseSize;
|
|
||||||
|
|
||||||
+ (BOOL)automaticallyNotifiesObserversOfDefaultResponseSize
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSUInteger)defaultResponseSize
|
|
||||||
{
|
|
||||||
return self->_defaultResponseSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setDefaultResponseSize:(NSUInteger)newValue
|
|
||||||
{
|
|
||||||
if (self.dataAccumulator != nil) {
|
|
||||||
assert(NO);
|
|
||||||
} else {
|
|
||||||
if (newValue != self->_defaultResponseSize) {
|
|
||||||
[self willChangeValueForKey:@"defaultResponseSize"];
|
|
||||||
self->_defaultResponseSize = newValue;
|
|
||||||
[self didChangeValueForKey:@"defaultResponseSize"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@synthesize maximumResponseSize = _maximumResponseSize;
|
|
||||||
|
|
||||||
+ (BOOL)automaticallyNotifiesObserversOfMaximumResponseSize
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSUInteger)maximumResponseSize
|
|
||||||
{
|
|
||||||
return self->_maximumResponseSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setMaximumResponseSize:(NSUInteger)newValue
|
|
||||||
{
|
|
||||||
if (self.dataAccumulator != nil) {
|
|
||||||
assert(NO);
|
|
||||||
} else {
|
|
||||||
if (newValue != self->_maximumResponseSize) {
|
|
||||||
[self willChangeValueForKey:@"maximumResponseSize"];
|
|
||||||
self->_maximumResponseSize = newValue;
|
|
||||||
[self didChangeValueForKey:@"maximumResponseSize"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@synthesize lastRequest = _lastRequest;
|
|
||||||
@synthesize lastResponse = _lastResponse;
|
|
||||||
@synthesize responseBody = _responseBody;
|
|
||||||
|
|
||||||
@synthesize connection = _connection;
|
|
||||||
@synthesize firstData = _firstData;
|
|
||||||
@synthesize dataAccumulator = _dataAccumulator;
|
|
||||||
|
|
||||||
- (NSURL *)URL
|
|
||||||
{
|
|
||||||
return [self.request URL];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isStatusCodeAcceptable
|
|
||||||
{
|
|
||||||
NSIndexSet * acceptableStatusCodes;
|
|
||||||
NSInteger statusCode;
|
|
||||||
|
|
||||||
assert(self.lastResponse != nil);
|
|
||||||
|
|
||||||
acceptableStatusCodes = self.acceptableStatusCodes;
|
|
||||||
if (acceptableStatusCodes == nil) {
|
|
||||||
acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
|
|
||||||
}
|
|
||||||
assert(acceptableStatusCodes != nil);
|
|
||||||
|
|
||||||
statusCode = [self.lastResponse statusCode];
|
|
||||||
return (statusCode >= 0) && [acceptableStatusCodes containsIndex: (NSUInteger) statusCode];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isContentTypeAcceptable
|
|
||||||
{
|
|
||||||
NSString * contentType;
|
|
||||||
|
|
||||||
assert(self.lastResponse != nil);
|
|
||||||
contentType = [self.lastResponse MIMEType];
|
|
||||||
return (self.acceptableContentTypes == nil) || ((contentType != nil) && [self.acceptableContentTypes containsObject:contentType]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark * Start and finish overrides
|
|
||||||
|
|
||||||
- (void)operationDidStart
|
|
||||||
// Called by QRunLoopOperation when the operation starts. This kicks of an
|
|
||||||
// asynchronous NSURLConnection.
|
|
||||||
{
|
|
||||||
assert(self.isActualRunLoopThread);
|
|
||||||
assert(self.state == kQRunLoopOperationStateExecuting);
|
|
||||||
|
|
||||||
assert(self.defaultResponseSize > 0);
|
|
||||||
assert(self.maximumResponseSize > 0);
|
|
||||||
assert(self.defaultResponseSize <= self.maximumResponseSize);
|
|
||||||
|
|
||||||
assert(self.request != nil);
|
|
||||||
|
|
||||||
// If a debug error is set, apply that error rather than running the connection.
|
|
||||||
|
|
||||||
#if ! defined(NDEBUG)
|
|
||||||
if (self.debugError != nil) {
|
|
||||||
[self finishWithError:self.debugError];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Create a connection that's scheduled in the required run loop modes.
|
|
||||||
|
|
||||||
assert(self.connection == nil);
|
|
||||||
self.connection = [[[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO] autorelease];
|
|
||||||
assert(self.connection != nil);
|
|
||||||
|
|
||||||
for (NSString * mode in self.actualRunLoopModes) {
|
|
||||||
[self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:mode];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self.connection start];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)operationWillFinish
|
|
||||||
// Called by QRunLoopOperation when the operation has finished. We
|
|
||||||
// do various bits of tidying up.
|
|
||||||
{
|
|
||||||
assert(self.isActualRunLoopThread);
|
|
||||||
assert(self.state == kQRunLoopOperationStateExecuting);
|
|
||||||
|
|
||||||
// It is possible to hit this state of the operation is cancelled while
|
|
||||||
// the debugDelayTimer is running. In that case, hey, we'll just accept
|
|
||||||
// the inevitable and finish rather than trying anything else clever.
|
|
||||||
|
|
||||||
#if ! defined(NDEBUG)
|
|
||||||
if (self.debugDelayTimer != nil) {
|
|
||||||
[self.debugDelayTimer invalidate];
|
|
||||||
self.debugDelayTimer = nil;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
[self.connection cancel];
|
|
||||||
self.connection = nil;
|
|
||||||
|
|
||||||
// If we have an output stream, close it at this point. We might never
|
|
||||||
// have actually opened this stream but, AFAICT, closing an unopened stream
|
|
||||||
// doesn't hurt.
|
|
||||||
|
|
||||||
if (self.responseOutputStream != nil) {
|
|
||||||
[self.responseOutputStream close];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)finishWithError:(NSError *)error
|
|
||||||
// We override -finishWithError: just so we can handle our debug delay.
|
|
||||||
{
|
|
||||||
// If a debug delay was set, don't finish now but rather start the debug delay timer
|
|
||||||
// and have it do the actual finish. We clear self.debugDelay so that the next
|
|
||||||
// time this code runs its doesn't do this again.
|
|
||||||
//
|
|
||||||
// We only do this in the non-cancellation case. In the cancellation case, we
|
|
||||||
// just stop immediately.
|
|
||||||
|
|
||||||
#if ! defined(NDEBUG)
|
|
||||||
if (self.debugDelay > 0.0) {
|
|
||||||
if ( (error != nil) && [[error domain] isEqual:NSCocoaErrorDomain] && ([error code] == NSUserCancelledError) ) {
|
|
||||||
self.debugDelay = 0.0;
|
|
||||||
} else {
|
|
||||||
assert(self.debugDelayTimer == nil);
|
|
||||||
self.debugDelayTimer = [NSTimer timerWithTimeInterval:self.debugDelay target:self selector:@selector(debugDelayTimerDone:) userInfo:error repeats:NO];
|
|
||||||
assert(self.debugDelayTimer != nil);
|
|
||||||
for (NSString * mode in self.actualRunLoopModes) {
|
|
||||||
[[NSRunLoop currentRunLoop] addTimer:self.debugDelayTimer forMode:mode];
|
|
||||||
}
|
|
||||||
self.debugDelay = 0.0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
[super finishWithError:error];
|
|
||||||
}
|
|
||||||
|
|
||||||
#if ! defined(NDEBUG)
|
|
||||||
|
|
||||||
@synthesize debugError = _debugError;
|
|
||||||
@synthesize debugDelay = _debugDelay;
|
|
||||||
@synthesize debugDelayTimer = _debugDelayTimer;
|
|
||||||
|
|
||||||
- (void)debugDelayTimerDone:(NSTimer *)timer
|
|
||||||
{
|
|
||||||
NSError * error;
|
|
||||||
|
|
||||||
assert(timer == self.debugDelayTimer);
|
|
||||||
|
|
||||||
error = [[[timer userInfo] retain] autorelease];
|
|
||||||
assert( (error == nil) || [error isKindOfClass:[NSError class]] );
|
|
||||||
|
|
||||||
[self.debugDelayTimer invalidate];
|
|
||||||
self.debugDelayTimer = nil;
|
|
||||||
|
|
||||||
[self finishWithError:error];
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#pragma mark * NSURLConnection delegate callbacks
|
|
||||||
|
|
||||||
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
|
|
||||||
// See comment in header.
|
|
||||||
{
|
|
||||||
BOOL result;
|
|
||||||
|
|
||||||
assert(self.isActualRunLoopThread);
|
|
||||||
assert(connection == self.connection);
|
|
||||||
#pragma unused(connection)
|
|
||||||
assert(protectionSpace != nil);
|
|
||||||
#pragma unused(protectionSpace)
|
|
||||||
|
|
||||||
result = NO;
|
|
||||||
if (self.authenticationDelegate != nil) {
|
|
||||||
result = [self.authenticationDelegate httpOperation:self canAuthenticateAgainstProtectionSpace:protectionSpace];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
|
|
||||||
// See comment in header.
|
|
||||||
{
|
|
||||||
assert(self.isActualRunLoopThread);
|
|
||||||
assert(connection == self.connection);
|
|
||||||
#pragma unused(connection)
|
|
||||||
assert(challenge != nil);
|
|
||||||
#pragma unused(challenge)
|
|
||||||
|
|
||||||
if (self.authenticationDelegate != nil) {
|
|
||||||
[self.authenticationDelegate httpOperation:self didReceiveAuthenticationChallenge:challenge];
|
|
||||||
} else {
|
|
||||||
if ( [challenge previousFailureCount] == 0 ) {
|
|
||||||
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
|
|
||||||
} else {
|
|
||||||
[[challenge sender] cancelAuthenticationChallenge:challenge];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response
|
|
||||||
// See comment in header.
|
|
||||||
{
|
|
||||||
assert(self.isActualRunLoopThread);
|
|
||||||
assert(connection == self.connection);
|
|
||||||
#pragma unused(connection)
|
|
||||||
assert( (response == nil) || [response isKindOfClass:[NSHTTPURLResponse class]] );
|
|
||||||
|
|
||||||
self.lastRequest = request;
|
|
||||||
self.lastResponse = (NSHTTPURLResponse *) response;
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
|
|
||||||
// See comment in header.
|
|
||||||
{
|
|
||||||
assert(self.isActualRunLoopThread);
|
|
||||||
assert(connection == self.connection);
|
|
||||||
#pragma unused(connection)
|
|
||||||
assert([response isKindOfClass:[NSHTTPURLResponse class]]);
|
|
||||||
|
|
||||||
self.lastResponse = (NSHTTPURLResponse *) response;
|
|
||||||
|
|
||||||
// We don't check the status code here because we want to give the client an opportunity
|
|
||||||
// to get the data of the error message. Perhaps we /should/ check the content type
|
|
||||||
// here, but I'm not sure whether that's the right thing to do.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
|
|
||||||
// See comment in header.
|
|
||||||
{
|
|
||||||
BOOL success;
|
|
||||||
|
|
||||||
assert(self.isActualRunLoopThread);
|
|
||||||
assert(connection == self.connection);
|
|
||||||
#pragma unused(connection)
|
|
||||||
assert(data != nil);
|
|
||||||
|
|
||||||
// If we don't yet have a destination for the data, calculate one. Note that, even
|
|
||||||
// if there is an output stream, we don't use it for error responses.
|
|
||||||
|
|
||||||
success = YES;
|
|
||||||
if (self.firstData) {
|
|
||||||
assert(self.dataAccumulator == nil);
|
|
||||||
|
|
||||||
if ( (self.responseOutputStream == nil) || ! self.isStatusCodeAcceptable ) {
|
|
||||||
long long length;
|
|
||||||
|
|
||||||
assert(self.dataAccumulator == nil);
|
|
||||||
|
|
||||||
length = [self.lastResponse expectedContentLength];
|
|
||||||
if (length == NSURLResponseUnknownLength) {
|
|
||||||
length = self.defaultResponseSize;
|
|
||||||
}
|
|
||||||
if (length <= (long long) self.maximumResponseSize) {
|
|
||||||
self.dataAccumulator = [NSMutableData dataWithCapacity:(NSUInteger)length];
|
|
||||||
} else {
|
|
||||||
[self finishWithError:[NSError errorWithDomain:kQHTTPOperationErrorDomain code:kQHTTPOperationErrorResponseTooLarge userInfo:nil]];
|
|
||||||
success = NO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the data is going to an output stream, open it.
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
if (self.dataAccumulator == nil) {
|
|
||||||
assert(self.responseOutputStream != nil);
|
|
||||||
[self.responseOutputStream open];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.firstData = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the data to its destination.
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
if (self.dataAccumulator != nil) {
|
|
||||||
if ( ([self.dataAccumulator length] + [data length]) <= self.maximumResponseSize ) {
|
|
||||||
[self.dataAccumulator appendData:data];
|
|
||||||
} else {
|
|
||||||
[self finishWithError:[NSError errorWithDomain:kQHTTPOperationErrorDomain code:kQHTTPOperationErrorResponseTooLarge userInfo:nil]];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
NSUInteger dataOffset;
|
|
||||||
NSUInteger dataLength;
|
|
||||||
const uint8_t * dataPtr;
|
|
||||||
NSError * error;
|
|
||||||
NSInteger bytesWritten;
|
|
||||||
|
|
||||||
assert(self.responseOutputStream != nil);
|
|
||||||
|
|
||||||
dataOffset = 0;
|
|
||||||
dataLength = [data length];
|
|
||||||
dataPtr = [data bytes];
|
|
||||||
error = nil;
|
|
||||||
do {
|
|
||||||
if (dataOffset == dataLength) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
bytesWritten = [self.responseOutputStream write:&dataPtr[dataOffset] maxLength:dataLength - dataOffset];
|
|
||||||
if (bytesWritten <= 0) {
|
|
||||||
error = [self.responseOutputStream streamError];
|
|
||||||
if (error == nil) {
|
|
||||||
error = [NSError errorWithDomain:kQHTTPOperationErrorDomain code:kQHTTPOperationErrorOnOutputStream userInfo:nil];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
dataOffset += bytesWritten;
|
|
||||||
}
|
|
||||||
} while (YES);
|
|
||||||
|
|
||||||
if (error != nil) {
|
|
||||||
[self finishWithError:error];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
|
|
||||||
// See comment in header.
|
|
||||||
{
|
|
||||||
assert(self.isActualRunLoopThread);
|
|
||||||
assert(connection == self.connection);
|
|
||||||
#pragma unused(connection)
|
|
||||||
|
|
||||||
assert(self.lastResponse != nil);
|
|
||||||
|
|
||||||
// Swap the data accumulator over to the response data so that we don't trigger a copy.
|
|
||||||
|
|
||||||
assert(self->_responseBody == nil);
|
|
||||||
self->_responseBody = self->_dataAccumulator;
|
|
||||||
self->_dataAccumulator = nil;
|
|
||||||
|
|
||||||
// Because we fill out _dataAccumulator lazily, an empty body will leave _dataAccumulator
|
|
||||||
// set to nil. That's not what our clients expect, so we fix it here.
|
|
||||||
|
|
||||||
if (self->_responseBody == nil) {
|
|
||||||
self->_responseBody = [[NSData alloc] init];
|
|
||||||
assert(self->_responseBody != nil);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! self.isStatusCodeAcceptable ) {
|
|
||||||
[self finishWithError:[NSError errorWithDomain:kQHTTPOperationErrorDomain code:self.lastResponse.statusCode userInfo:nil]];
|
|
||||||
} else if ( ! self.isContentTypeAcceptable ) {
|
|
||||||
[self finishWithError:[NSError errorWithDomain:kQHTTPOperationErrorDomain code:kQHTTPOperationErrorBadContentType userInfo:nil]];
|
|
||||||
} else {
|
|
||||||
[self finishWithError:nil];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
|
|
||||||
// See comment in header.
|
|
||||||
{
|
|
||||||
assert(self.isActualRunLoopThread);
|
|
||||||
assert(connection == self.connection);
|
|
||||||
#pragma unused(connection)
|
|
||||||
assert(error != nil);
|
|
||||||
|
|
||||||
[self finishWithError:error];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NSString * kQHTTPOperationErrorDomain = @"kQHTTPOperationErrorDomain";
|
|
||||||
|
|
@ -1,119 +0,0 @@
|
||||||
/*
|
|
||||||
File: QRunLoopOperation.h
|
|
||||||
|
|
||||||
Contains: An abstract subclass of NSOperation for async run loop based operations.
|
|
||||||
|
|
||||||
Written by: DTS
|
|
||||||
|
|
||||||
Copyright: Copyright (c) 2010 Apple Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
|
|
||||||
("Apple") in consideration of your agreement to the following
|
|
||||||
terms, and your use, installation, modification or
|
|
||||||
redistribution of this Apple software constitutes acceptance of
|
|
||||||
these terms. If you do not agree with these terms, please do
|
|
||||||
not use, install, modify or redistribute this Apple software.
|
|
||||||
|
|
||||||
In consideration of your agreement to abide by the following
|
|
||||||
terms, and subject to these terms, Apple grants you a personal,
|
|
||||||
non-exclusive license, under Apple's copyrights in this
|
|
||||||
original Apple software (the "Apple Software"), to use,
|
|
||||||
reproduce, modify and redistribute the Apple Software, with or
|
|
||||||
without modifications, in source and/or binary forms; provided
|
|
||||||
that if you redistribute the Apple Software in its entirety and
|
|
||||||
without modifications, you must retain this notice and the
|
|
||||||
following text and disclaimers in all such redistributions of
|
|
||||||
the Apple Software. Neither the name, trademarks, service marks
|
|
||||||
or logos of Apple Inc. may be used to endorse or promote
|
|
||||||
products derived from the Apple Software without specific prior
|
|
||||||
written permission from Apple. Except as expressly stated in
|
|
||||||
this notice, no other rights or licenses, express or implied,
|
|
||||||
are granted by Apple herein, including but not limited to any
|
|
||||||
patent rights that may be infringed by your derivative works or
|
|
||||||
by other works in which the Apple Software may be incorporated.
|
|
||||||
|
|
||||||
The Apple Software is provided by Apple on an "AS IS" basis.
|
|
||||||
APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
|
|
||||||
WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
|
|
||||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING
|
|
||||||
THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
|
|
||||||
COMBINATION WITH YOUR PRODUCTS.
|
|
||||||
|
|
||||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT,
|
|
||||||
INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
||||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY
|
|
||||||
OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
|
|
||||||
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY
|
|
||||||
OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
|
|
||||||
OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF
|
|
||||||
SUCH DAMAGE.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
enum QRunLoopOperationState {
|
|
||||||
kQRunLoopOperationStateInited,
|
|
||||||
kQRunLoopOperationStateExecuting,
|
|
||||||
kQRunLoopOperationStateFinished
|
|
||||||
};
|
|
||||||
typedef enum QRunLoopOperationState QRunLoopOperationState;
|
|
||||||
|
|
||||||
@interface QRunLoopOperation : NSOperation
|
|
||||||
{
|
|
||||||
QRunLoopOperationState _state;
|
|
||||||
NSThread * _runLoopThread;
|
|
||||||
NSSet * _runLoopModes;
|
|
||||||
NSError * _error;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Things you can configure before queuing the operation.
|
|
||||||
|
|
||||||
// IMPORTANT: Do not change these after queuing the operation; it's very likely that
|
|
||||||
// bad things will happen if you do.
|
|
||||||
|
|
||||||
@property (retain, readwrite) NSThread * runLoopThread; // default is nil, implying main thread
|
|
||||||
@property (copy, readwrite) NSSet * runLoopModes; // default is nil, implying set containing NSDefaultRunLoopMode
|
|
||||||
|
|
||||||
// Things that are only meaningful after the operation is finished.
|
|
||||||
|
|
||||||
@property (copy, readonly ) NSError * error;
|
|
||||||
|
|
||||||
// Things you can only alter implicitly.
|
|
||||||
|
|
||||||
@property (assign, readonly ) QRunLoopOperationState state;
|
|
||||||
@property (retain, readonly ) NSThread * actualRunLoopThread; // main thread if runLoopThread is nil, runLoopThread otherwise
|
|
||||||
@property (assign, readonly ) BOOL isActualRunLoopThread; // YES if the current thread is the actual run loop thread
|
|
||||||
@property (copy, readonly ) NSSet * actualRunLoopModes; // set containing NSDefaultRunLoopMode if runLoopModes is nil or empty, runLoopModes otherwise
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface QRunLoopOperation (SubClassSupport)
|
|
||||||
|
|
||||||
// Override points
|
|
||||||
|
|
||||||
// A subclass will probably need to override -operationDidStart and -operationWillFinish
|
|
||||||
// to set up and tear down its run loop sources, respectively. These are always called
|
|
||||||
// on the actual run loop thread.
|
|
||||||
//
|
|
||||||
// Note that -operationWillFinish will be called even if the operation is cancelled.
|
|
||||||
//
|
|
||||||
// -operationWillFinish can check the error property to see whether the operation was
|
|
||||||
// successful. error will be NSCocoaErrorDomain/NSUserCancelledError on cancellation.
|
|
||||||
//
|
|
||||||
// -operationDidStart is allowed to call -finishWithError:.
|
|
||||||
|
|
||||||
- (void)operationDidStart;
|
|
||||||
- (void)operationWillFinish;
|
|
||||||
|
|
||||||
// Support methods
|
|
||||||
|
|
||||||
// A subclass should call finishWithError: when the operation is complete, passing nil
|
|
||||||
// for no error and an error otherwise. It must call this on the actual run loop thread.
|
|
||||||
//
|
|
||||||
// Note that this will call -operationWillFinish before returning.
|
|
||||||
|
|
||||||
- (void)finishWithError:(NSError *)error;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
@ -1,359 +0,0 @@
|
||||||
/*
|
|
||||||
File: QRunLoopOperation.m
|
|
||||||
|
|
||||||
Contains: An abstract subclass of NSOperation for async run loop based operations.
|
|
||||||
|
|
||||||
Written by: DTS
|
|
||||||
|
|
||||||
Copyright: Copyright (c) 2010 Apple Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
|
|
||||||
("Apple") in consideration of your agreement to the following
|
|
||||||
terms, and your use, installation, modification or
|
|
||||||
redistribution of this Apple software constitutes acceptance of
|
|
||||||
these terms. If you do not agree with these terms, please do
|
|
||||||
not use, install, modify or redistribute this Apple software.
|
|
||||||
|
|
||||||
In consideration of your agreement to abide by the following
|
|
||||||
terms, and subject to these terms, Apple grants you a personal,
|
|
||||||
non-exclusive license, under Apple's copyrights in this
|
|
||||||
original Apple software (the "Apple Software"), to use,
|
|
||||||
reproduce, modify and redistribute the Apple Software, with or
|
|
||||||
without modifications, in source and/or binary forms; provided
|
|
||||||
that if you redistribute the Apple Software in its entirety and
|
|
||||||
without modifications, you must retain this notice and the
|
|
||||||
following text and disclaimers in all such redistributions of
|
|
||||||
the Apple Software. Neither the name, trademarks, service marks
|
|
||||||
or logos of Apple Inc. may be used to endorse or promote
|
|
||||||
products derived from the Apple Software without specific prior
|
|
||||||
written permission from Apple. Except as expressly stated in
|
|
||||||
this notice, no other rights or licenses, express or implied,
|
|
||||||
are granted by Apple herein, including but not limited to any
|
|
||||||
patent rights that may be infringed by your derivative works or
|
|
||||||
by other works in which the Apple Software may be incorporated.
|
|
||||||
|
|
||||||
The Apple Software is provided by Apple on an "AS IS" basis.
|
|
||||||
APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
|
|
||||||
WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
|
|
||||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING
|
|
||||||
THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
|
|
||||||
COMBINATION WITH YOUR PRODUCTS.
|
|
||||||
|
|
||||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT,
|
|
||||||
INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
||||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY
|
|
||||||
OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
|
|
||||||
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY
|
|
||||||
OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
|
|
||||||
OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF
|
|
||||||
SUCH DAMAGE.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import "QRunLoopOperation.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
Theory of Operation
|
|
||||||
-------------------
|
|
||||||
Some critical points:
|
|
||||||
|
|
||||||
1. By the time we're running on the run loop thread, we know that all further state
|
|
||||||
transitions happen on the run loop thread. That's because there are only three
|
|
||||||
states (inited, executing, and finished) and run loop thread code can only run
|
|
||||||
in the last two states and the transition from executing to finished is
|
|
||||||
always done on the run loop thread.
|
|
||||||
|
|
||||||
2. -start can only be called once. So run loop thread code doesn't have to worry
|
|
||||||
about racing with -start because, by the time the run loop thread code runs,
|
|
||||||
-start has already been called.
|
|
||||||
|
|
||||||
3. -cancel can be called multiple times from any thread. Run loop thread code
|
|
||||||
must take a lot of care with do the right thing with cancellation.
|
|
||||||
|
|
||||||
Some state transitions:
|
|
||||||
|
|
||||||
1. init -> dealloc
|
|
||||||
2. init -> cancel -> dealloc
|
|
||||||
XXX 3. init -> cancel -> start -> finish -> dealloc
|
|
||||||
4. init -> cancel -> start -> startOnRunLoopThreadThread -> finish dealloc
|
|
||||||
!!! 5. init -> start -> cancel -> startOnRunLoopThreadThread -> finish -> cancelOnRunLoopThreadThread -> dealloc
|
|
||||||
XXX 6. init -> start -> cancel -> cancelOnRunLoopThreadThread -> startOnRunLoopThreadThread -> finish -> dealloc
|
|
||||||
XXX 7. init -> start -> cancel -> startOnRunLoopThreadThread -> cancelOnRunLoopThreadThread -> finish -> dealloc
|
|
||||||
8. init -> start -> startOnRunLoopThreadThread -> finish -> dealloc
|
|
||||||
9. init -> start -> startOnRunLoopThreadThread -> cancel -> cancelOnRunLoopThreadThread -> finish -> dealloc
|
|
||||||
!!! 10. init -> start -> startOnRunLoopThreadThread -> cancel -> finish -> cancelOnRunLoopThreadThread -> dealloc
|
|
||||||
11. init -> start -> startOnRunLoopThreadThread -> finish -> cancel -> dealloc
|
|
||||||
|
|
||||||
Markup:
|
|
||||||
XXX means that the case doesn't happen.
|
|
||||||
!!! means that the case is interesting.
|
|
||||||
|
|
||||||
Described:
|
|
||||||
|
|
||||||
1. It's valid to allocate an operation and never run it.
|
|
||||||
2. It's also valid to allocate an operation, cancel it, and yet never run it.
|
|
||||||
3. While it's valid to cancel an operation before it starting it, this case doesn't
|
|
||||||
happen because -start always bounces to the run loop thread to maintain the invariant
|
|
||||||
that the executing to finished transition always happens on the run loop thread.
|
|
||||||
4. In this -startOnRunLoopThread detects the cancellation and finishes immediately.
|
|
||||||
5. Because the -cancel can happen on any thread, it's possible for the -cancel
|
|
||||||
to come in between the -start and the -startOnRunLoop thread. In this case
|
|
||||||
-startOnRunLoopThread notices isCancelled and finishes straightaway. And
|
|
||||||
-cancelOnRunLoopThread detects that the operation is finished and does nothing.
|
|
||||||
6. This case can never happen because -performSelecton:onThread:xxx
|
|
||||||
callbacks happen in order, -start is synchronised with -cancel, and -cancel
|
|
||||||
only schedules if -start has run.
|
|
||||||
7. This case can never happen because -startOnRunLoopThread will finish immediately
|
|
||||||
if it detects isCancelled (see case 5).
|
|
||||||
8. This is the standard run-to-completion case.
|
|
||||||
9. This is the standard cancellation case. -cancelOnRunLoopThread wins the race
|
|
||||||
with finish, and it detects that the operation is executing and actually cancels.
|
|
||||||
10. In this case the -cancelOnRunLoopThread loses the race with finish, but that's OK
|
|
||||||
because -cancelOnRunLoopThread already does nothing if the operation is already
|
|
||||||
finished.
|
|
||||||
11. Cancellating after finishing still sets isCancelled but has no impact
|
|
||||||
on the RunLoop thread code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@interface QRunLoopOperation ()
|
|
||||||
|
|
||||||
// read/write versions of public properties
|
|
||||||
|
|
||||||
@property (assign, readwrite) QRunLoopOperationState state;
|
|
||||||
@property (copy, readwrite) NSError * error;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation QRunLoopOperation
|
|
||||||
|
|
||||||
- (id)init
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
if (self != nil) {
|
|
||||||
assert(self->_state == kQRunLoopOperationStateInited);
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc
|
|
||||||
{
|
|
||||||
assert(self->_state != kQRunLoopOperationStateExecuting);
|
|
||||||
[self->_runLoopModes release];
|
|
||||||
[self->_runLoopThread release];
|
|
||||||
[self->_error release];
|
|
||||||
[super dealloc];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark * Properties
|
|
||||||
|
|
||||||
@synthesize runLoopThread = _runLoopThread;
|
|
||||||
@synthesize runLoopModes = _runLoopModes;
|
|
||||||
|
|
||||||
- (NSThread *)actualRunLoopThread
|
|
||||||
// Returns the effective run loop thread, that is, the one set by the user
|
|
||||||
// or, if that's not set, the main thread.
|
|
||||||
{
|
|
||||||
NSThread * result;
|
|
||||||
|
|
||||||
result = self.runLoopThread;
|
|
||||||
if (result == nil) {
|
|
||||||
result = [NSThread mainThread];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isActualRunLoopThread
|
|
||||||
// Returns YES if the current thread is the actual run loop thread.
|
|
||||||
{
|
|
||||||
return [[NSThread currentThread] isEqual:self.actualRunLoopThread];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSSet *)actualRunLoopModes
|
|
||||||
{
|
|
||||||
NSSet * result;
|
|
||||||
|
|
||||||
result = self.runLoopModes;
|
|
||||||
if ( (result == nil) || ([result count] == 0) ) {
|
|
||||||
result = [NSSet setWithObject:NSDefaultRunLoopMode];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@synthesize error = _error;
|
|
||||||
|
|
||||||
#pragma mark * Core state transitions
|
|
||||||
|
|
||||||
- (QRunLoopOperationState)state
|
|
||||||
{
|
|
||||||
return self->_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setState:(QRunLoopOperationState)newState
|
|
||||||
// Change the state of the operation, sending the appropriate KVO notifications.
|
|
||||||
{
|
|
||||||
// any thread
|
|
||||||
|
|
||||||
@synchronized (self) {
|
|
||||||
QRunLoopOperationState oldState;
|
|
||||||
|
|
||||||
// The following check is really important. The state can only go forward, and there
|
|
||||||
// should be no redundant changes to the state (that is, newState must never be
|
|
||||||
// equal to self->_state).
|
|
||||||
|
|
||||||
assert(newState > self->_state);
|
|
||||||
|
|
||||||
// Transitions from executing to finished must be done on the run loop thread.
|
|
||||||
|
|
||||||
assert( (newState != kQRunLoopOperationStateFinished) || self.isActualRunLoopThread );
|
|
||||||
|
|
||||||
// inited + executing -> isExecuting
|
|
||||||
// inited + finished -> isFinished
|
|
||||||
// executing + finished -> isExecuting + isFinished
|
|
||||||
|
|
||||||
oldState = self->_state;
|
|
||||||
if ( (newState == kQRunLoopOperationStateExecuting) || (oldState == kQRunLoopOperationStateExecuting) ) {
|
|
||||||
[self willChangeValueForKey:@"isExecuting"];
|
|
||||||
}
|
|
||||||
if (newState == kQRunLoopOperationStateFinished) {
|
|
||||||
[self willChangeValueForKey:@"isFinished"];
|
|
||||||
}
|
|
||||||
self->_state = newState;
|
|
||||||
if (newState == kQRunLoopOperationStateFinished) {
|
|
||||||
[self didChangeValueForKey:@"isFinished"];
|
|
||||||
}
|
|
||||||
if ( (newState == kQRunLoopOperationStateExecuting) || (oldState == kQRunLoopOperationStateExecuting) ) {
|
|
||||||
[self didChangeValueForKey:@"isExecuting"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)startOnRunLoopThread
|
|
||||||
// Starts the operation. The actual -start method is very simple,
|
|
||||||
// deferring all of the work to be done on the run loop thread by this
|
|
||||||
// method.
|
|
||||||
{
|
|
||||||
assert(self.isActualRunLoopThread);
|
|
||||||
assert(self.state == kQRunLoopOperationStateExecuting);
|
|
||||||
|
|
||||||
if ([self isCancelled]) {
|
|
||||||
|
|
||||||
// We were cancelled before we even got running. Flip the the finished
|
|
||||||
// state immediately.
|
|
||||||
|
|
||||||
[self finishWithError:[NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]];
|
|
||||||
} else {
|
|
||||||
[self operationDidStart];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)cancelOnRunLoopThread
|
|
||||||
// Cancels the operation.
|
|
||||||
{
|
|
||||||
assert(self.isActualRunLoopThread);
|
|
||||||
|
|
||||||
// We know that a) state was kQRunLoopOperationStateExecuting when we were
|
|
||||||
// scheduled (that's enforced by -cancel), and b) the state can't go
|
|
||||||
// backwards (that's enforced by -setState), so we know the state must
|
|
||||||
// either be kQRunLoopOperationStateExecuting or kQRunLoopOperationStateFinished.
|
|
||||||
// We also know that the transition from executing to finished always
|
|
||||||
// happens on the run loop thread. Thus, we don't need to lock here.
|
|
||||||
// We can look at state and, if we're executing, trigger a cancellation.
|
|
||||||
|
|
||||||
if (self.state == kQRunLoopOperationStateExecuting) {
|
|
||||||
[self finishWithError:[NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)finishWithError:(NSError *)error
|
|
||||||
{
|
|
||||||
assert(self.isActualRunLoopThread);
|
|
||||||
// error may be nil
|
|
||||||
|
|
||||||
if (self.error == nil) {
|
|
||||||
self.error = error;
|
|
||||||
}
|
|
||||||
[self operationWillFinish];
|
|
||||||
self.state = kQRunLoopOperationStateFinished;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark * Subclass override points
|
|
||||||
|
|
||||||
- (void)operationDidStart
|
|
||||||
{
|
|
||||||
assert(self.isActualRunLoopThread);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)operationWillFinish
|
|
||||||
{
|
|
||||||
assert(self.isActualRunLoopThread);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark * Overrides
|
|
||||||
|
|
||||||
- (BOOL)isConcurrent
|
|
||||||
{
|
|
||||||
// any thread
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isExecuting
|
|
||||||
{
|
|
||||||
// any thread
|
|
||||||
return self.state == kQRunLoopOperationStateExecuting;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isFinished
|
|
||||||
{
|
|
||||||
// any thread
|
|
||||||
return self.state == kQRunLoopOperationStateFinished;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)start
|
|
||||||
{
|
|
||||||
// any thread
|
|
||||||
|
|
||||||
assert(self.state == kQRunLoopOperationStateInited);
|
|
||||||
|
|
||||||
// We have to change the state here, otherwise isExecuting won't necessarily return
|
|
||||||
// true by the time we return from -start. Also, we don't test for cancellation
|
|
||||||
// here because that would a) result in us sending isFinished notifications on a
|
|
||||||
// thread that isn't our run loop thread, and b) confuse the core cancellation code,
|
|
||||||
// which expects to run on our run loop thread. Finally, we don't have to worry
|
|
||||||
// about races with other threads calling -start. Only one thread is allowed to
|
|
||||||
// start us at a time.
|
|
||||||
|
|
||||||
self.state = kQRunLoopOperationStateExecuting;
|
|
||||||
[self performSelector:@selector(startOnRunLoopThread) onThread:self.actualRunLoopThread withObject:nil waitUntilDone:NO modes:[self.actualRunLoopModes allObjects]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)cancel
|
|
||||||
{
|
|
||||||
BOOL runCancelOnRunLoopThread;
|
|
||||||
BOOL oldValue;
|
|
||||||
|
|
||||||
// any thread
|
|
||||||
|
|
||||||
// We need to synchronise here to avoid state changes to isCancelled and state
|
|
||||||
// while we're running.
|
|
||||||
|
|
||||||
@synchronized (self) {
|
|
||||||
oldValue = [self isCancelled];
|
|
||||||
|
|
||||||
// Call our super class so that isCancelled starts returning true immediately.
|
|
||||||
|
|
||||||
[super cancel];
|
|
||||||
|
|
||||||
// If we were the one to set isCancelled (that is, we won the race with regards
|
|
||||||
// other threads calling -cancel) and we're actually running (that is, we lost
|
|
||||||
// the race with other threads calling -start and the run loop thread finishing),
|
|
||||||
// we schedule to run on the run loop thread.
|
|
||||||
|
|
||||||
runCancelOnRunLoopThread = ! oldValue && self.state == kQRunLoopOperationStateExecuting;
|
|
||||||
}
|
|
||||||
if (runCancelOnRunLoopThread) {
|
|
||||||
[self performSelector:@selector(cancelOnRunLoopThread) onThread:self.actualRunLoopThread withObject:nil waitUntilDone:YES modes:[self.actualRunLoopModes allObjects]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
56
Example/AFHTTPRequestOperation.h
Normal file
56
Example/AFHTTPRequestOperation.h
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
// AFHTTPOperation.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 <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
extern NSString * const AFHTTPOperationDidStartNotification;
|
||||||
|
extern NSString * const AFHTTPOperationDidFinishNotification;
|
||||||
|
|
||||||
|
@interface AFHTTPRequestOperation : NSOperation <NSURLConnectionDelegate, NSURLConnectionDataDelegate> {
|
||||||
|
@private
|
||||||
|
NSURLConnection *_connection;
|
||||||
|
NSPort *_port;
|
||||||
|
NSSet *_runLoopModes;
|
||||||
|
|
||||||
|
NSURLRequest *_request;
|
||||||
|
NSHTTPURLResponse *_response;
|
||||||
|
|
||||||
|
NSData *_responseBody;
|
||||||
|
NSMutableData *_dataAccumulator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property (nonatomic, retain) NSURLConnection *connection;
|
||||||
|
@property (nonatomic, retain) NSSet *runLoopModes;
|
||||||
|
|
||||||
|
@property (nonatomic, retain) NSURLRequest *request;
|
||||||
|
@property (nonatomic, retain) NSHTTPURLResponse *response;
|
||||||
|
@property (nonatomic, retain) NSError *error;
|
||||||
|
|
||||||
|
@property (nonatomic, retain) NSData *responseBody;
|
||||||
|
@property (readonly) NSString *responseString;
|
||||||
|
|
||||||
|
+ (id)operationWithRequest:(NSURLRequest *)urlRequest
|
||||||
|
completion:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSData *data, NSError *error))completion;
|
||||||
|
|
||||||
|
- (id)initWithRequest:(NSURLRequest *)urlRequest;
|
||||||
|
|
||||||
|
@end
|
||||||
269
Example/AFHTTPRequestOperation.m
Normal file
269
Example/AFHTTPRequestOperation.m
Normal file
|
|
@ -0,0 +1,269 @@
|
||||||
|
// AFHTTPOperation.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 "AFHTTPRequestOperation.h"
|
||||||
|
#import "AFNetworkActivityIndicatorManager.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
AFHTTPOperationReadyState = 1,
|
||||||
|
AFHTTPOperationExecutingState = 2,
|
||||||
|
AFHTTPOperationFinishedState = 3,
|
||||||
|
AFHTTPOperationCancelledState = 4,
|
||||||
|
} AFHTTPOperationState;
|
||||||
|
|
||||||
|
NSString * const AFHTTPOperationDidStartNotification = @"com.alamofire.http-operation.start";
|
||||||
|
NSString * const AFHTTPOperationDidFinishNotification = @"com.alamofire.http-operation.finish";
|
||||||
|
|
||||||
|
typedef void (^AFHTTPRequestOperationCompletionBlock)(NSURLRequest *request, NSHTTPURLResponse *response, NSData *data, NSError *error);
|
||||||
|
|
||||||
|
static inline NSString * AFKeyPathFromOperationState(AFHTTPOperationState state) {
|
||||||
|
switch (state) {
|
||||||
|
case AFHTTPOperationReadyState:
|
||||||
|
return @"isReady";
|
||||||
|
case AFHTTPOperationExecutingState:
|
||||||
|
return @"isExecuting";
|
||||||
|
case AFHTTPOperationFinishedState:
|
||||||
|
case AFHTTPOperationCancelledState:
|
||||||
|
return @"isFinished";
|
||||||
|
default:
|
||||||
|
return @"state";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline BOOL AFHTTPOperationStateTransitionIsValid(AFHTTPOperationState from, AFHTTPOperationState to) {
|
||||||
|
if (from == to) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (from) {
|
||||||
|
case AFHTTPOperationReadyState:
|
||||||
|
switch (to) {
|
||||||
|
case AFHTTPOperationExecutingState:
|
||||||
|
case AFHTTPOperationCancelledState:
|
||||||
|
return YES;
|
||||||
|
default:
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
case AFHTTPOperationExecutingState:
|
||||||
|
switch (to) {
|
||||||
|
case AFHTTPOperationReadyState:
|
||||||
|
return NO;
|
||||||
|
default:
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
case AFHTTPOperationFinishedState:
|
||||||
|
case AFHTTPOperationCancelledState:
|
||||||
|
return NO;
|
||||||
|
default:
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@interface AFHTTPRequestOperation ()
|
||||||
|
@property (nonatomic, assign) AFHTTPOperationState state;
|
||||||
|
@property (readwrite, nonatomic, retain) NSPort *port;
|
||||||
|
@property (readwrite, nonatomic, retain) NSMutableData *dataAccumulator;
|
||||||
|
@property (readwrite, nonatomic, copy) AFHTTPRequestOperationCompletionBlock completion;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation AFHTTPRequestOperation
|
||||||
|
@synthesize state = _state;
|
||||||
|
@synthesize connection = _connection;
|
||||||
|
@synthesize runLoopModes = _runLoopModes;
|
||||||
|
@synthesize port = _port;
|
||||||
|
@synthesize request = _request;
|
||||||
|
@synthesize response = _response;
|
||||||
|
@synthesize error = _error;
|
||||||
|
@synthesize responseBody = _responseBody;
|
||||||
|
@synthesize dataAccumulator = _dataAccumulator;
|
||||||
|
@synthesize completion = _completion;
|
||||||
|
|
||||||
|
+ (id)operationWithRequest:(NSURLRequest *)urlRequest
|
||||||
|
completion:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSData *data, NSError *error))completion
|
||||||
|
{
|
||||||
|
AFHTTPRequestOperation *operation = [[[self alloc] initWithRequest:urlRequest] autorelease];
|
||||||
|
operation.completion = completion;
|
||||||
|
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)initWithRequest:(NSURLRequest *)urlRequest {
|
||||||
|
self = [super init];
|
||||||
|
if (!self) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.request = urlRequest;
|
||||||
|
|
||||||
|
self.runLoopModes = [NSSet setWithObjects:NSDefaultRunLoopMode, NSRunLoopCommonModes, nil];
|
||||||
|
|
||||||
|
self.state = AFHTTPOperationReadyState;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc {
|
||||||
|
[_runLoopModes release];
|
||||||
|
[_port release];
|
||||||
|
|
||||||
|
[_request release];
|
||||||
|
[_response release];
|
||||||
|
[_responseBody release];
|
||||||
|
[_dataAccumulator release];
|
||||||
|
|
||||||
|
[_connection release];
|
||||||
|
|
||||||
|
[_completion release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setState:(AFHTTPOperationState)state {
|
||||||
|
if (!AFHTTPOperationStateTransitionIsValid(self.state, 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:
|
||||||
|
[[AFNetworkActivityIndicatorManager sharedManager] startAnimating];
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:AFHTTPOperationDidStartNotification object:self];
|
||||||
|
break;
|
||||||
|
case AFHTTPOperationFinishedState:
|
||||||
|
case AFHTTPOperationCancelledState:
|
||||||
|
[[AFNetworkActivityIndicatorManager sharedManager] stopAnimating];
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:AFHTTPOperationDidFinishNotification object:self];
|
||||||
|
|
||||||
|
for (NSString *runLoopMode in self.runLoopModes) {
|
||||||
|
[[NSRunLoop currentRunLoop] removePort:self.port forMode:runLoopMode];
|
||||||
|
[self.connection unscheduleFromRunLoop:[NSRunLoop currentRunLoop] forMode:runLoopMode];
|
||||||
|
}
|
||||||
|
CFRunLoopStop([[NSRunLoop currentRunLoop] getCFRunLoop]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)responseString {
|
||||||
|
return [[[NSString alloc] initWithData:self.responseBody encoding:NSUTF8StringEncoding] autorelease];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - NSOperation
|
||||||
|
|
||||||
|
- (BOOL)isReady {
|
||||||
|
return self.state == AFHTTPOperationReadyState;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isExecuting {
|
||||||
|
return self.state == AFHTTPOperationExecutingState;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isFinished {
|
||||||
|
return self.state == AFHTTPOperationFinishedState || self.isCancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isCancelled {
|
||||||
|
return self.state == AFHTTPOperationCancelledState;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isConcurrent {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)start {
|
||||||
|
if (self.isFinished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.state = AFHTTPOperationExecutingState;
|
||||||
|
|
||||||
|
self.connection = [[[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO] autorelease];
|
||||||
|
self.port = [NSPort port];
|
||||||
|
|
||||||
|
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
|
||||||
|
for (NSString *runLoopMode in self.runLoopModes) {
|
||||||
|
[self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
|
||||||
|
[runLoop addPort:self.port forMode:runLoopMode];
|
||||||
|
}
|
||||||
|
|
||||||
|
[self.connection start];
|
||||||
|
|
||||||
|
[runLoop run];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)cancel {
|
||||||
|
self.state = AFHTTPOperationCancelledState;
|
||||||
|
|
||||||
|
[self.connection cancel];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - AFHTTPRequestOperation
|
||||||
|
|
||||||
|
- (void)finish {
|
||||||
|
if (self.isCancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.completion) {
|
||||||
|
self.completion(self.request, self.response, self.responseBody, self.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - NSURLConnection
|
||||||
|
|
||||||
|
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
|
||||||
|
self.response = (NSHTTPURLResponse *)response;
|
||||||
|
NSUInteger contentLength = MIN(MAX(abs(response.expectedContentLength), 1024), 1024 * 1024 * 8);
|
||||||
|
|
||||||
|
self.dataAccumulator = [NSMutableData dataWithCapacity:contentLength];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
|
||||||
|
[self.dataAccumulator appendData:data];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
|
||||||
|
self.state = AFHTTPOperationFinishedState;
|
||||||
|
|
||||||
|
self.responseBody = [NSData dataWithData:self.dataAccumulator];
|
||||||
|
self.dataAccumulator = nil;
|
||||||
|
|
||||||
|
[self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:YES modes:[self.runLoopModes allObjects]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
|
||||||
|
self.state = AFHTTPOperationFinishedState;
|
||||||
|
|
||||||
|
self.error = error;
|
||||||
|
|
||||||
|
[self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:YES modes:[self.runLoopModes allObjects]];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// AFImageRequest.h
|
// AFImageCache.h
|
||||||
//
|
//
|
||||||
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
|
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
|
||||||
//
|
//
|
||||||
|
|
@ -20,20 +20,20 @@
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
#import "AFImageRequestOperation.h"
|
#import "AFImageRequestOperation.h"
|
||||||
|
|
||||||
@protocol AFImageRequester
|
@interface AFImageCache : NSCache
|
||||||
@required
|
|
||||||
- (void)setImageURLString:(NSString *)urlString;
|
|
||||||
- (void)setImageURLString:(NSString *)urlString options:(AFImageRequestOptions)options;
|
|
||||||
@optional
|
|
||||||
@property (nonatomic, copy) NSString *imageURLString;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface AFImageRequest : NSObject
|
+ (id)sharedImageCache;
|
||||||
|
|
||||||
|
- (UIImage *)cachedImageForRequest:(NSURLRequest *)urlRequest
|
||||||
|
imageSize:(CGSize)imageSize
|
||||||
|
options:(AFImageRequestOptions)options;
|
||||||
|
|
||||||
|
- (void)cacheImage:(UIImage *)image
|
||||||
|
forRequest:(NSURLRequest *)urlRequest
|
||||||
|
imageSize:(CGSize)imageSize
|
||||||
|
options:(AFImageRequestOptions)options;
|
||||||
|
|
||||||
+ (void)requestImageWithURLString:(NSString *)urlString options:(AFImageRequestOptions)options block:(void (^)(UIImage *image))block;
|
|
||||||
+ (void)requestImageWithURLString:(NSString *)urlString size:(CGSize)imageSize options:(AFImageRequestOptions)options block:(void (^)(UIImage *image))block;
|
|
||||||
+ (void)cancelImageRequestOperationsForURLString:(NSString *)urlString;
|
|
||||||
+ (void)cancelAllImageRequestOperations;
|
|
||||||
@end
|
@end
|
||||||
60
Example/AFImageCache.m
Normal file
60
Example/AFImageCache.m
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
// 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 * AFImageCacheKey(NSURLRequest *urlRequest, CGSize imageSize, AFImageRequestOptions options) {
|
||||||
|
return [[[urlRequest URL] absoluteString] stringByAppendingFormat:@"#%fx%f:%d", imageSize.width, imageSize.height, options];
|
||||||
|
}
|
||||||
|
|
||||||
|
@implementation AFImageCache
|
||||||
|
|
||||||
|
+ (id)sharedImageCache {
|
||||||
|
static NSCache *_sharedImageCache = nil;
|
||||||
|
|
||||||
|
if (!_sharedImageCache) {
|
||||||
|
_sharedImageCache = [[self alloc] init];
|
||||||
|
}
|
||||||
|
|
||||||
|
return _sharedImageCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIImage *)cachedImageForRequest:(NSURLRequest *)urlRequest
|
||||||
|
imageSize:(CGSize)imageSize
|
||||||
|
options:(AFImageRequestOptions)options
|
||||||
|
{
|
||||||
|
return [self objectForKey:AFImageCacheKey(urlRequest, imageSize, options)];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)cacheImage:(UIImage *)image
|
||||||
|
forRequest:(NSURLRequest *)urlRequest
|
||||||
|
imageSize:(CGSize)imageSize
|
||||||
|
options:(AFImageRequestOptions)options
|
||||||
|
{
|
||||||
|
if (!image) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[self setObject:image forKey:AFImageCacheKey(urlRequest, imageSize, options)];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
42
Example/AFImageRequestOperation.h
Normal file
42
Example/AFImageRequestOperation.h
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
// AFImageRequestOperation.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 <Foundation/Foundation.h>
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
#import "AFHTTPRequestOperation.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
AFImageRequestDefaultOptions = 0,
|
||||||
|
AFImageRequestRoundCorners = 1 << 1,
|
||||||
|
} AFImageRequestOptions;
|
||||||
|
|
||||||
|
@interface AFImageRequestOperation : AFHTTPRequestOperation
|
||||||
|
|
||||||
|
+ (id)operationWithRequest:(NSURLRequest *)urlRequest
|
||||||
|
success:(void (^)(UIImage *image))success;
|
||||||
|
|
||||||
|
+ (id)operationWithRequest:(NSURLRequest *)urlRequest
|
||||||
|
imageSize:(CGSize)imageSize
|
||||||
|
options:(AFImageRequestOptions)options
|
||||||
|
success:(void (^)(UIImage *image))success;
|
||||||
|
|
||||||
|
@end
|
||||||
88
Example/AFImageRequestOperation.m
Normal file
88
Example/AFImageRequestOperation.m
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
// AFImageRequestOperation.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 "AFImageRequestOperation.h"
|
||||||
|
#import "AFImageCache.h"
|
||||||
|
|
||||||
|
#import "UIImage+AFNetworking.h"
|
||||||
|
|
||||||
|
static CGFloat const kAFImageRequestJPEGQuality = 0.8;
|
||||||
|
static NSUInteger const kAFImageRequestMaximumResponseSize = 8 * 1024 * 1024;
|
||||||
|
|
||||||
|
static inline CGSize kAFImageRequestRoundedCornerRadii(CGSize imageSize) {
|
||||||
|
CGFloat dimension = fmaxf(imageSize.width, imageSize.height) * 0.1;
|
||||||
|
return CGSizeMake(dimension, dimension);
|
||||||
|
}
|
||||||
|
|
||||||
|
@implementation AFImageRequestOperation
|
||||||
|
|
||||||
|
+ (id)operationWithRequest:(NSURLRequest *)urlRequest
|
||||||
|
success:(void (^)(UIImage *image))success
|
||||||
|
{
|
||||||
|
return [self operationWithRequest:urlRequest imageSize:CGSizeZero options:AFImageRequestDefaultOptions success:success];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (id)operationWithRequest:(NSURLRequest *)urlRequest
|
||||||
|
imageSize:(CGSize)imageSize
|
||||||
|
options:(AFImageRequestOptions)options
|
||||||
|
success:(void (^)(UIImage *image))success
|
||||||
|
{
|
||||||
|
return [self operationWithRequest:urlRequest completion:^(NSURLRequest *request, NSHTTPURLResponse *response, NSData *data, NSError *error) {
|
||||||
|
UIImage *image = nil;
|
||||||
|
if ([[UIScreen mainScreen] scale] == 2.0) {
|
||||||
|
CGImageRef imageRef = [[UIImage imageWithData:data] CGImage];
|
||||||
|
image = [UIImage imageWithCGImage:imageRef scale:2.0 orientation:UIImageOrientationUp];
|
||||||
|
} else {
|
||||||
|
image = [UIImage imageWithData:data];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(CGSizeEqualToSize(image.size, imageSize) || CGSizeEqualToSize(imageSize, CGSizeZero))) {
|
||||||
|
image = [UIImage imageByScalingAndCroppingImage:image size:imageSize];
|
||||||
|
}
|
||||||
|
if ((options & AFImageRequestRoundCorners)) {
|
||||||
|
image = [UIImage imageByRoundingCornersOfImage:image corners:UIRectCornerAllCorners cornerRadii:kAFImageRequestRoundedCornerRadii(image.size)];
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
||||||
|
if (success) {
|
||||||
|
success(image);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
|
||||||
|
[[AFImageCache sharedImageCache] cacheImage:image forRequest:request imageSize:imageSize options:options];
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)initWithRequest:(NSURLRequest *)urlRequest {
|
||||||
|
self = [super initWithRequest:urlRequest];
|
||||||
|
if (!self) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.runLoopModes = [NSSet setWithObject:NSRunLoopCommonModes];
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// AFCallback.m
|
// AFJSONRequestOperation.h
|
||||||
//
|
//
|
||||||
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
|
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
|
||||||
//
|
//
|
||||||
|
|
@ -20,41 +20,21 @@
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
#import "AFCallback.h"
|
#import "AFHTTPRequestOperation.h"
|
||||||
|
|
||||||
@interface AFCallback ()
|
@interface AFJSONRequestOperation : AFHTTPRequestOperation
|
||||||
@property (readwrite, nonatomic, copy) id successBlock;
|
|
||||||
@property (readwrite, nonatomic, copy) id errorBlock;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation AFCallback
|
+ (id)operationWithRequest:(NSURLRequest *)urlRequest
|
||||||
@synthesize successBlock = _successBlock;
|
success:(void (^)(NSDictionary *JSON))success;
|
||||||
@synthesize errorBlock = _errorBlock;
|
|
||||||
|
|
||||||
+ (id)callbackWithSuccess:(id)success {
|
+ (id)operationWithRequest:(NSURLRequest *)urlRequest
|
||||||
return [self callbackWithSuccess:success error:nil];
|
success:(void (^)(NSDictionary *JSON))success
|
||||||
}
|
failure:(void (^)(NSError *error))failure;
|
||||||
|
|
||||||
+ (id)callbackWithSuccess:(id)success error:(id)error {
|
+ (id)operationWithRequest:(NSURLRequest *)urlRequest
|
||||||
id callback = [[[self alloc] init] autorelease];
|
acceptableStatusCodes:(NSIndexSet *)acceptableStatusCodes
|
||||||
[callback setSuccessBlock:success];
|
acceptableContentTypes:(NSSet *)acceptableContentTypes
|
||||||
[callback setErrorBlock:error];
|
success:(void (^)(NSDictionary *JSON))success
|
||||||
|
failure:(void (^)(NSError *error))failure;
|
||||||
return callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)init {
|
|
||||||
if ([self class] == [AFCallback class]) {
|
|
||||||
[NSException raise:NSInternalInconsistencyException format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [super init];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc {
|
|
||||||
[_successBlock release];
|
|
||||||
[_errorBlock release];
|
|
||||||
[super dealloc];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
75
Example/AFJSONRequestOperation.m
Normal file
75
Example/AFJSONRequestOperation.m
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
// AFJSONRequestOperation.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 "AFJSONRequestOperation.h"
|
||||||
|
#import "JSONKit.h"
|
||||||
|
|
||||||
|
@implementation AFJSONRequestOperation
|
||||||
|
|
||||||
|
+ (id)operationWithRequest:(NSURLRequest *)urlRequest
|
||||||
|
success:(void (^)(NSDictionary *JSON))success
|
||||||
|
{
|
||||||
|
return [self operationWithRequest:urlRequest success:success failure:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (id)operationWithRequest:(NSURLRequest *)urlRequest
|
||||||
|
success:(void (^)(NSDictionary *JSON))success
|
||||||
|
failure:(void (^)(NSError *error))failure
|
||||||
|
{
|
||||||
|
NSIndexSet *acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
|
||||||
|
NSSet *acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"application/x-javascript", @"text/javascript", @"text/x-javascript", @"text/x-json", @"text/plain", nil];
|
||||||
|
|
||||||
|
return [self operationWithRequest:urlRequest acceptableStatusCodes:acceptableStatusCodes acceptableContentTypes:acceptableContentTypes success:success failure:failure];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (id)operationWithRequest:(NSURLRequest *)urlRequest
|
||||||
|
acceptableStatusCodes:(NSIndexSet *)acceptableStatusCodes
|
||||||
|
acceptableContentTypes:(NSSet *)acceptableContentTypes
|
||||||
|
success:(void (^)(NSDictionary *JSON))success
|
||||||
|
failure:(void (^)(NSError *error))failure
|
||||||
|
{
|
||||||
|
return [self operationWithRequest:urlRequest completion:^(NSURLRequest *request, NSHTTPURLResponse *response, NSData *data, NSError *error) {
|
||||||
|
BOOL statusCodeAcceptable = [acceptableStatusCodes containsIndex:[response statusCode]];
|
||||||
|
BOOL contentTypeAcceptable = [acceptableContentTypes containsObject:[response MIMEType]];
|
||||||
|
if (!statusCodeAcceptable || !contentTypeAcceptable) {
|
||||||
|
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
||||||
|
[userInfo setValue:[NSHTTPURLResponse localizedStringForStatusCode:[response statusCode]] forKey:NSLocalizedDescriptionKey];
|
||||||
|
[userInfo setValue:[request URL] forKey:NSURLErrorFailingURLErrorKey];
|
||||||
|
|
||||||
|
error = [[[NSError alloc] initWithDomain:NSURLErrorDomain code:[response statusCode] userInfo:userInfo] autorelease];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
if (failure) {
|
||||||
|
failure(error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NSDictionary *JSON = [[JSONDecoder decoder] objectWithData:data error:&error];
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
success(JSON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// AFCallback.h
|
// AFNetworkActivityIndicatorManager.h
|
||||||
//
|
//
|
||||||
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
|
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
|
||||||
//
|
//
|
||||||
|
|
@ -22,15 +22,14 @@
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
@protocol AFCallback <NSObject>
|
@interface AFNetworkActivityIndicatorManager : NSObject {
|
||||||
+ (id)callbackWithSuccess:(id)success;
|
|
||||||
+ (id)callbackWithSuccess:(id)success error:(id)error;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface AFCallback : NSObject <AFCallback> {
|
|
||||||
@private
|
@private
|
||||||
id _successBlock;
|
NSUInteger _activityCount;
|
||||||
id _errorBlock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (AFNetworkActivityIndicatorManager *)sharedManager;
|
||||||
|
|
||||||
|
- (void)startAnimating;
|
||||||
|
- (void)stopAnimating;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
57
Example/AFNetworkActivityIndicatorManager.m
Normal file
57
Example/AFNetworkActivityIndicatorManager.m
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
// AFNetworkActivityIndicatorManager.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 "AFNetworkActivityIndicatorManager.h"
|
||||||
|
|
||||||
|
@interface AFNetworkActivityIndicatorManager ()
|
||||||
|
@property (readwrite, nonatomic, assign) NSUInteger activityCount;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation AFNetworkActivityIndicatorManager
|
||||||
|
@synthesize activityCount = _activityCount;
|
||||||
|
|
||||||
|
+ (AFNetworkActivityIndicatorManager *)sharedManager {
|
||||||
|
static AFNetworkActivityIndicatorManager *_sharedManager = nil;
|
||||||
|
if (!_sharedManager) {
|
||||||
|
_sharedManager = [[AFNetworkActivityIndicatorManager alloc] init];
|
||||||
|
}
|
||||||
|
|
||||||
|
return _sharedManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setActivityCount:(NSUInteger)activityCount {
|
||||||
|
[self willChangeValueForKey:@"activityCount"];
|
||||||
|
_activityCount = MAX(activityCount, 0);
|
||||||
|
[self didChangeValueForKey:@"activityCount"];
|
||||||
|
|
||||||
|
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:self.activityCount > 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)startAnimating {
|
||||||
|
self.activityCount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)stopAnimating {
|
||||||
|
self.activityCount -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
@ -7,20 +7,20 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
F874B5B113E0937400B28E3E /* AFHTTPRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F874B5A413E0937400B28E3E /* AFHTTPRequestOperation.m */; };
|
||||||
|
F874B5B213E0937400B28E3E /* AFJSONRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F874B5A613E0937400B28E3E /* AFJSONRequestOperation.m */; };
|
||||||
|
F874B5B313E0937400B28E3E /* AFImageRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F874B5A813E0937400B28E3E /* AFImageRequestOperation.m */; };
|
||||||
|
F874B5B413E0937400B28E3E /* AFImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = F874B5AA13E0937400B28E3E /* AFImageCache.m */; };
|
||||||
|
F874B5B513E0937400B28E3E /* AFRestClient.m in Sources */ = {isa = PBXBuildFile; fileRef = F874B5AC13E0937400B28E3E /* AFRestClient.m */; };
|
||||||
|
F874B5B613E0937400B28E3E /* UIImage+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = F874B5AE13E0937400B28E3E /* UIImage+AFNetworking.m */; };
|
||||||
|
F874B5B713E0937400B28E3E /* UIImageView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = F874B5B013E0937400B28E3E /* UIImageView+AFNetworking.m */; };
|
||||||
|
F874B5BA13E096C400B28E3E /* AFNetworkActivityIndicatorManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F874B5B913E096C400B28E3E /* AFNetworkActivityIndicatorManager.m */; };
|
||||||
F8D25D191396A9D300CF3BD6 /* placeholder-stamp.png in Resources */ = {isa = PBXBuildFile; fileRef = F8D25D171396A9D300CF3BD6 /* placeholder-stamp.png */; };
|
F8D25D191396A9D300CF3BD6 /* placeholder-stamp.png in Resources */ = {isa = PBXBuildFile; fileRef = F8D25D171396A9D300CF3BD6 /* placeholder-stamp.png */; };
|
||||||
F8D25D1A1396A9D300CF3BD6 /* placeholder-stamp@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F8D25D181396A9D300CF3BD6 /* placeholder-stamp@2x.png */; };
|
F8D25D1A1396A9D300CF3BD6 /* placeholder-stamp@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F8D25D181396A9D300CF3BD6 /* placeholder-stamp@2x.png */; };
|
||||||
F8DA09D21396ABED0057D0CC /* AFGowallaAPIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D25D1D1396A9DE00CF3BD6 /* AFGowallaAPIClient.m */; };
|
F8DA09D21396ABED0057D0CC /* AFGowallaAPIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D25D1D1396A9DE00CF3BD6 /* AFGowallaAPIClient.m */; };
|
||||||
F8DA09D31396ABED0057D0CC /* AFImageRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D25D1E1396A9DE00CF3BD6 /* AFImageRequest.m */; };
|
|
||||||
F8DA09D41396ABED0057D0CC /* NearbySpotsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F8DA09C81396AB690057D0CC /* NearbySpotsViewController.m */; };
|
F8DA09D41396ABED0057D0CC /* NearbySpotsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F8DA09C81396AB690057D0CC /* NearbySpotsViewController.m */; };
|
||||||
F8DA09D51396ABED0057D0CC /* Spot.m in Sources */ = {isa = PBXBuildFile; fileRef = F8DA09CB1396AB690057D0CC /* Spot.m */; };
|
F8DA09D51396ABED0057D0CC /* Spot.m in Sources */ = {isa = PBXBuildFile; fileRef = F8DA09CB1396AB690057D0CC /* Spot.m */; };
|
||||||
F8DA09D61396ABED0057D0CC /* SpotTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F8DA09CE1396AB690057D0CC /* SpotTableViewCell.m */; };
|
F8DA09D61396ABED0057D0CC /* SpotTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F8DA09CE1396AB690057D0CC /* SpotTableViewCell.m */; };
|
||||||
F8DA09D91396ABED0057D0CC /* AFCallback.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D25CF01396A98600CF3BD6 /* AFCallback.m */; };
|
|
||||||
F8DA09DA1396ABED0057D0CC /* AFHTTPOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D25CF11396A98600CF3BD6 /* AFHTTPOperation.m */; };
|
|
||||||
F8DA09DB1396ABED0057D0CC /* AFImageRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D25CF21396A98600CF3BD6 /* AFImageRequestOperation.m */; };
|
|
||||||
F8DA09DC1396ABED0057D0CC /* AFRestClient.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D25CF31396A98600CF3BD6 /* AFRestClient.m */; };
|
|
||||||
F8DA09DE1396ABED0057D0CC /* UIImage+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D25CF51396A98600CF3BD6 /* UIImage+AFNetworking.m */; };
|
|
||||||
F8DA09DF1396ABED0057D0CC /* QHTTPOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D25D0A1396A9A900CF3BD6 /* QHTTPOperation.m */; };
|
|
||||||
F8DA09E01396ABED0057D0CC /* QRunLoopOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D25D0C1396A9A900CF3BD6 /* QRunLoopOperation.m */; };
|
|
||||||
F8DA09E11396ABED0057D0CC /* JSONKit.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D25D111396A9C400CF3BD6 /* JSONKit.m */; };
|
F8DA09E11396ABED0057D0CC /* JSONKit.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D25D111396A9C400CF3BD6 /* JSONKit.m */; };
|
||||||
F8DA09E21396ABED0057D0CC /* TTTLocationFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D25D141396A9C400CF3BD6 /* TTTLocationFormatter.m */; };
|
F8DA09E21396ABED0057D0CC /* TTTLocationFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D25D141396A9C400CF3BD6 /* TTTLocationFormatter.m */; };
|
||||||
F8DA09E41396AC040057D0CC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F8DA09E31396AC040057D0CC /* main.m */; };
|
F8DA09E41396AC040057D0CC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F8DA09E31396AC040057D0CC /* main.m */; };
|
||||||
|
|
@ -32,20 +32,22 @@
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
F8D25CEA1396A98600CF3BD6 /* AFCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFCallback.h; path = ../AFNetworking/AFCallback.h; sourceTree = "<group>"; };
|
F874B5A313E0937400B28E3E /* AFHTTPRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFHTTPRequestOperation.h; sourceTree = "<group>"; };
|
||||||
F8D25CEB1396A98600CF3BD6 /* AFHTTPOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFHTTPOperation.h; path = ../AFNetworking/AFHTTPOperation.h; sourceTree = "<group>"; };
|
F874B5A413E0937400B28E3E /* AFHTTPRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFHTTPRequestOperation.m; sourceTree = "<group>"; };
|
||||||
F8D25CEC1396A98600CF3BD6 /* AFImageRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFImageRequestOperation.h; path = ../AFNetworking/AFImageRequestOperation.h; sourceTree = "<group>"; };
|
F874B5A513E0937400B28E3E /* AFJSONRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFJSONRequestOperation.h; sourceTree = "<group>"; };
|
||||||
F8D25CED1396A98600CF3BD6 /* AFRestClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFRestClient.h; path = ../AFNetworking/AFRestClient.h; sourceTree = "<group>"; };
|
F874B5A613E0937400B28E3E /* AFJSONRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFJSONRequestOperation.m; sourceTree = "<group>"; };
|
||||||
F8D25CEF1396A98600CF3BD6 /* UIImage+AFNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImage+AFNetworking.h"; path = "../AFNetworking/UIImage+AFNetworking.h"; sourceTree = "<group>"; };
|
F874B5A713E0937400B28E3E /* AFImageRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFImageRequestOperation.h; sourceTree = "<group>"; };
|
||||||
F8D25CF01396A98600CF3BD6 /* AFCallback.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFCallback.m; path = ../AFNetworking/AFCallback.m; sourceTree = "<group>"; };
|
F874B5A813E0937400B28E3E /* AFImageRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFImageRequestOperation.m; sourceTree = "<group>"; };
|
||||||
F8D25CF11396A98600CF3BD6 /* AFHTTPOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFHTTPOperation.m; path = ../AFNetworking/AFHTTPOperation.m; sourceTree = "<group>"; };
|
F874B5A913E0937400B28E3E /* AFImageCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFImageCache.h; sourceTree = "<group>"; };
|
||||||
F8D25CF21396A98600CF3BD6 /* AFImageRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFImageRequestOperation.m; path = ../AFNetworking/AFImageRequestOperation.m; sourceTree = "<group>"; };
|
F874B5AA13E0937400B28E3E /* AFImageCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFImageCache.m; sourceTree = "<group>"; };
|
||||||
F8D25CF31396A98600CF3BD6 /* AFRestClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFRestClient.m; path = ../AFNetworking/AFRestClient.m; sourceTree = "<group>"; };
|
F874B5AB13E0937400B28E3E /* AFRestClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFRestClient.h; sourceTree = "<group>"; };
|
||||||
F8D25CF51396A98600CF3BD6 /* UIImage+AFNetworking.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIImage+AFNetworking.m"; path = "../AFNetworking/UIImage+AFNetworking.m"; sourceTree = "<group>"; };
|
F874B5AC13E0937400B28E3E /* AFRestClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFRestClient.m; sourceTree = "<group>"; };
|
||||||
F8D25D091396A9A900CF3BD6 /* QHTTPOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QHTTPOperation.h; sourceTree = "<group>"; };
|
F874B5AD13E0937400B28E3E /* UIImage+AFNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+AFNetworking.h"; sourceTree = "<group>"; };
|
||||||
F8D25D0A1396A9A900CF3BD6 /* QHTTPOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QHTTPOperation.m; sourceTree = "<group>"; };
|
F874B5AE13E0937400B28E3E /* UIImage+AFNetworking.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+AFNetworking.m"; sourceTree = "<group>"; };
|
||||||
F8D25D0B1396A9A900CF3BD6 /* QRunLoopOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QRunLoopOperation.h; sourceTree = "<group>"; };
|
F874B5AF13E0937400B28E3E /* UIImageView+AFNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImageView+AFNetworking.h"; sourceTree = "<group>"; };
|
||||||
F8D25D0C1396A9A900CF3BD6 /* QRunLoopOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QRunLoopOperation.m; sourceTree = "<group>"; };
|
F874B5B013E0937400B28E3E /* UIImageView+AFNetworking.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImageView+AFNetworking.m"; sourceTree = "<group>"; };
|
||||||
|
F874B5B813E096C400B28E3E /* AFNetworkActivityIndicatorManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFNetworkActivityIndicatorManager.h; sourceTree = "<group>"; };
|
||||||
|
F874B5B913E096C400B28E3E /* AFNetworkActivityIndicatorManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFNetworkActivityIndicatorManager.m; sourceTree = "<group>"; };
|
||||||
F8D25D101396A9C400CF3BD6 /* JSONKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONKit.h; sourceTree = "<group>"; };
|
F8D25D101396A9C400CF3BD6 /* JSONKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONKit.h; sourceTree = "<group>"; };
|
||||||
F8D25D111396A9C400CF3BD6 /* JSONKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONKit.m; sourceTree = "<group>"; };
|
F8D25D111396A9C400CF3BD6 /* JSONKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONKit.m; sourceTree = "<group>"; };
|
||||||
F8D25D131396A9C400CF3BD6 /* TTTLocationFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTTLocationFormatter.h; sourceTree = "<group>"; };
|
F8D25D131396A9C400CF3BD6 /* TTTLocationFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTTLocationFormatter.h; sourceTree = "<group>"; };
|
||||||
|
|
@ -53,9 +55,7 @@
|
||||||
F8D25D171396A9D300CF3BD6 /* placeholder-stamp.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "placeholder-stamp.png"; path = "Images/placeholder-stamp.png"; sourceTree = SOURCE_ROOT; };
|
F8D25D171396A9D300CF3BD6 /* placeholder-stamp.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "placeholder-stamp.png"; path = "Images/placeholder-stamp.png"; sourceTree = SOURCE_ROOT; };
|
||||||
F8D25D181396A9D300CF3BD6 /* placeholder-stamp@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "placeholder-stamp@2x.png"; path = "Images/placeholder-stamp@2x.png"; sourceTree = SOURCE_ROOT; };
|
F8D25D181396A9D300CF3BD6 /* placeholder-stamp@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "placeholder-stamp@2x.png"; path = "Images/placeholder-stamp@2x.png"; sourceTree = SOURCE_ROOT; };
|
||||||
F8D25D1B1396A9DE00CF3BD6 /* AFGowallaAPIClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFGowallaAPIClient.h; path = Classes/AFGowallaAPIClient.h; sourceTree = "<group>"; };
|
F8D25D1B1396A9DE00CF3BD6 /* AFGowallaAPIClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFGowallaAPIClient.h; path = Classes/AFGowallaAPIClient.h; sourceTree = "<group>"; };
|
||||||
F8D25D1C1396A9DE00CF3BD6 /* AFImageRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFImageRequest.h; path = Classes/AFImageRequest.h; sourceTree = "<group>"; };
|
|
||||||
F8D25D1D1396A9DE00CF3BD6 /* AFGowallaAPIClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFGowallaAPIClient.m; path = Classes/AFGowallaAPIClient.m; sourceTree = "<group>"; };
|
F8D25D1D1396A9DE00CF3BD6 /* AFGowallaAPIClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFGowallaAPIClient.m; path = Classes/AFGowallaAPIClient.m; sourceTree = "<group>"; };
|
||||||
F8D25D1E1396A9DE00CF3BD6 /* AFImageRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFImageRequest.m; path = Classes/AFImageRequest.m; sourceTree = "<group>"; };
|
|
||||||
F8DA09C71396AB690057D0CC /* NearbySpotsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NearbySpotsViewController.h; sourceTree = "<group>"; };
|
F8DA09C71396AB690057D0CC /* NearbySpotsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NearbySpotsViewController.h; sourceTree = "<group>"; };
|
||||||
F8DA09C81396AB690057D0CC /* NearbySpotsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NearbySpotsViewController.m; sourceTree = "<group>"; };
|
F8DA09C81396AB690057D0CC /* NearbySpotsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NearbySpotsViewController.m; sourceTree = "<group>"; };
|
||||||
F8DA09CA1396AB690057D0CC /* Spot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Spot.h; sourceTree = "<group>"; };
|
F8DA09CA1396AB690057D0CC /* Spot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Spot.h; sourceTree = "<group>"; };
|
||||||
|
|
@ -92,18 +92,6 @@
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
F8D25D081396A9A900CF3BD6 /* QHTTPOperation */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
F8D25D091396A9A900CF3BD6 /* QHTTPOperation.h */,
|
|
||||||
F8D25D0A1396A9A900CF3BD6 /* QHTTPOperation.m */,
|
|
||||||
F8D25D0B1396A9A900CF3BD6 /* QRunLoopOperation.h */,
|
|
||||||
F8D25D0C1396A9A900CF3BD6 /* QRunLoopOperation.m */,
|
|
||||||
);
|
|
||||||
name = QHTTPOperation;
|
|
||||||
path = ../AFNetworking/QHTTPOperation;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
F8D25D0F1396A9C400CF3BD6 /* JSONKit */ = {
|
F8D25D0F1396A9C400CF3BD6 /* JSONKit */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
|
@ -216,7 +204,6 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
F8E469941395744600DB05C8 /* Alamofire */,
|
F8E469941395744600DB05C8 /* Alamofire */,
|
||||||
F8D25D081396A9A900CF3BD6 /* QHTTPOperation */,
|
|
||||||
F8D25D0F1396A9C400CF3BD6 /* JSONKit */,
|
F8D25D0F1396A9C400CF3BD6 /* JSONKit */,
|
||||||
F8D25D121396A9C400CF3BD6 /* TTT */,
|
F8D25D121396A9C400CF3BD6 /* TTT */,
|
||||||
);
|
);
|
||||||
|
|
@ -226,16 +213,22 @@
|
||||||
F8E469941395744600DB05C8 /* Alamofire */ = {
|
F8E469941395744600DB05C8 /* Alamofire */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
F8D25CEA1396A98600CF3BD6 /* AFCallback.h */,
|
F874B5A313E0937400B28E3E /* AFHTTPRequestOperation.h */,
|
||||||
F8D25CF01396A98600CF3BD6 /* AFCallback.m */,
|
F874B5A413E0937400B28E3E /* AFHTTPRequestOperation.m */,
|
||||||
F8D25CEB1396A98600CF3BD6 /* AFHTTPOperation.h */,
|
F874B5A513E0937400B28E3E /* AFJSONRequestOperation.h */,
|
||||||
F8D25CF11396A98600CF3BD6 /* AFHTTPOperation.m */,
|
F874B5A613E0937400B28E3E /* AFJSONRequestOperation.m */,
|
||||||
F8D25CEC1396A98600CF3BD6 /* AFImageRequestOperation.h */,
|
F874B5AB13E0937400B28E3E /* AFRestClient.h */,
|
||||||
F8D25CF21396A98600CF3BD6 /* AFImageRequestOperation.m */,
|
F874B5AC13E0937400B28E3E /* AFRestClient.m */,
|
||||||
F8D25CED1396A98600CF3BD6 /* AFRestClient.h */,
|
F874B5A713E0937400B28E3E /* AFImageRequestOperation.h */,
|
||||||
F8D25CF31396A98600CF3BD6 /* AFRestClient.m */,
|
F874B5A813E0937400B28E3E /* AFImageRequestOperation.m */,
|
||||||
F8D25CEF1396A98600CF3BD6 /* UIImage+AFNetworking.h */,
|
F874B5A913E0937400B28E3E /* AFImageCache.h */,
|
||||||
F8D25CF51396A98600CF3BD6 /* UIImage+AFNetworking.m */,
|
F874B5AA13E0937400B28E3E /* AFImageCache.m */,
|
||||||
|
F874B5B813E096C400B28E3E /* AFNetworkActivityIndicatorManager.h */,
|
||||||
|
F874B5B913E096C400B28E3E /* AFNetworkActivityIndicatorManager.m */,
|
||||||
|
F874B5AD13E0937400B28E3E /* UIImage+AFNetworking.h */,
|
||||||
|
F874B5AE13E0937400B28E3E /* UIImage+AFNetworking.m */,
|
||||||
|
F874B5AF13E0937400B28E3E /* UIImageView+AFNetworking.h */,
|
||||||
|
F874B5B013E0937400B28E3E /* UIImageView+AFNetworking.m */,
|
||||||
);
|
);
|
||||||
name = Alamofire;
|
name = Alamofire;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
|
@ -245,8 +238,6 @@
|
||||||
children = (
|
children = (
|
||||||
F8D25D1B1396A9DE00CF3BD6 /* AFGowallaAPIClient.h */,
|
F8D25D1B1396A9DE00CF3BD6 /* AFGowallaAPIClient.h */,
|
||||||
F8D25D1D1396A9DE00CF3BD6 /* AFGowallaAPIClient.m */,
|
F8D25D1D1396A9DE00CF3BD6 /* AFGowallaAPIClient.m */,
|
||||||
F8D25D1C1396A9DE00CF3BD6 /* AFImageRequest.h */,
|
|
||||||
F8D25D1E1396A9DE00CF3BD6 /* AFImageRequest.m */,
|
|
||||||
);
|
);
|
||||||
name = "Networking Extensions";
|
name = "Networking Extensions";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
|
@ -287,6 +278,7 @@
|
||||||
F8E469571395739C00DB05C8 /* Project object */ = {
|
F8E469571395739C00DB05C8 /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
|
LastUpgradeCheck = 0420;
|
||||||
ORGANIZATIONNAME = Gowalla;
|
ORGANIZATIONNAME = Gowalla;
|
||||||
};
|
};
|
||||||
buildConfigurationList = F8E4695A1395739C00DB05C8 /* Build configuration list for PBXProject "AFNetworking Example" */;
|
buildConfigurationList = F8E4695A1395739C00DB05C8 /* Build configuration list for PBXProject "AFNetworking Example" */;
|
||||||
|
|
@ -324,21 +316,21 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
F8DA09D21396ABED0057D0CC /* AFGowallaAPIClient.m in Sources */,
|
F8DA09D21396ABED0057D0CC /* AFGowallaAPIClient.m in Sources */,
|
||||||
F8DA09D31396ABED0057D0CC /* AFImageRequest.m in Sources */,
|
|
||||||
F8DA09D41396ABED0057D0CC /* NearbySpotsViewController.m in Sources */,
|
F8DA09D41396ABED0057D0CC /* NearbySpotsViewController.m in Sources */,
|
||||||
F8DA09D51396ABED0057D0CC /* Spot.m in Sources */,
|
F8DA09D51396ABED0057D0CC /* Spot.m in Sources */,
|
||||||
F8DA09D61396ABED0057D0CC /* SpotTableViewCell.m in Sources */,
|
F8DA09D61396ABED0057D0CC /* SpotTableViewCell.m in Sources */,
|
||||||
F8DA09D91396ABED0057D0CC /* AFCallback.m in Sources */,
|
|
||||||
F8DA09DA1396ABED0057D0CC /* AFHTTPOperation.m in Sources */,
|
|
||||||
F8DA09DB1396ABED0057D0CC /* AFImageRequestOperation.m in Sources */,
|
|
||||||
F8DA09DC1396ABED0057D0CC /* AFRestClient.m in Sources */,
|
|
||||||
F8DA09DE1396ABED0057D0CC /* UIImage+AFNetworking.m in Sources */,
|
|
||||||
F8DA09DF1396ABED0057D0CC /* QHTTPOperation.m in Sources */,
|
|
||||||
F8DA09E01396ABED0057D0CC /* QRunLoopOperation.m in Sources */,
|
|
||||||
F8DA09E11396ABED0057D0CC /* JSONKit.m in Sources */,
|
F8DA09E11396ABED0057D0CC /* JSONKit.m in Sources */,
|
||||||
F8DA09E21396ABED0057D0CC /* TTTLocationFormatter.m in Sources */,
|
F8DA09E21396ABED0057D0CC /* TTTLocationFormatter.m in Sources */,
|
||||||
F8DA09E41396AC040057D0CC /* main.m in Sources */,
|
F8DA09E41396AC040057D0CC /* main.m in Sources */,
|
||||||
F8DA09E81396AC220057D0CC /* AppDelegate.m in Sources */,
|
F8DA09E81396AC220057D0CC /* AppDelegate.m in Sources */,
|
||||||
|
F874B5B113E0937400B28E3E /* AFHTTPRequestOperation.m in Sources */,
|
||||||
|
F874B5B213E0937400B28E3E /* AFJSONRequestOperation.m in Sources */,
|
||||||
|
F874B5B313E0937400B28E3E /* AFImageRequestOperation.m in Sources */,
|
||||||
|
F874B5B413E0937400B28E3E /* AFImageCache.m in Sources */,
|
||||||
|
F874B5B513E0937400B28E3E /* AFRestClient.m in Sources */,
|
||||||
|
F874B5B613E0937400B28E3E /* UIImage+AFNetworking.m in Sources */,
|
||||||
|
F874B5B713E0937400B28E3E /* UIImageView+AFNetworking.m in Sources */,
|
||||||
|
F874B5BA13E096C400B28E3E /* AFNetworkActivityIndicatorManager.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -2,4 +2,20 @@
|
||||||
<Bucket
|
<Bucket
|
||||||
type = "1"
|
type = "1"
|
||||||
version = "1.0">
|
version = "1.0">
|
||||||
|
<FileBreakpoints>
|
||||||
|
<FileBreakpoint
|
||||||
|
shouldBeEnabled = "No"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
isPathRelative = "0"
|
||||||
|
filePath = "/Users/mattt/Code/Objective-C/AFNetworking/AFNetworking/AFImageRequestOperation.m"
|
||||||
|
timestampString = "332372128.476073"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "86"
|
||||||
|
endingLineNumber = "86"
|
||||||
|
landmarkName = "-finish"
|
||||||
|
landmarkType = "5">
|
||||||
|
</FileBreakpoint>
|
||||||
|
</FileBreakpoints>
|
||||||
</Bucket>
|
</Bucket>
|
||||||
|
|
|
||||||
|
|
@ -21,27 +21,37 @@
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import "AFHTTPOperation.h"
|
#import "AFHTTPRequestOperation.h"
|
||||||
|
|
||||||
@protocol AFRestClient <NSObject>
|
@protocol AFRestClient <NSObject>
|
||||||
@required
|
|
||||||
+ (NSURL *)baseURL;
|
+ (NSURL *)baseURL;
|
||||||
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters;
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface AFRestClient : NSObject <AFRestClient>
|
@interface AFRestClient : NSObject <AFRestClient> {
|
||||||
|
@protected
|
||||||
|
NSMutableDictionary *_defaultHeaders;
|
||||||
|
NSOperationQueue *_operationQueue;
|
||||||
|
}
|
||||||
|
|
||||||
- (NSString *)defaultValueForHeader:(NSString *)header;
|
- (NSString *)defaultValueForHeader:(NSString *)header;
|
||||||
- (void)setDefaultHeader:(NSString *)header value:(NSString *)value;
|
- (void)setDefaultHeader:(NSString *)header value:(NSString *)value;
|
||||||
- (void)setAuthorizationHeaderWithToken:(NSString *)token;
|
- (void)setAuthorizationHeaderWithToken:(NSString *)token;
|
||||||
- (void)clearAuthorizationHeader;
|
- (void)clearAuthorizationHeader;
|
||||||
|
|
||||||
- (void)getPath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback;
|
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters;
|
||||||
- (void)postPath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback;
|
- (void)enqueueHTTPOperationWithRequest:(NSURLRequest *)request success:(void (^)(NSDictionary *response))success failure:(void (^)(NSError *error))failure;
|
||||||
- (void)putPath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback;
|
|
||||||
- (void)deletePath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback;
|
|
||||||
|
|
||||||
- (void)enqueueHTTPOperationWithRequest:(NSURLRequest *)request callback:(AFHTTPOperationCallback *)callback;
|
- (void)getPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(NSDictionary *response))success;
|
||||||
- (void)enqueueHTTPOperation:(AFHTTPOperation *)operation;
|
- (void)getPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(NSDictionary *response))success failure:(void (^)(NSError *error))failure;
|
||||||
|
|
||||||
|
- (void)postPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(NSDictionary *response))success;
|
||||||
|
- (void)postPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(NSDictionary *response))success failure:(void (^)(NSError *error))failure;
|
||||||
|
|
||||||
|
- (void)putPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(NSDictionary *response))success;
|
||||||
|
- (void)putPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(NSDictionary *response))success failure:(void (^)(NSError *error))failure;
|
||||||
|
|
||||||
|
- (void)deletePath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(NSDictionary *response))success;
|
||||||
|
- (void)deletePath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(NSDictionary *response))success failure:(void (^)(NSError *error))failure;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#pragma mark - NSString + AFRestClient
|
#pragma mark - NSString + AFRestClient
|
||||||
|
|
@ -21,13 +21,15 @@
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
#import "AFRestClient.h"
|
#import "AFRestClient.h"
|
||||||
#import "AFHTTPOperation.h"
|
#import "AFJSONRequestOperation.h"
|
||||||
|
|
||||||
static NSStringEncoding const kAFRestClientStringEncoding = NSUTF8StringEncoding;
|
static NSStringEncoding const kAFRestClientStringEncoding = NSUTF8StringEncoding;
|
||||||
|
|
||||||
@interface AFRestClient ()
|
@interface AFRestClient ()
|
||||||
@property (readwrite, nonatomic, retain) NSMutableDictionary *defaultHeaders;
|
@property (readwrite, nonatomic, retain) NSMutableDictionary *defaultHeaders;
|
||||||
@property (readwrite, nonatomic, retain) NSOperationQueue *operationQueue;
|
@property (readwrite, nonatomic, retain) NSOperationQueue *operationQueue;
|
||||||
|
|
||||||
|
- (void)enqueueHTTPOperationWithRequest:(NSURLRequest *)request success:(void (^)(NSDictionary *response))success failure:(void (^)(NSError *error))failure;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation AFRestClient
|
@implementation AFRestClient
|
||||||
|
|
@ -78,36 +80,6 @@ static NSStringEncoding const kAFRestClientStringEncoding = NSUTF8StringEncoding
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters {
|
|
||||||
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
|
|
||||||
NSMutableDictionary *headers = [NSMutableDictionary dictionaryWithDictionary:_defaultHeaders];
|
|
||||||
NSURL *url = nil;
|
|
||||||
|
|
||||||
NSMutableArray *mutableParameterComponents = [NSMutableArray array];
|
|
||||||
for (id key in [parameters allKeys]) {
|
|
||||||
NSString *component = [NSString stringWithFormat:@"%@=%@", [key urlEncodedStringWithEncoding:kAFRestClientStringEncoding], [[parameters valueForKey:key] urlEncodedStringWithEncoding:kAFRestClientStringEncoding]];
|
|
||||||
[mutableParameterComponents addObject:component];
|
|
||||||
}
|
|
||||||
NSString *queryString = [mutableParameterComponents componentsJoinedByString:@"&"];
|
|
||||||
|
|
||||||
if ([method isEqualToString:@"GET"]) {
|
|
||||||
path = [path stringByAppendingFormat:[path rangeOfString:@"?"].location == NSNotFound ? @"?%@" : @"&%@", queryString];
|
|
||||||
url = [NSURL URLWithString:path relativeToURL:[[self class] baseURL]];
|
|
||||||
} else {
|
|
||||||
url = [NSURL URLWithString:path relativeToURL:[[self class] baseURL]];
|
|
||||||
NSString *charset = (NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(kAFRestClientStringEncoding));
|
|
||||||
[headers setObject:[NSString stringWithFormat:@"application/x-www-form-urlencoded; charset=%@", charset] forKey:@"Content-Type"];
|
|
||||||
[request setHTTPBody:[queryString dataUsingEncoding:NSUTF8StringEncoding]];
|
|
||||||
}
|
|
||||||
|
|
||||||
[request setURL:url];
|
|
||||||
[request setHTTPMethod:method];
|
|
||||||
[request setHTTPShouldHandleCookies:NO];
|
|
||||||
[request setAllHTTPHeaderFields:headers];
|
|
||||||
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)defaultValueForHeader:(NSString *)header {
|
- (NSString *)defaultValueForHeader:(NSString *)header {
|
||||||
return [self.defaultHeaders valueForKey:header];
|
return [self.defaultHeaders valueForKey:header];
|
||||||
}
|
}
|
||||||
|
|
@ -124,42 +96,81 @@ static NSStringEncoding const kAFRestClientStringEncoding = NSUTF8StringEncoding
|
||||||
[self.defaultHeaders removeObjectForKey:@"Authorization"];
|
[self.defaultHeaders removeObjectForKey:@"Authorization"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
|
|
||||||
- (void)getPath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback {
|
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters {
|
||||||
NSURLRequest *request = [self requestWithMethod:@"GET" path:path parameters:parameters];
|
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
|
||||||
[self enqueueHTTPOperationWithRequest:request callback:callback];
|
NSMutableDictionary *headers = [NSMutableDictionary dictionaryWithDictionary:self.defaultHeaders];
|
||||||
|
NSURL *url = [NSURL URLWithString:path relativeToURL:[[self class] baseURL]];
|
||||||
|
|
||||||
|
if (parameters) {
|
||||||
|
NSMutableArray *mutableParameterComponents = [NSMutableArray array];
|
||||||
|
for (id key in [parameters allKeys]) {
|
||||||
|
NSString *component = [NSString stringWithFormat:@"%@=%@", [key urlEncodedStringWithEncoding:kAFRestClientStringEncoding], [[parameters valueForKey:key] urlEncodedStringWithEncoding:kAFRestClientStringEncoding]];
|
||||||
|
[mutableParameterComponents addObject:component];
|
||||||
|
}
|
||||||
|
NSString *queryString = [mutableParameterComponents componentsJoinedByString:@"&"];
|
||||||
|
|
||||||
|
if ([method isEqualToString:@"GET"]) {
|
||||||
|
url = [NSURL URLWithString:[[url absoluteString] stringByAppendingFormat:[path rangeOfString:@"?"].location == NSNotFound ? @"?%@" : @"&%@", queryString]];
|
||||||
|
} else {
|
||||||
|
NSString *charset = (NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(kAFRestClientStringEncoding));
|
||||||
|
[headers setObject:[NSString stringWithFormat:@"application/x-www-form-urlencoded; charset=%@", charset] forKey:@"Content-Type"];
|
||||||
|
[request setHTTPBody:[queryString dataUsingEncoding:NSUTF8StringEncoding]];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)postPath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback {
|
[request setURL:url];
|
||||||
NSURLRequest *request = [self requestWithMethod:@"POST" path:path parameters:parameters];
|
[request setHTTPMethod:method];
|
||||||
[self enqueueHTTPOperationWithRequest:request callback:callback];
|
[request setHTTPShouldHandleCookies:NO];
|
||||||
|
[request setAllHTTPHeaderFields:headers];
|
||||||
|
|
||||||
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)putPath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback {
|
- (void)enqueueHTTPOperationWithRequest:(NSURLRequest *)request success:(void (^)(NSDictionary *response))success failure:(void (^)(NSError *error))failure {
|
||||||
NSURLRequest *request = [self requestWithMethod:@"PUT" path:path parameters:parameters];
|
|
||||||
[self enqueueHTTPOperationWithRequest:request callback:callback];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)deletePath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback {
|
|
||||||
NSURLRequest *request = [self requestWithMethod:@"DELETE" path:path parameters:parameters];
|
|
||||||
[self enqueueHTTPOperationWithRequest:request callback:callback];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
- (void)enqueueHTTPOperationWithRequest:(NSURLRequest *)request callback:(AFHTTPOperationCallback *)callback {
|
|
||||||
if ([request URL] == nil || [[request URL] isEqual:[NSNull null]]) {
|
if ([request URL] == nil || [[request URL] isEqual:[NSNull null]]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AFHTTPOperation *operation = [[[AFHTTPOperation alloc] initWithRequest:request callback:callback] autorelease];
|
AFHTTPRequestOperation *operation = [AFJSONRequestOperation operationWithRequest:request success:success failure:failure];
|
||||||
[self enqueueHTTPOperation:operation];
|
[self.operationQueue addOperation:operation];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)enqueueHTTPOperation:(AFHTTPOperation *)operation {
|
- (void)getPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(NSDictionary *))success {
|
||||||
[self.operationQueue addOperation:operation];
|
[self getPath:path parameters:parameters success:success failure:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)getPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(NSDictionary *))success failure:(void (^)(NSError *error))failure {
|
||||||
|
NSURLRequest *request = [self requestWithMethod:@"GET" path:path parameters:parameters];
|
||||||
|
[self enqueueHTTPOperationWithRequest:request success:success failure:failure];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)postPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(NSDictionary *))success {
|
||||||
|
[self postPath:path parameters:parameters success:success failure:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)postPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(NSDictionary *))success failure:(void (^)(NSError *error))failure {
|
||||||
|
NSURLRequest *request = [self requestWithMethod:@"POST" path:path parameters:parameters];
|
||||||
|
[self enqueueHTTPOperationWithRequest:request success:success failure:failure];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)putPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(NSDictionary *))success {
|
||||||
|
[self putPath:path parameters:parameters success:success failure:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)putPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(NSDictionary *))success failure:(void (^)(NSError *error))failure {
|
||||||
|
NSURLRequest *request = [self requestWithMethod:@"PUT" path:path parameters:parameters];
|
||||||
|
[self enqueueHTTPOperationWithRequest:request success:success failure:failure];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)deletePath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(NSDictionary *))success {
|
||||||
|
[self deletePath:path parameters:parameters success:success failure:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)deletePath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(NSDictionary *))success failure:(void (^)(NSError *error))failure {
|
||||||
|
NSURLRequest *request = [self requestWithMethod:@"DELETE" path:path parameters:parameters];
|
||||||
|
[self enqueueHTTPOperationWithRequest:request success:success failure:failure];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
// AFImageRequest.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 "AFImageRequest.h"
|
|
||||||
#import "AFImageRequestOperation.h"
|
|
||||||
|
|
||||||
static NSOperationQueue *_operationQueue = nil;
|
|
||||||
static NSMutableSet *_cachedRequests = nil;
|
|
||||||
|
|
||||||
@implementation AFImageRequest
|
|
||||||
|
|
||||||
+ (void)initialize {
|
|
||||||
_operationQueue = [[NSOperationQueue alloc] init];
|
|
||||||
[_operationQueue setMaxConcurrentOperationCount:6];
|
|
||||||
|
|
||||||
_cachedRequests = [[NSMutableSet alloc] init];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)requestImageWithURLString:(NSString *)urlString options:(AFImageRequestOptions)options block:(void (^)(UIImage *image))block {
|
|
||||||
[self requestImageWithURLString:urlString size:CGSizeZero options:options block:block];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)requestImageWithURLString:(NSString *)urlString size:(CGSize)imageSize options:(AFImageRequestOptions)options block:(void (^)(UIImage *image))block {
|
|
||||||
// Append a hash anchor to the image URL so that unique image options get cached separately
|
|
||||||
NSString *cacheAnchor = [NSString stringWithFormat:@"%fx%f:%d", imageSize.width, imageSize.height, options];
|
|
||||||
NSURL *url = [NSURL URLWithString:[urlString stringByAppendingString:[NSString stringWithFormat:@"#%@", cacheAnchor]]];
|
|
||||||
if (!url) {
|
|
||||||
if (block) {
|
|
||||||
block(nil);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLCacheStorageAllowed timeoutInterval:30.0];
|
|
||||||
[request setHTTPShouldHandleCookies:NO];
|
|
||||||
|
|
||||||
AFImageRequestOperationCallback *callback = [AFImageRequestOperationCallback callbackWithSuccess:block imageSize:imageSize options:options];
|
|
||||||
AFImageRequestOperation *operation = [[[AFImageRequestOperation alloc] initWithRequest:request callback:callback] autorelease];
|
|
||||||
|
|
||||||
NSCachedURLResponse *cachedResponse = [[[[NSURLCache sharedURLCache] cachedResponseForRequest:request] retain] autorelease];
|
|
||||||
if (cachedResponse) {
|
|
||||||
if (block) {
|
|
||||||
block([UIImage imageWithData:[cachedResponse data]]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[_operationQueue addOperation:operation];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)cancelImageRequestOperationsForURLString:(NSString *)urlString {
|
|
||||||
if (!urlString) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (AFImageRequestOperation *operation in [_operationQueue operations]) {
|
|
||||||
NSString *requestURLString = [[[operation request] URL] absoluteString];
|
|
||||||
NSRange anchorRange = [requestURLString rangeOfString:@"#" options:NSBackwardsSearch];
|
|
||||||
if (anchorRange.location != NSNotFound && [[requestURLString substringToIndex:anchorRange.location] isEqualToString:urlString]) {
|
|
||||||
if (!([operation isExecuting] || [operation isCancelled])) {
|
|
||||||
[operation cancel];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)cancelAllImageRequestOperations {
|
|
||||||
for (AFImageRequestOperation *operation in [_operationQueue operations]) {
|
|
||||||
if (!([operation isExecuting] || [operation isCancelled])) {
|
|
||||||
[operation cancel];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
@ -21,9 +21,14 @@
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
#import "NearbySpotsViewController.h"
|
#import "NearbySpotsViewController.h"
|
||||||
|
|
||||||
#import "Spot.h"
|
#import "Spot.h"
|
||||||
|
|
||||||
#import "SpotTableViewCell.h"
|
#import "SpotTableViewCell.h"
|
||||||
|
|
||||||
#import "TTTLocationFormatter.h"
|
#import "TTTLocationFormatter.h"
|
||||||
|
#import "AFImageCache.h"
|
||||||
|
#import "UIImageView+AFNetworking.h"
|
||||||
|
|
||||||
@interface NearbySpotsViewController ()
|
@interface NearbySpotsViewController ()
|
||||||
@property (readwrite, nonatomic, retain) NSArray *nearbySpots;
|
@property (readwrite, nonatomic, retain) NSArray *nearbySpots;
|
||||||
|
|
@ -72,7 +77,7 @@ static TTTLocationFormatter *__locationFormatter;
|
||||||
[self.activityIndicatorView startAnimating];
|
[self.activityIndicatorView startAnimating];
|
||||||
self.navigationItem.rightBarButtonItem.enabled = NO;
|
self.navigationItem.rightBarButtonItem.enabled = NO;
|
||||||
|
|
||||||
[Spot spotsWithURLString:@"/spots/advanced_search" near:location parameters:[NSDictionary dictionaryWithObject:@"128" forKey:@"per_page"] withBlock:^(NSArray *records) {
|
[Spot spotsWithURLString:@"/spots/advanced_search" near:location parameters:[NSDictionary dictionaryWithObject:@"128" forKey:@"per_page"] block:^(NSArray *records) {
|
||||||
self.nearbySpots = [records sortedArrayUsingComparator:^ NSComparisonResult(id obj1, id obj2) {
|
self.nearbySpots = [records sortedArrayUsingComparator:^ NSComparisonResult(id obj1, id obj2) {
|
||||||
CLLocationDistance d1 = [[(Spot *)obj1 location] distanceFromLocation:location];
|
CLLocationDistance d1 = [[(Spot *)obj1 location] distanceFromLocation:location];
|
||||||
CLLocationDistance d2 = [[(Spot *)obj2 location] distanceFromLocation:location];
|
CLLocationDistance d2 = [[(Spot *)obj2 location] distanceFromLocation:location];
|
||||||
|
|
@ -119,24 +124,25 @@ static TTTLocationFormatter *__locationFormatter;
|
||||||
[self.locationManager stopUpdatingLocation];
|
[self.locationManager stopUpdatingLocation];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - CLLocationManagerDelegate
|
|
||||||
|
|
||||||
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
|
|
||||||
[self loadSpotsForLocation:newLocation];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Actions
|
#pragma mark - Actions
|
||||||
|
|
||||||
- (void)refresh:(id)sender {
|
- (void)refresh:(id)sender {
|
||||||
self.nearbySpots = [NSArray array];
|
self.nearbySpots = [NSArray array];
|
||||||
[self.tableView reloadData];
|
[self.tableView reloadData];
|
||||||
[[NSURLCache sharedURLCache] removeAllCachedResponses];
|
[[NSURLCache sharedURLCache] removeAllCachedResponses];
|
||||||
|
[[AFImageCache sharedImageCache] removeAllObjects];
|
||||||
|
|
||||||
if (self.locationManager.location) {
|
if (self.locationManager.location) {
|
||||||
[self loadSpotsForLocation:self.locationManager.location];
|
[self loadSpotsForLocation:self.locationManager.location];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - CLLocationManagerDelegate
|
||||||
|
|
||||||
|
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
|
||||||
|
[self loadSpotsForLocation:newLocation];
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - UITableViewDelegate
|
#pragma mark - UITableViewDelegate
|
||||||
|
|
||||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||||
|
|
@ -160,7 +166,7 @@ static TTTLocationFormatter *__locationFormatter;
|
||||||
if (self.locationManager.location) {
|
if (self.locationManager.location) {
|
||||||
cell.detailTextLabel.text = [__locationFormatter stringFromDistanceAndBearingFromLocation:self.locationManager.location toLocation:spot.location];
|
cell.detailTextLabel.text = [__locationFormatter stringFromDistanceAndBearingFromLocation:self.locationManager.location toLocation:spot.location];
|
||||||
}
|
}
|
||||||
cell.imageURLString = spot.imageURLString;
|
[cell.imageView setImageWithURL:[NSURL URLWithString:spot.imageURLString] placeholderImage:[UIImage imageNamed:@"placeholder-stamp.png"]];
|
||||||
|
|
||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,6 @@ typedef void (^AFRecordsBlock)(NSArray *records);
|
||||||
@property (readonly) CLLocation *location;
|
@property (readonly) CLLocation *location;
|
||||||
|
|
||||||
- (id)initWithAttributes:(NSDictionary *)attributes;
|
- (id)initWithAttributes:(NSDictionary *)attributes;
|
||||||
+ (void)spotsWithURLString:(NSString *)urlString near:(CLLocation *)location parameters:(NSDictionary *)parameters withBlock:(AFRecordsBlock)block;
|
+ (void)spotsWithURLString:(NSString *)urlString near:(CLLocation *)location parameters:(NSDictionary *)parameters block:(AFRecordsBlock)block;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
||||||
|
|
@ -56,24 +56,28 @@
|
||||||
return [[[CLLocation alloc] initWithLatitude:[self.latitude doubleValue] longitude:[self.longitude doubleValue]] autorelease];
|
return [[[CLLocation alloc] initWithLatitude:[self.latitude doubleValue] longitude:[self.longitude doubleValue]] autorelease];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void)spotsWithURLString:(NSString *)urlString near:(CLLocation *)location parameters:(NSDictionary *)parameters withBlock:(AFRecordsBlock)block {
|
+ (void)spotsWithURLString:(NSString *)urlString near:(CLLocation *)location parameters:(NSDictionary *)parameters block:(AFRecordsBlock)block {
|
||||||
NSDictionary *mutableParameters = [NSMutableDictionary dictionaryWithDictionary:parameters];
|
NSDictionary *mutableParameters = [NSMutableDictionary dictionaryWithDictionary:parameters];
|
||||||
if (location) {
|
if (location) {
|
||||||
[mutableParameters setValue:[NSString stringWithFormat:@"%1.7f", location.coordinate.latitude] forKey:@"lat"];
|
[mutableParameters setValue:[NSString stringWithFormat:@"%1.7f", location.coordinate.latitude] forKey:@"lat"];
|
||||||
[mutableParameters setValue:[NSString stringWithFormat:@"%1.7f", location.coordinate.longitude] forKey:@"lng"];
|
[mutableParameters setValue:[NSString stringWithFormat:@"%1.7f", location.coordinate.longitude] forKey:@"lng"];
|
||||||
}
|
}
|
||||||
|
|
||||||
[[AFGowallaAPIClient sharedClient] getPath:urlString parameters:mutableParameters callback:[AFHTTPOperationCallback callbackWithSuccess:^(NSURLRequest *request, NSHTTPURLResponse *response, NSDictionary *data) {
|
[[AFGowallaAPIClient sharedClient] getPath:urlString parameters:mutableParameters success:^(NSDictionary *response) {
|
||||||
if (block) {
|
|
||||||
NSMutableArray *mutableRecords = [NSMutableArray array];
|
NSMutableArray *mutableRecords = [NSMutableArray array];
|
||||||
for (NSDictionary *attributes in [data valueForKeyPath:@"spots"]) {
|
for (NSDictionary *attributes in [response valueForKeyPath:@"spots"]) {
|
||||||
Spot *spot = [[[Spot alloc] initWithAttributes:attributes] autorelease];
|
Spot *spot = [[[Spot alloc] initWithAttributes:attributes] autorelease];
|
||||||
[mutableRecords addObject:spot];
|
[mutableRecords addObject:spot];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (block) {
|
||||||
block([NSArray arrayWithArray:mutableRecords]);
|
block([NSArray arrayWithArray:mutableRecords]);
|
||||||
}
|
}
|
||||||
}]];
|
} failure:^(NSError *error) {
|
||||||
|
if (block) {
|
||||||
|
block([NSArray array]);
|
||||||
|
}
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,7 @@
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#import "AFImageRequest.h"
|
|
||||||
|
|
||||||
@interface SpotTableViewCell : UITableViewCell <AFImageRequester> {
|
@interface SpotTableViewCell : UITableViewCell
|
||||||
NSString *_imageURLString;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@
|
||||||
#import "SpotTableViewCell.h"
|
#import "SpotTableViewCell.h"
|
||||||
|
|
||||||
@implementation SpotTableViewCell
|
@implementation SpotTableViewCell
|
||||||
@synthesize imageURLString = _imageURLString;
|
|
||||||
|
|
||||||
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||||
|
|
@ -41,50 +40,6 @@
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
|
||||||
[_imageURLString release];
|
|
||||||
[super dealloc];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setImageURLString:(NSString *)imageURLString {
|
|
||||||
[self setImageURLString:imageURLString options:AFImageRequestResize | AFImageCacheProcessedImage];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setImageURLString:(NSString *)imageURLString options:(AFImageRequestOptions)options {
|
|
||||||
if ([self.imageURLString isEqual:imageURLString]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.imageURLString) {
|
|
||||||
self.imageView.image = [UIImage imageNamed:@"placeholder-stamp.png"];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self willChangeValueForKey:@"imageURLString"];
|
|
||||||
[_imageURLString release];
|
|
||||||
_imageURLString = [imageURLString copy];
|
|
||||||
[self didChangeValueForKey:@"imageURLString"];
|
|
||||||
|
|
||||||
if (self.imageURLString) {
|
|
||||||
[AFImageRequest requestImageWithURLString:self.imageURLString size:CGSizeMake(50.0f, 50.0f) options:options block:^(UIImage *image) {
|
|
||||||
if ([self.imageURLString isEqualToString:imageURLString]) {
|
|
||||||
BOOL needsLayout = self.imageView.image == nil;
|
|
||||||
self.imageView.image = image;
|
|
||||||
|
|
||||||
if (needsLayout) {
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - UITableViewCell
|
|
||||||
|
|
||||||
- (void)prepareForReuse {
|
|
||||||
[super prepareForReuse];
|
|
||||||
[AFImageRequest cancelImageRequestOperationsForURLString:self.imageURLString];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - UIView
|
#pragma mark - UIView
|
||||||
|
|
||||||
- (void)layoutSubviews {
|
- (void)layoutSubviews {
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,6 @@
|
||||||
UIGraphicsEndImageContext();
|
UIGraphicsEndImageContext();
|
||||||
|
|
||||||
return newImage;
|
return newImage;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
32
Example/UIImageView+AFNetworking.h
Normal file
32
Example/UIImageView+AFNetworking.h
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
// UIImageView+AFNetworking.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 <UIKit/UIKit.h>
|
||||||
|
#import "AFImageRequestOperation.h"
|
||||||
|
|
||||||
|
@interface UIImageView (AFNetworking)
|
||||||
|
|
||||||
|
- (void)setImageWithURL:(NSURL *)url;
|
||||||
|
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholderImage;
|
||||||
|
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholderImage imageSize:(CGSize)imageSize options:(AFImageRequestOptions)options;
|
||||||
|
|
||||||
|
@end
|
||||||
101
Example/UIImageView+AFNetworking.m
Normal file
101
Example/UIImageView+AFNetworking.m
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
// UIImageView+AFNetworking.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 "UIImageView+AFNetworking.h"
|
||||||
|
|
||||||
|
#import "AFImageCache.h"
|
||||||
|
|
||||||
|
static NSOperationQueue *_operationQueue = nil;
|
||||||
|
|
||||||
|
static NSString * const kUIImageViewImageRequestObjectKey = @"imageRequestOperation";
|
||||||
|
|
||||||
|
@interface UIImageView (_AFNetworking)
|
||||||
|
@property (readwrite, nonatomic, retain) AFImageRequestOperation *imageRequestOperation;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation UIImageView (_AFNetworking)
|
||||||
|
@dynamic imageRequestOperation;
|
||||||
|
@end
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
@implementation UIImageView (AFNetworking)
|
||||||
|
|
||||||
|
- (AFHTTPRequestOperation *)imageRequestOperation {
|
||||||
|
return objc_getAssociatedObject(self, kUIImageViewImageRequestObjectKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setImageRequestOperation:(AFImageRequestOperation *)imageRequestOperation {
|
||||||
|
objc_setAssociatedObject(self, kUIImageViewImageRequestObjectKey, imageRequestOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)initialize {
|
||||||
|
[super initialize];
|
||||||
|
|
||||||
|
_operationQueue = [[NSOperationQueue alloc] init];
|
||||||
|
[_operationQueue setMaxConcurrentOperationCount:6];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setImageWithURL:(NSURL *)url {
|
||||||
|
[self setImageWithURL:url placeholderImage:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholderImage {
|
||||||
|
[self setImageWithURL:url placeholderImage:placeholderImage imageSize:self.frame.size options:AFImageRequestDefaultOptions];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholderImage imageSize:(CGSize)imageSize options:(AFImageRequestOptions)options {
|
||||||
|
if (!url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.imageRequestOperation && ([self.imageRequestOperation isReady] || [self.imageRequestOperation isExecuting])) {
|
||||||
|
if ([[[self.imageRequestOperation request] URL] isEqual:url]) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
[self.imageRequestOperation cancel];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLCacheStorageAllowed timeoutInterval:30.0];
|
||||||
|
[request setHTTPShouldHandleCookies:NO];
|
||||||
|
|
||||||
|
UIImage *cachedImage = [[AFImageCache sharedImageCache] cachedImageForRequest:request imageSize:imageSize options:options];
|
||||||
|
if (cachedImage) {
|
||||||
|
self.image = cachedImage;
|
||||||
|
} else {
|
||||||
|
self.image = placeholderImage;
|
||||||
|
|
||||||
|
self.imageRequestOperation = [AFImageRequestOperation operationWithRequest:request imageSize:imageSize options:options success:^(UIImage *image) {
|
||||||
|
if ([[request URL] isEqual:[[self.imageRequestOperation request] URL]]) {
|
||||||
|
self.image = image;
|
||||||
|
} else {
|
||||||
|
self.image = placeholderImage;
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
[_operationQueue addOperation:self.imageRequestOperation];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@end
|
||||||
Loading…
Add table
Reference in a new issue