Approaching a reasonable design for restructuring AFHTTPRequestOperation and subclasses

This commit is contained in:
Mattt Thompson 2011-10-05 12:36:45 -05:00
parent c0cba6748a
commit ccdc5f2d9b
10 changed files with 89 additions and 41 deletions

View file

@ -183,9 +183,9 @@
@param success A block object to be executed when the request operation finishes successfully, with a status code in the 2xx range, and with an acceptable content type (e.g. `application/json`). This block has no return value and takes a single argument, which is an object created from the response data of request. @param success A block object to be executed when the request operation finishes successfully, with a status code in the 2xx range, and with an acceptable content type (e.g. `application/json`). This block has no return value and takes a single argument, which is an object created from the response data of request.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the resonse data as JSON. This block has no return value and takes a single argument, which is the `NSError` object describing the network or parsing error that occurred. @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the resonse data as JSON. This block has no return value and takes a single argument, which is the `NSError` object describing the network or parsing error that occurred.
*/ */
- (void)enqueueHTTPOperationWithRequest:(NSURLRequest *)request - (void)enqueueHTTPRequestOperationWithRequest:(NSURLRequest *)request
success:(void (^)(id object))success success:(void (^)(id object))success
failure:(void (^)(NSHTTPURLResponse *response, NSError *error))failure; failure:(void (^)(NSHTTPURLResponse *response, NSError *error))failure;
///--------------------------------- ///---------------------------------
/// @name Cancelling HTTP Operations /// @name Cancelling HTTP Operations
@ -322,3 +322,4 @@
*/ */
- (void)appendString:(NSString *)string; - (void)appendString:(NSString *)string;
@end @end

View file

@ -224,9 +224,9 @@ static NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSS
return request; return request;
} }
- (void)enqueueHTTPOperationWithRequest:(NSURLRequest *)urlRequest - (void)enqueueHTTPRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(id object))success success:(void (^)(id object))success
failure:(void (^)(NSHTTPURLResponse *response, NSError *error))failure failure:(void (^)(NSHTTPURLResponse *response, NSError *error))failure
{ {
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:urlRequest success:^(__unused NSURLRequest *request, __unused NSHTTPURLResponse *response, id JSON) { AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:urlRequest success:^(__unused NSURLRequest *request, __unused NSHTTPURLResponse *response, id JSON) {
if (success) { if (success) {
@ -257,7 +257,7 @@ static NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSS
failure:(void (^)(NSHTTPURLResponse *response, NSError *error))failure failure:(void (^)(NSHTTPURLResponse *response, NSError *error))failure
{ {
NSURLRequest *request = [self requestWithMethod:@"GET" path:path parameters:parameters]; NSURLRequest *request = [self requestWithMethod:@"GET" path:path parameters:parameters];
[self enqueueHTTPOperationWithRequest:request success:success failure:failure]; [self enqueueHTTPRequestOperationWithRequest:request success:success failure:failure];
} }
- (void)postPath:(NSString *)path - (void)postPath:(NSString *)path
@ -266,7 +266,7 @@ static NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSS
failure:(void (^)(NSHTTPURLResponse *response, NSError *error))failure failure:(void (^)(NSHTTPURLResponse *response, NSError *error))failure
{ {
NSURLRequest *request = [self requestWithMethod:@"POST" path:path parameters:parameters]; NSURLRequest *request = [self requestWithMethod:@"POST" path:path parameters:parameters];
[self enqueueHTTPOperationWithRequest:request success:success failure:failure]; [self enqueueHTTPRequestOperationWithRequest:request success:success failure:failure];
} }
- (void)putPath:(NSString *)path - (void)putPath:(NSString *)path
@ -275,7 +275,7 @@ static NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSS
failure:(void (^)(NSHTTPURLResponse *response, NSError *error))failure failure:(void (^)(NSHTTPURLResponse *response, NSError *error))failure
{ {
NSURLRequest *request = [self requestWithMethod:@"PUT" path:path parameters:parameters]; NSURLRequest *request = [self requestWithMethod:@"PUT" path:path parameters:parameters];
[self enqueueHTTPOperationWithRequest:request success:success failure:failure]; [self enqueueHTTPRequestOperationWithRequest:request success:success failure:failure];
} }
- (void)deletePath:(NSString *)path - (void)deletePath:(NSString *)path
@ -284,7 +284,7 @@ static NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSS
failure:(void (^)(NSHTTPURLResponse *response, NSError *error))failure failure:(void (^)(NSHTTPURLResponse *response, NSError *error))failure
{ {
NSURLRequest *request = [self requestWithMethod:@"DELETE" path:path parameters:parameters]; NSURLRequest *request = [self requestWithMethod:@"DELETE" path:path parameters:parameters];
[self enqueueHTTPOperationWithRequest:request success:success failure:failure]; [self enqueueHTTPRequestOperationWithRequest:request success:success failure:failure];
} }
@end @end

View file

@ -23,6 +23,11 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import "AFURLConnectionOperation.h" #import "AFURLConnectionOperation.h"
@protocol AFHTTPClientRequestOperation
+ (BOOL)canInitWithRequest:(NSURLRequest *)request;
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request;
@end
/** /**
`AFHTTPRequestOperation` is an `NSOperation` subclass that implements the `NSURLConnection` delegate methods, and provides a simple block-based interface to asynchronously get the result and context of that operation finishes. `AFHTTPRequestOperation` is an `NSOperation` subclass that implements the `NSURLConnection` delegate methods, and provides a simple block-based interface to asynchronously get the result and context of that operation finishes.
@ -58,6 +63,7 @@
@private @private
NSIndexSet *_acceptableStatusCodes; NSIndexSet *_acceptableStatusCodes;
NSSet *_acceptableContentTypes; NSSet *_acceptableContentTypes;
NSError *_HTTPError;
} }
@property (readonly, nonatomic, retain) NSHTTPURLResponse *response; @property (readonly, nonatomic, retain) NSHTTPURLResponse *response;

View file

@ -27,10 +27,9 @@
@end @end
@implementation AFHTTPRequestOperation @implementation AFHTTPRequestOperation
@dynamic error;
@dynamic response;
@synthesize acceptableStatusCodes = _acceptableStatusCodes; @synthesize acceptableStatusCodes = _acceptableStatusCodes;
@synthesize acceptableContentTypes = _acceptableContentTypes; @synthesize acceptableContentTypes = _acceptableContentTypes;
@synthesize error = _HTTPError;
+ (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)urlRequest + (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSData *data))success success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSData *data))success
@ -51,7 +50,7 @@
} else { } else {
if (success) { if (success) {
dispatch_async(dispatch_get_main_queue(), ^(void) { dispatch_async(dispatch_get_main_queue(), ^(void) {
success(operation.request, operation.response, operation.responseBody); success(operation.request, operation.response, operation.responseData);
}); });
} }
} }
@ -81,7 +80,7 @@
} }
- (NSError *)error { - (NSError *)error {
if (self.response && ![super error]) { if (self.response) {
if (![self hasAcceptableStatusCode]) { if (![self hasAcceptableStatusCode]) {
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[userInfo setValue:[NSString stringWithFormat:NSLocalizedString(@"Expected status code %@, got %d", nil), self.acceptableStatusCodes, [self.response statusCode]] forKey:NSLocalizedDescriptionKey]; [userInfo setValue:[NSString stringWithFormat:NSLocalizedString(@"Expected status code %@, got %d", nil), self.acceptableStatusCodes, [self.response statusCode]] forKey:NSLocalizedDescriptionKey];

View file

@ -30,7 +30,12 @@
@see NSOperation @see NSOperation
@see AFHTTPRequestOperation @see AFHTTPRequestOperation
*/ */
@interface AFImageRequestOperation : AFHTTPRequestOperation @interface AFImageRequestOperation : AFHTTPRequestOperation {
@private
UIImage *_responseImage;
}
@property (readonly, nonatomic, retain) UIImage *responseImage;
/** /**
Creates and returns an `AFImageRequestOperation` object and sets the specified success callback. Creates and returns an `AFImageRequestOperation` object and sets the specified success callback.

View file

@ -32,7 +32,26 @@ static dispatch_queue_t image_request_operation_processing_queue() {
return af_image_request_operation_processing_queue; return af_image_request_operation_processing_queue;
} }
@interface AFImageRequestOperation ()
@property (readwrite, nonatomic, retain) UIImage *responseImage;
+ (UIImage *)imageWithData:(NSData *)data error:(NSError **)error ;
@end
@implementation AFImageRequestOperation @implementation AFImageRequestOperation
@synthesize responseImage = _responseImage;
+ (UIImage *)imageWithData:(NSData *)data error:(NSError **)__unused 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];
}
return image;
}
+ (AFImageRequestOperation *)imageRequestOperationWithRequest:(NSURLRequest *)urlRequest + (AFImageRequestOperation *)imageRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(UIImage *image))success success:(void (^)(UIImage *image))success
@ -64,24 +83,19 @@ static dispatch_queue_t image_request_operation_processing_queue() {
failure(operation.request, operation.response, operation.error); failure(operation.request, operation.response, operation.error);
}); });
} }
} else { } else {
UIImage *image = nil; UIImage *image = [[self class] imageWithData:operation.responseData error:nil];
if ([[UIScreen mainScreen] scale] == 2.0) {
CGImageRef imageRef = [[UIImage imageWithData:operation.responseBody] CGImage];
image = [UIImage imageWithCGImage:imageRef scale:2.0 orientation:UIImageOrientationUp];
} else {
image = [UIImage imageWithData:operation.responseBody];
}
if (imageProcessingBlock) { if (imageProcessingBlock) {
image = imageProcessingBlock(image); image = imageProcessingBlock(image);
} }
dispatch_async(dispatch_get_main_queue(), ^(void) { if (success) {
if (success) { dispatch_async(dispatch_get_main_queue(), ^(void) {
success(operation.request, operation.response, image); success(operation.request, operation.response, image);
} });
}); }
if ([operation.request cachePolicy] != NSURLCacheStorageNotAllowed) { if ([operation.request cachePolicy] != NSURLCacheStorageNotAllowed) {
[[AFImageCache sharedImageCache] cacheImage:image forURL:[operation.request URL] cacheName:cacheNameOrNil]; [[AFImageCache sharedImageCache] cacheImage:image forURL:[operation.request URL] cacheName:cacheNameOrNil];
@ -104,4 +118,17 @@ static dispatch_queue_t image_request_operation_processing_queue() {
return self; return self;
} }
- (void)dealloc {
[_responseImage release];
[super dealloc];
}
- (UIImage *)responseImage {
if (!_responseImage && [self isFinished]) {
self.responseImage = [[self class] imageWithData:self.responseData error:nil];
}
return _responseImage;
}
@end @end

View file

@ -31,12 +31,12 @@
*/ */
@interface AFJSONRequestOperation : AFHTTPRequestOperation { @interface AFJSONRequestOperation : AFHTTPRequestOperation {
@private @private
id _responseJSON; id _responseJSON;
NSError *_JSONError;
} }
@property (readonly, nonatomic, retain) id responseJSON; @property (readonly, nonatomic, retain) id responseJSON;
///--------------------------------------- ///---------------------------------------
/// @name Creating JSON Request Operations /// @name Creating JSON Request Operations
///--------------------------------------- ///---------------------------------------

View file

@ -36,12 +36,14 @@ static dispatch_queue_t json_request_operation_processing_queue() {
@interface AFJSONRequestOperation () @interface AFJSONRequestOperation ()
@property (readwrite, nonatomic, retain) id responseJSON; @property (readwrite, nonatomic, retain) id responseJSON;
@property (readwrite, nonatomic, retain) NSError *error;
+ (id)JSONObjectWithData:(NSData *)data error:(NSError **)error; + (id)JSONObjectWithData:(NSData *)data error:(NSError **)error;
@end @end
@implementation AFJSONRequestOperation @implementation AFJSONRequestOperation
@synthesize responseJSON = _responseJSON; @synthesize responseJSON = _responseJSON;
@synthesize error = _JSONError;
+ (id)JSONObjectWithData:(NSData *)data error:(NSError **)error { + (id)JSONObjectWithData:(NSData *)data error:(NSError **)error {
id JSON = nil; id JSON = nil;
@ -55,7 +57,7 @@ static dispatch_queue_t json_request_operation_processing_queue() {
#else #else
JSON = [[JSONDecoder decoder] objectWithData:data error:error]; JSON = [[JSONDecoder decoder] objectWithData:data error:error];
#endif #endif
return JSON; return JSON;
} }
@ -65,6 +67,10 @@ static dispatch_queue_t json_request_operation_processing_queue() {
{ {
AFJSONRequestOperation *operation = [[[AFJSONRequestOperation alloc] initWithRequest:urlRequest] autorelease]; AFJSONRequestOperation *operation = [[[AFJSONRequestOperation alloc] initWithRequest:urlRequest] autorelease];
operation.completionBlock = ^ { operation.completionBlock = ^ {
if ([operation isCancelled]) {
return;
}
if (operation.error) { if (operation.error) {
if (failure) { if (failure) {
dispatch_async(dispatch_get_main_queue(), ^(void) { dispatch_async(dispatch_get_main_queue(), ^(void) {
@ -74,10 +80,11 @@ static dispatch_queue_t json_request_operation_processing_queue() {
} else { } else {
dispatch_async(json_request_operation_processing_queue(), ^(void) { dispatch_async(json_request_operation_processing_queue(), ^(void) {
NSError *error = nil; NSError *error = nil;
id JSON = [self JSONObjectWithData:operation.responseBody error:&error]; id JSON = [self JSONObjectWithData:operation.responseData error:&error];
operation.error = error;
dispatch_async(dispatch_get_main_queue(), ^(void) { dispatch_async(dispatch_get_main_queue(), ^(void) {
if (error) { if (operation.error) {
if (failure) { if (failure) {
failure(operation.request, operation.response, error); failure(operation.request, operation.response, error);
} }
@ -111,8 +118,10 @@ static dispatch_queue_t json_request_operation_processing_queue() {
} }
- (id)responseJSON { - (id)responseJSON {
if (!_responseJSON && self.response && self.responseBody) { if (!_responseJSON && [self isFinished]) {
self.responseJSON = [[self class] JSONObjectWithData:self.responseBody error:nil]; NSError *error = nil;
self.responseJSON = [[self class] JSONObjectWithData:self.responseData error:nil];
self.error = error;
} }
return _responseJSON; return _responseJSON;

View file

@ -61,7 +61,7 @@ extern NSString * const AFNetworkingOperationDidFinishNotification;
@property (readonly, nonatomic, retain) NSURLResponse *response; @property (readonly, nonatomic, retain) NSURLResponse *response;
@property (readonly, nonatomic, retain) NSError *error; @property (readonly, nonatomic, retain) NSError *error;
@property (readonly, nonatomic, retain) NSData *responseBody; @property (readonly, nonatomic, retain) NSData *responseData;
@property (readonly, nonatomic, copy) NSString *responseString; @property (readonly, nonatomic, copy) NSString *responseString;
@property (nonatomic, retain) NSInputStream *inputStream; @property (nonatomic, retain) NSInputStream *inputStream;

View file

@ -58,7 +58,7 @@ static inline NSString * AFKeyPathFromOperationState(AFOperationState state) {
@property (readwrite, nonatomic, retain) NSURLRequest *request; @property (readwrite, nonatomic, retain) NSURLRequest *request;
@property (readwrite, nonatomic, retain) NSURLResponse *response; @property (readwrite, nonatomic, retain) NSURLResponse *response;
@property (readwrite, nonatomic, retain) NSError *error; @property (readwrite, nonatomic, retain) NSError *error;
@property (readwrite, nonatomic, retain) NSData *responseBody; @property (readwrite, nonatomic, retain) NSData *responseData;
@property (readwrite, nonatomic, copy) NSString *responseString; @property (readwrite, nonatomic, copy) NSString *responseString;
@property (readwrite, nonatomic, assign) NSInteger totalBytesRead; @property (readwrite, nonatomic, assign) NSInteger totalBytesRead;
@property (readwrite, nonatomic, retain) NSMutableData *dataAccumulator; @property (readwrite, nonatomic, retain) NSMutableData *dataAccumulator;
@ -78,7 +78,7 @@ static inline NSString * AFKeyPathFromOperationState(AFOperationState state) {
@synthesize request = _request; @synthesize request = _request;
@synthesize response = _response; @synthesize response = _response;
@synthesize error = _error; @synthesize error = _error;
@synthesize responseBody = _responseBody; @synthesize responseData = _responseBody;
@synthesize responseString = _responseString; @synthesize responseString = _responseString;
@synthesize totalBytesRead = _totalBytesRead; @synthesize totalBytesRead = _totalBytesRead;
@synthesize dataAccumulator = _dataAccumulator; @synthesize dataAccumulator = _dataAccumulator;
@ -207,8 +207,9 @@ static inline NSString * AFKeyPathFromOperationState(AFOperationState state) {
return NO; return NO;
} }
case AFHTTPOperationFinishedState: case AFHTTPOperationFinishedState:
default:
return NO; return NO;
default:
return YES;
} }
} }
@ -223,9 +224,9 @@ static inline NSString * AFKeyPathFromOperationState(AFOperationState state) {
} }
- (NSString *)responseString { - (NSString *)responseString {
if (!_responseString && self.response && self.responseBody) { if (!_responseString && self.response && self.responseData) {
NSStringEncoding textEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)self.response.textEncodingName)); NSStringEncoding textEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)self.response.textEncodingName));
self.responseString = [[[NSString alloc] initWithData:self.responseBody encoding:textEncoding] autorelease]; self.responseString = [[[NSString alloc] initWithData:self.responseData encoding:textEncoding] autorelease];
} }
return _responseString; return _responseString;
@ -336,7 +337,7 @@ didReceiveResponse:(NSURLResponse *)response
if (self.outputStream) { if (self.outputStream) {
[self.outputStream close]; [self.outputStream close];
} else { } else {
self.responseBody = [NSData dataWithData:self.dataAccumulator]; self.responseData = [NSData dataWithData:self.dataAccumulator];
[_dataAccumulator release]; _dataAccumulator = nil; [_dataAccumulator release]; _dataAccumulator = nil;
} }