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/)
|
||||
//
|
||||
|
|
@ -20,20 +20,20 @@
|
|||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "AFImageRequestOperation.h"
|
||||
|
||||
@protocol AFImageRequester
|
||||
@required
|
||||
- (void)setImageURLString:(NSString *)urlString;
|
||||
- (void)setImageURLString:(NSString *)urlString options:(AFImageRequestOptions)options;
|
||||
@optional
|
||||
@property (nonatomic, copy) NSString *imageURLString;
|
||||
@end
|
||||
@interface AFImageCache : NSCache
|
||||
|
||||
@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
|
||||
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/)
|
||||
//
|
||||
|
|
@ -20,41 +20,21 @@
|
|||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import "AFCallback.h"
|
||||
#import "AFHTTPRequestOperation.h"
|
||||
|
||||
@interface AFJSONRequestOperation : AFHTTPRequestOperation
|
||||
|
||||
+ (id)operationWithRequest:(NSURLRequest *)urlRequest
|
||||
success:(void (^)(NSDictionary *JSON))success;
|
||||
|
||||
+ (id)operationWithRequest:(NSURLRequest *)urlRequest
|
||||
success:(void (^)(NSDictionary *JSON))success
|
||||
failure:(void (^)(NSError *error))failure;
|
||||
|
||||
+ (id)operationWithRequest:(NSURLRequest *)urlRequest
|
||||
acceptableStatusCodes:(NSIndexSet *)acceptableStatusCodes
|
||||
acceptableContentTypes:(NSSet *)acceptableContentTypes
|
||||
success:(void (^)(NSDictionary *JSON))success
|
||||
failure:(void (^)(NSError *error))failure;
|
||||
|
||||
@interface AFCallback ()
|
||||
@property (readwrite, nonatomic, copy) id successBlock;
|
||||
@property (readwrite, nonatomic, copy) id errorBlock;
|
||||
@end
|
||||
|
||||
@implementation AFCallback
|
||||
@synthesize successBlock = _successBlock;
|
||||
@synthesize errorBlock = _errorBlock;
|
||||
|
||||
+ (id)callbackWithSuccess:(id)success {
|
||||
return [self callbackWithSuccess:success error:nil];
|
||||
}
|
||||
|
||||
+ (id)callbackWithSuccess:(id)success error:(id)error {
|
||||
id callback = [[[self alloc] init] autorelease];
|
||||
[callback setSuccessBlock:success];
|
||||
[callback setErrorBlock:error];
|
||||
|
||||
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
|
||||
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/)
|
||||
//
|
||||
|
|
@ -22,15 +22,14 @@
|
|||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@protocol AFCallback <NSObject>
|
||||
+ (id)callbackWithSuccess:(id)success;
|
||||
+ (id)callbackWithSuccess:(id)success error:(id)error;
|
||||
@end
|
||||
|
||||
@interface AFCallback : NSObject <AFCallback> {
|
||||
@interface AFNetworkActivityIndicatorManager : NSObject {
|
||||
@private
|
||||
id _successBlock;
|
||||
id _errorBlock;
|
||||
NSUInteger _activityCount;
|
||||
}
|
||||
|
||||
@end
|
||||
+ (AFNetworkActivityIndicatorManager *)sharedManager;
|
||||
|
||||
- (void)startAnimating;
|
||||
- (void)stopAnimating;
|
||||
|
||||
@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 = {
|
||||
|
||||
/* 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 */; };
|
||||
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 */; };
|
||||
F8DA09D31396ABED0057D0CC /* AFImageRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D25D1E1396A9DE00CF3BD6 /* AFImageRequest.m */; };
|
||||
F8DA09D41396ABED0057D0CC /* NearbySpotsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F8DA09C81396AB690057D0CC /* NearbySpotsViewController.m */; };
|
||||
F8DA09D51396ABED0057D0CC /* Spot.m in Sources */ = {isa = PBXBuildFile; fileRef = F8DA09CB1396AB690057D0CC /* Spot.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 */; };
|
||||
F8DA09E21396ABED0057D0CC /* TTTLocationFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D25D141396A9C400CF3BD6 /* TTTLocationFormatter.m */; };
|
||||
F8DA09E41396AC040057D0CC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F8DA09E31396AC040057D0CC /* main.m */; };
|
||||
|
|
@ -32,20 +32,22 @@
|
|||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
F8D25CEA1396A98600CF3BD6 /* AFCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFCallback.h; path = ../AFNetworking/AFCallback.h; sourceTree = "<group>"; };
|
||||
F8D25CEB1396A98600CF3BD6 /* AFHTTPOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFHTTPOperation.h; path = ../AFNetworking/AFHTTPOperation.h; sourceTree = "<group>"; };
|
||||
F8D25CEC1396A98600CF3BD6 /* AFImageRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFImageRequestOperation.h; path = ../AFNetworking/AFImageRequestOperation.h; sourceTree = "<group>"; };
|
||||
F8D25CED1396A98600CF3BD6 /* AFRestClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFRestClient.h; path = ../AFNetworking/AFRestClient.h; 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>"; };
|
||||
F8D25CF01396A98600CF3BD6 /* AFCallback.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFCallback.m; path = ../AFNetworking/AFCallback.m; sourceTree = "<group>"; };
|
||||
F8D25CF11396A98600CF3BD6 /* AFHTTPOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFHTTPOperation.m; path = ../AFNetworking/AFHTTPOperation.m; sourceTree = "<group>"; };
|
||||
F8D25CF21396A98600CF3BD6 /* AFImageRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFImageRequestOperation.m; path = ../AFNetworking/AFImageRequestOperation.m; sourceTree = "<group>"; };
|
||||
F8D25CF31396A98600CF3BD6 /* AFRestClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFRestClient.m; path = ../AFNetworking/AFRestClient.m; 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>"; };
|
||||
F8D25D091396A9A900CF3BD6 /* QHTTPOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QHTTPOperation.h; sourceTree = "<group>"; };
|
||||
F8D25D0A1396A9A900CF3BD6 /* QHTTPOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QHTTPOperation.m; sourceTree = "<group>"; };
|
||||
F8D25D0B1396A9A900CF3BD6 /* QRunLoopOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QRunLoopOperation.h; sourceTree = "<group>"; };
|
||||
F8D25D0C1396A9A900CF3BD6 /* QRunLoopOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QRunLoopOperation.m; sourceTree = "<group>"; };
|
||||
F874B5A313E0937400B28E3E /* AFHTTPRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFHTTPRequestOperation.h; sourceTree = "<group>"; };
|
||||
F874B5A413E0937400B28E3E /* AFHTTPRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFHTTPRequestOperation.m; sourceTree = "<group>"; };
|
||||
F874B5A513E0937400B28E3E /* AFJSONRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFJSONRequestOperation.h; sourceTree = "<group>"; };
|
||||
F874B5A613E0937400B28E3E /* AFJSONRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFJSONRequestOperation.m; sourceTree = "<group>"; };
|
||||
F874B5A713E0937400B28E3E /* AFImageRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFImageRequestOperation.h; sourceTree = "<group>"; };
|
||||
F874B5A813E0937400B28E3E /* AFImageRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFImageRequestOperation.m; sourceTree = "<group>"; };
|
||||
F874B5A913E0937400B28E3E /* AFImageCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFImageCache.h; sourceTree = "<group>"; };
|
||||
F874B5AA13E0937400B28E3E /* AFImageCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFImageCache.m; sourceTree = "<group>"; };
|
||||
F874B5AB13E0937400B28E3E /* AFRestClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFRestClient.h; sourceTree = "<group>"; };
|
||||
F874B5AC13E0937400B28E3E /* AFRestClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFRestClient.m; sourceTree = "<group>"; };
|
||||
F874B5AD13E0937400B28E3E /* UIImage+AFNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+AFNetworking.h"; sourceTree = "<group>"; };
|
||||
F874B5AE13E0937400B28E3E /* UIImage+AFNetworking.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+AFNetworking.m"; sourceTree = "<group>"; };
|
||||
F874B5AF13E0937400B28E3E /* UIImageView+AFNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImageView+AFNetworking.h"; 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>"; };
|
||||
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>"; };
|
||||
|
|
@ -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; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
|
@ -92,18 +92,6 @@
|
|||
/* End PBXFrameworksBuildPhase 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 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -216,7 +204,6 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
F8E469941395744600DB05C8 /* Alamofire */,
|
||||
F8D25D081396A9A900CF3BD6 /* QHTTPOperation */,
|
||||
F8D25D0F1396A9C400CF3BD6 /* JSONKit */,
|
||||
F8D25D121396A9C400CF3BD6 /* TTT */,
|
||||
);
|
||||
|
|
@ -226,16 +213,22 @@
|
|||
F8E469941395744600DB05C8 /* Alamofire */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F8D25CEA1396A98600CF3BD6 /* AFCallback.h */,
|
||||
F8D25CF01396A98600CF3BD6 /* AFCallback.m */,
|
||||
F8D25CEB1396A98600CF3BD6 /* AFHTTPOperation.h */,
|
||||
F8D25CF11396A98600CF3BD6 /* AFHTTPOperation.m */,
|
||||
F8D25CEC1396A98600CF3BD6 /* AFImageRequestOperation.h */,
|
||||
F8D25CF21396A98600CF3BD6 /* AFImageRequestOperation.m */,
|
||||
F8D25CED1396A98600CF3BD6 /* AFRestClient.h */,
|
||||
F8D25CF31396A98600CF3BD6 /* AFRestClient.m */,
|
||||
F8D25CEF1396A98600CF3BD6 /* UIImage+AFNetworking.h */,
|
||||
F8D25CF51396A98600CF3BD6 /* UIImage+AFNetworking.m */,
|
||||
F874B5A313E0937400B28E3E /* AFHTTPRequestOperation.h */,
|
||||
F874B5A413E0937400B28E3E /* AFHTTPRequestOperation.m */,
|
||||
F874B5A513E0937400B28E3E /* AFJSONRequestOperation.h */,
|
||||
F874B5A613E0937400B28E3E /* AFJSONRequestOperation.m */,
|
||||
F874B5AB13E0937400B28E3E /* AFRestClient.h */,
|
||||
F874B5AC13E0937400B28E3E /* AFRestClient.m */,
|
||||
F874B5A713E0937400B28E3E /* AFImageRequestOperation.h */,
|
||||
F874B5A813E0937400B28E3E /* AFImageRequestOperation.m */,
|
||||
F874B5A913E0937400B28E3E /* AFImageCache.h */,
|
||||
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;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -245,8 +238,6 @@
|
|||
children = (
|
||||
F8D25D1B1396A9DE00CF3BD6 /* AFGowallaAPIClient.h */,
|
||||
F8D25D1D1396A9DE00CF3BD6 /* AFGowallaAPIClient.m */,
|
||||
F8D25D1C1396A9DE00CF3BD6 /* AFImageRequest.h */,
|
||||
F8D25D1E1396A9DE00CF3BD6 /* AFImageRequest.m */,
|
||||
);
|
||||
name = "Networking Extensions";
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -287,6 +278,7 @@
|
|||
F8E469571395739C00DB05C8 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0420;
|
||||
ORGANIZATIONNAME = Gowalla;
|
||||
};
|
||||
buildConfigurationList = F8E4695A1395739C00DB05C8 /* Build configuration list for PBXProject "AFNetworking Example" */;
|
||||
|
|
@ -324,21 +316,21 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F8DA09D21396ABED0057D0CC /* AFGowallaAPIClient.m in Sources */,
|
||||
F8DA09D31396ABED0057D0CC /* AFImageRequest.m in Sources */,
|
||||
F8DA09D41396ABED0057D0CC /* NearbySpotsViewController.m in Sources */,
|
||||
F8DA09D51396ABED0057D0CC /* Spot.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 */,
|
||||
F8DA09E21396ABED0057D0CC /* TTTLocationFormatter.m in Sources */,
|
||||
F8DA09E41396AC040057D0CC /* main.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;
|
||||
};
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -2,4 +2,20 @@
|
|||
<Bucket
|
||||
type = "1"
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -21,27 +21,37 @@
|
|||
// THE SOFTWARE.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "AFHTTPOperation.h"
|
||||
#import "AFHTTPRequestOperation.h"
|
||||
|
||||
@protocol AFRestClient <NSObject>
|
||||
@required
|
||||
+ (NSURL *)baseURL;
|
||||
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters;
|
||||
@end
|
||||
|
||||
@interface AFRestClient : NSObject <AFRestClient>
|
||||
@interface AFRestClient : NSObject <AFRestClient> {
|
||||
@protected
|
||||
NSMutableDictionary *_defaultHeaders;
|
||||
NSOperationQueue *_operationQueue;
|
||||
}
|
||||
|
||||
- (NSString *)defaultValueForHeader:(NSString *)header;
|
||||
- (void)setDefaultHeader:(NSString *)header value:(NSString *)value;
|
||||
- (void)setAuthorizationHeaderWithToken:(NSString *)token;
|
||||
- (void)clearAuthorizationHeader;
|
||||
|
||||
- (void)getPath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback;
|
||||
- (void)postPath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback;
|
||||
- (void)putPath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback;
|
||||
- (void)deletePath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback;
|
||||
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters;
|
||||
- (void)enqueueHTTPOperationWithRequest:(NSURLRequest *)request success:(void (^)(NSDictionary *response))success failure:(void (^)(NSError *error))failure;
|
||||
|
||||
- (void)enqueueHTTPOperationWithRequest:(NSURLRequest *)request callback:(AFHTTPOperationCallback *)callback;
|
||||
- (void)enqueueHTTPOperation:(AFHTTPOperation *)operation;
|
||||
- (void)getPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(NSDictionary *response))success;
|
||||
- (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
|
||||
|
||||
#pragma mark - NSString + AFRestClient
|
||||
|
|
@ -21,13 +21,15 @@
|
|||
// THE SOFTWARE.
|
||||
|
||||
#import "AFRestClient.h"
|
||||
#import "AFHTTPOperation.h"
|
||||
#import "AFJSONRequestOperation.h"
|
||||
|
||||
static NSStringEncoding const kAFRestClientStringEncoding = NSUTF8StringEncoding;
|
||||
|
||||
@interface AFRestClient ()
|
||||
@property (readwrite, nonatomic, retain) NSMutableDictionary *defaultHeaders;
|
||||
@property (readwrite, nonatomic, retain) NSOperationQueue *operationQueue;
|
||||
|
||||
- (void)enqueueHTTPOperationWithRequest:(NSURLRequest *)request success:(void (^)(NSDictionary *response))success failure:(void (^)(NSError *error))failure;
|
||||
@end
|
||||
|
||||
@implementation AFRestClient
|
||||
|
|
@ -78,36 +80,6 @@ static NSStringEncoding const kAFRestClientStringEncoding = NSUTF8StringEncoding
|
|||
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 {
|
||||
return [self.defaultHeaders valueForKey:header];
|
||||
}
|
||||
|
|
@ -124,42 +96,81 @@ static NSStringEncoding const kAFRestClientStringEncoding = NSUTF8StringEncoding
|
|||
[self.defaultHeaders removeObjectForKey:@"Authorization"];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)getPath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback {
|
||||
NSURLRequest *request = [self requestWithMethod:@"GET" path:path parameters:parameters];
|
||||
[self enqueueHTTPOperationWithRequest:request callback:callback];
|
||||
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters {
|
||||
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
|
||||
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]];
|
||||
}
|
||||
}
|
||||
|
||||
[request setURL:url];
|
||||
[request setHTTPMethod:method];
|
||||
[request setHTTPShouldHandleCookies:NO];
|
||||
[request setAllHTTPHeaderFields:headers];
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
- (void)postPath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback {
|
||||
NSURLRequest *request = [self requestWithMethod:@"POST" path:path parameters:parameters];
|
||||
[self enqueueHTTPOperationWithRequest:request callback:callback];
|
||||
}
|
||||
|
||||
- (void)putPath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback {
|
||||
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 {
|
||||
- (void)enqueueHTTPOperationWithRequest:(NSURLRequest *)request success:(void (^)(NSDictionary *response))success failure:(void (^)(NSError *error))failure {
|
||||
if ([request URL] == nil || [[request URL] isEqual:[NSNull null]]) {
|
||||
return;
|
||||
}
|
||||
|
||||
AFHTTPOperation *operation = [[[AFHTTPOperation alloc] initWithRequest:request callback:callback] autorelease];
|
||||
[self enqueueHTTPOperation:operation];
|
||||
|
||||
AFHTTPRequestOperation *operation = [AFJSONRequestOperation operationWithRequest:request success:success failure:failure];
|
||||
[self.operationQueue addOperation:operation];
|
||||
}
|
||||
|
||||
- (void)enqueueHTTPOperation:(AFHTTPOperation *)operation {
|
||||
[self.operationQueue addOperation:operation];
|
||||
- (void)getPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(NSDictionary *))success {
|
||||
[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
|
||||
|
|
@ -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.
|
||||
|
||||
#import "NearbySpotsViewController.h"
|
||||
|
||||
#import "Spot.h"
|
||||
|
||||
#import "SpotTableViewCell.h"
|
||||
|
||||
#import "TTTLocationFormatter.h"
|
||||
#import "AFImageCache.h"
|
||||
#import "UIImageView+AFNetworking.h"
|
||||
|
||||
@interface NearbySpotsViewController ()
|
||||
@property (readwrite, nonatomic, retain) NSArray *nearbySpots;
|
||||
|
|
@ -72,7 +77,7 @@ static TTTLocationFormatter *__locationFormatter;
|
|||
[self.activityIndicatorView startAnimating];
|
||||
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) {
|
||||
CLLocationDistance d1 = [[(Spot *)obj1 location] distanceFromLocation:location];
|
||||
CLLocationDistance d2 = [[(Spot *)obj2 location] distanceFromLocation:location];
|
||||
|
|
@ -119,24 +124,25 @@ static TTTLocationFormatter *__locationFormatter;
|
|||
[self.locationManager stopUpdatingLocation];
|
||||
}
|
||||
|
||||
#pragma mark - CLLocationManagerDelegate
|
||||
|
||||
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
|
||||
[self loadSpotsForLocation:newLocation];
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)refresh:(id)sender {
|
||||
self.nearbySpots = [NSArray array];
|
||||
[self.tableView reloadData];
|
||||
[[NSURLCache sharedURLCache] removeAllCachedResponses];
|
||||
[[AFImageCache sharedImageCache] removeAllObjects];
|
||||
|
||||
if (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
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
|
|
@ -160,7 +166,7 @@ static TTTLocationFormatter *__locationFormatter;
|
|||
if (self.locationManager.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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,6 @@ typedef void (^AFRecordsBlock)(NSArray *records);
|
|||
@property (readonly) CLLocation *location;
|
||||
|
||||
- (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
|
||||
|
|
|
|||
|
|
@ -56,24 +56,28 @@
|
|||
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];
|
||||
if (location) {
|
||||
[mutableParameters setValue:[NSString stringWithFormat:@"%1.7f", location.coordinate.latitude] forKey:@"lat"];
|
||||
[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) {
|
||||
if (block) {
|
||||
NSMutableArray *mutableRecords = [NSMutableArray array];
|
||||
for (NSDictionary *attributes in [data valueForKeyPath:@"spots"]) {
|
||||
Spot *spot = [[[Spot alloc] initWithAttributes:attributes] autorelease];
|
||||
[mutableRecords addObject:spot];
|
||||
}
|
||||
|
||||
[[AFGowallaAPIClient sharedClient] getPath:urlString parameters:mutableParameters success:^(NSDictionary *response) {
|
||||
NSMutableArray *mutableRecords = [NSMutableArray array];
|
||||
for (NSDictionary *attributes in [response valueForKeyPath:@"spots"]) {
|
||||
Spot *spot = [[[Spot alloc] initWithAttributes:attributes] autorelease];
|
||||
[mutableRecords addObject:spot];
|
||||
}
|
||||
|
||||
if (block) {
|
||||
block([NSArray arrayWithArray:mutableRecords]);
|
||||
}
|
||||
}]];
|
||||
} failure:^(NSError *error) {
|
||||
if (block) {
|
||||
block([NSArray array]);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -21,10 +21,7 @@
|
|||
// THE SOFTWARE.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "AFImageRequest.h"
|
||||
|
||||
@interface SpotTableViewCell : UITableViewCell <AFImageRequester> {
|
||||
NSString *_imageURLString;
|
||||
}
|
||||
@interface SpotTableViewCell : UITableViewCell
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@
|
|||
#import "SpotTableViewCell.h"
|
||||
|
||||
@implementation SpotTableViewCell
|
||||
@synthesize imageURLString = _imageURLString;
|
||||
|
||||
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
|
|
@ -41,50 +40,6 @@
|
|||
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
|
||||
|
||||
- (void)layoutSubviews {
|
||||
|
|
|
|||
|
|
@ -75,7 +75,6 @@
|
|||
UIGraphicsEndImageContext();
|
||||
|
||||
return newImage;
|
||||
|
||||
}
|
||||
|
||||
@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