From c2be31d4fa9acca15f21a91ca2804e7164b33f77 Mon Sep 17 00:00:00 2001 From: Mattt Thompson Date: Wed, 5 Oct 2011 15:44:51 -0500 Subject: [PATCH] First implementation of protocol for AFHTTPClient to register classes to automatically determine the appropriate operation to create Wrapping AFURLConnectionOperation -setCompletionBlock: with blockSelf release pattern to avoid retain cycle --- AFNetworking/AFHTTPClient.h | 7 ++ AFNetworking/AFHTTPClient.m | 39 ++++++++--- AFNetworking/AFHTTPRequestOperation.h | 15 +++-- AFNetworking/AFHTTPRequestOperation.m | 66 +++++++++++-------- AFNetworking/AFImageRequestOperation.m | 23 +++++++ AFNetworking/AFJSONRequestOperation.m | 22 +++++++ AFNetworking/AFPropertyListRequestOperation.m | 22 +++++++ AFNetworking/AFURLConnectionOperation.m | 14 +++- AFNetworking/UIImageView+AFNetworking.m | 2 +- .../project.pbxproj | 4 +- Example/Classes/AFGowallaAPIClient.m | 4 ++ Example/Classes/Views/SpotTableViewCell.m | 21 ++++++ 12 files changed, 191 insertions(+), 48 deletions(-) diff --git a/AFNetworking/AFHTTPClient.h b/AFNetworking/AFHTTPClient.h index c095d28..5eb0f02 100644 --- a/AFNetworking/AFHTTPClient.h +++ b/AFNetworking/AFHTTPClient.h @@ -49,6 +49,7 @@ @private NSURL *_baseURL; NSStringEncoding _stringEncoding; + NSMutableArray *_registeredHTTPOperationClassNames; NSMutableDictionary *_defaultHeaders; NSOperationQueue *_operationQueue; } @@ -96,6 +97,12 @@ */ - (id)initWithBaseURL:(NSURL *)url; +///---------------------------------- +/// @name Managing HTTP Operations +///---------------------------------- + +- (BOOL)registerHTTPOperationClass:(Class)operationClass; + ///---------------------------------- /// @name Managing HTTP Header Values ///---------------------------------- diff --git a/AFNetworking/AFHTTPClient.m b/AFNetworking/AFHTTPClient.m index 05f989a..b936e95 100644 --- a/AFNetworking/AFHTTPClient.m +++ b/AFNetworking/AFHTTPClient.m @@ -77,6 +77,7 @@ static NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSS @interface AFHTTPClient () @property (readwrite, nonatomic, retain) NSURL *baseURL; +@property (readwrite, nonatomic, retain) NSMutableArray *registeredHTTPOperationClassNames; @property (readwrite, nonatomic, retain) NSMutableDictionary *defaultHeaders; @property (readwrite, nonatomic, retain) NSOperationQueue *operationQueue; @end @@ -84,6 +85,7 @@ static NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSS @implementation AFHTTPClient @synthesize baseURL = _baseURL; @synthesize stringEncoding = _stringEncoding; +@synthesize registeredHTTPOperationClassNames = _registeredHTTPOperationClassNames; @synthesize defaultHeaders = _defaultHeaders; @synthesize operationQueue = _operationQueue; @@ -101,6 +103,8 @@ static NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSS self.stringEncoding = NSUTF8StringEncoding; + self.registeredHTTPOperationClassNames = [NSMutableArray array]; + self.defaultHeaders = [NSMutableDictionary dictionary]; // Accept HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1 @@ -124,11 +128,28 @@ static NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSS - (void)dealloc { [_baseURL release]; + [_registeredHTTPOperationClassNames release]; [_defaultHeaders release]; [_operationQueue release]; [super dealloc]; } +#pragma mark - + +- (BOOL)registerHTTPOperationClass:(Class)operationClass { + if (![operationClass conformsToProtocol:@protocol(AFHTTPClientOperation)]) { + return NO; + } + + NSString *className = NSStringFromClass(operationClass); + [self.registeredHTTPOperationClassNames removeObject:className]; + [self.registeredHTTPOperationClassNames insertObject:className atIndex:0]; + + return YES; +} + +#pragma mark - + - (NSString *)defaultValueForHeader:(NSString *)header { return [self.defaultHeaders valueForKey:header]; } @@ -228,16 +249,16 @@ static NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSS success:(void (^)(id object))success failure:(void (^)(NSHTTPURLResponse *response, NSError *error))failure { - AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:urlRequest success:^(__unused NSURLRequest *request, __unused NSHTTPURLResponse *response, id JSON) { - if (success) { - success(JSON); + AFHTTPRequestOperation *operation = nil; + NSString *className = nil; + NSEnumerator *enumerator = [self.registeredHTTPOperationClassNames reverseObjectEnumerator]; + while (!operation && (className = [enumerator nextObject])) { + Class class = NSClassFromString(className); + if (class && [class canProcessRequest:urlRequest]) { + operation = [class HTTPRequestOperationWithRequest:urlRequest success:success failure:failure]; } - } failure:^(__unused NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) { - if (failure) { - failure(response, error); - } - }]; - + } + [self.operationQueue addOperation:operation]; } diff --git a/AFNetworking/AFHTTPRequestOperation.h b/AFNetworking/AFHTTPRequestOperation.h index 714c1a4..0ac8678 100644 --- a/AFNetworking/AFHTTPRequestOperation.h +++ b/AFNetworking/AFHTTPRequestOperation.h @@ -23,9 +23,12 @@ #import #import "AFURLConnectionOperation.h" -@protocol AFHTTPClientRequestOperation -+ (BOOL)canInitWithRequest:(NSURLRequest *)request; +@protocol AFHTTPClientOperation ++ (BOOL)canProcessRequest:(NSURLRequest *)request; + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request; ++ (id)HTTPRequestOperationWithRequest:(NSURLRequest *)urlRequest + success:(void (^)(id object))success + failure:(void (^)(NSHTTPURLResponse *response, NSError *error))failure; @end /** @@ -59,7 +62,7 @@ @see NSOperation @see NSURLConnection */ -@interface AFHTTPRequestOperation : AFURLConnectionOperation { +@interface AFHTTPRequestOperation : AFURLConnectionOperation { @private NSIndexSet *_acceptableStatusCodes; NSSet *_acceptableContentTypes; @@ -80,9 +83,9 @@ /// @name Creating HTTP Request Operations ///--------------------------------------- -+ (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)urlRequest - success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSData *data))success - failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure; +//+ (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)urlRequest +// success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSData *data))success +// failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure; ///------------------------------- /// @name Validating HTTP Response diff --git a/AFNetworking/AFHTTPRequestOperation.m b/AFNetworking/AFHTTPRequestOperation.m index 8646873..7ed0025 100644 --- a/AFNetworking/AFHTTPRequestOperation.m +++ b/AFNetworking/AFHTTPRequestOperation.m @@ -31,34 +31,6 @@ @synthesize acceptableContentTypes = _acceptableContentTypes; @synthesize error = _HTTPError; -+ (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)urlRequest - success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSData *data))success - failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure -{ - AFHTTPRequestOperation *operation = [[[self alloc] initWithRequest:urlRequest] autorelease]; - operation.completionBlock = ^ { - if ([operation isCancelled]) { - return; - } - - if (operation.error) { - if (failure) { - dispatch_async(dispatch_get_main_queue(), ^(void) { - failure(operation.request, operation.response, operation.error); - }); - } - } else { - if (success) { - dispatch_async(dispatch_get_main_queue(), ^(void) { - success(operation.request, operation.response, operation.responseData); - }); - } - } - }; - - return operation; -} - - (id)initWithRequest:(NSURLRequest *)request { self = [super initWithRequest:request]; if (!self) { @@ -108,4 +80,42 @@ return !self.acceptableContentTypes || [self.acceptableContentTypes containsObject:[self.response MIMEType]]; } +#pragma mark - AFHTTPClientOperation + ++ (BOOL)canProcessRequest:(NSURLRequest *)request { + return NO; +} + ++ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { + return request; +} + ++ (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)urlRequest + success:(void (^)(id object))success + failure:(void (^)(NSHTTPURLResponse *response, NSError *error))failure +{ + AFHTTPRequestOperation *operation = [[[self alloc] initWithRequest:urlRequest] autorelease]; + operation.completionBlock = ^ { + if ([operation isCancelled]) { + return; + } + + if (operation.error) { + if (failure) { + dispatch_async(dispatch_get_main_queue(), ^(void) { + failure(operation.response, operation.error); + }); + } + } else { + if (success) { + dispatch_async(dispatch_get_main_queue(), ^(void) { + success(operation.responseData); + }); + } + } + }; + + return operation; +} + @end diff --git a/AFNetworking/AFImageRequestOperation.m b/AFNetworking/AFImageRequestOperation.m index 81e2fef..ba68b23 100644 --- a/AFNetworking/AFImageRequestOperation.m +++ b/AFNetworking/AFImageRequestOperation.m @@ -122,4 +122,27 @@ static dispatch_queue_t image_request_operation_processing_queue() { return _responseImage; } +#pragma mark - AFHTTPClientOperation + ++ (BOOL)canProcessRequest:(NSURLRequest *)request { + NSSet *acceptableContentTypes = [NSSet setWithObjects:@"image/tiff", @"image/jpeg", @"image/gif", @"image/png", @"image/ico", @"image/x-icon" @"image/bmp", @"image/x-bmp", @"image/x-xbitmap", @"image/x-win-bitmap", nil]; + NSSet *acceptablePathExtensions = [NSSet setWithObjects:@"tif", @"tiff", @"jpg", @"jpeg", @"gif", @"png", @"ico", @"bmp", @"cur", nil]; + return [acceptableContentTypes containsObject:[request valueForHTTPHeaderField:@"Accept"]] || [acceptablePathExtensions containsObject:[[request URL] pathExtension]]; +} + ++ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { + return request; +} + ++ (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)urlRequest + success:(void (^)(id object))success + failure:(void (^)(NSHTTPURLResponse *response, NSError *error))failure +{ + return [self imageRequestOperationWithRequest:urlRequest imageProcessingBlock:nil cacheName:nil success:^(NSURLRequest __unused *request, NSHTTPURLResponse __unused *response, UIImage *image) { + success(image); + } failure:^(NSURLRequest __unused *request, NSHTTPURLResponse *response, NSError *error) { + failure(response, error); + }]; +} + @end diff --git a/AFNetworking/AFJSONRequestOperation.m b/AFNetworking/AFJSONRequestOperation.m index b3a65e3..c3373e3 100644 --- a/AFNetworking/AFJSONRequestOperation.m +++ b/AFNetworking/AFJSONRequestOperation.m @@ -120,4 +120,26 @@ static dispatch_queue_t json_request_operation_processing_queue() { return _responseJSON; } +#pragma mark - AFHTTPClientOperation + ++ (BOOL)canProcessRequest:(NSURLRequest *)request { + NSSet *acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"application/x-javascript", @"text/javascript", @"text/x-javascript", @"text/x-json", @"text/json", @"text/plain", nil]; + return [acceptableContentTypes containsObject:[request valueForHTTPHeaderField:@"Accept"]] || [[[request URL] pathExtension] isEqualToString:@"json"]; +} + ++ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { + return request; +} + ++ (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)urlRequest + success:(void (^)(id object))success + failure:(void (^)(NSHTTPURLResponse *response, NSError *error))failure +{ + return [self JSONRequestOperationWithRequest:urlRequest success:^(NSURLRequest __unused *request, NSHTTPURLResponse __unused *response, id JSON) { + success(JSON); + } failure:^(NSURLRequest __unused *request, NSHTTPURLResponse *response, NSError *error) { + failure(response, error); + }]; +} + @end diff --git a/AFNetworking/AFPropertyListRequestOperation.m b/AFNetworking/AFPropertyListRequestOperation.m index 9d3847f..6fabbf5 100644 --- a/AFNetworking/AFPropertyListRequestOperation.m +++ b/AFNetworking/AFPropertyListRequestOperation.m @@ -113,4 +113,26 @@ static dispatch_queue_t property_list_request_operation_processing_queue() { return _responsePropertyList; } +#pragma mark - AFHTTPClientOperation + ++ (BOOL)canProcessRequest:(NSURLRequest *)request { + NSSet *acceptableContentTypes = [NSSet setWithObjects:@"application/x-plist", @"application/xml", nil]; + return [acceptableContentTypes containsObject:[request valueForHTTPHeaderField:@"Accept"]] || [[[request URL] pathExtension] isEqualToString:@"plist"]; +} + ++ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { + return request; +} + ++ (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)urlRequest + success:(void (^)(id object))success + failure:(void (^)(NSHTTPURLResponse *response, NSError *error))failure +{ + return [self propertyListRequestOperationWithRequest:urlRequest success:^(NSURLRequest __unused *request, NSHTTPURLResponse __unused *response, id propertyList) { + success(propertyList); + } failure:^(NSURLRequest __unused *request, NSHTTPURLResponse *response, NSError *error) { + failure(response, error); + }]; +} + @end diff --git a/AFNetworking/AFURLConnectionOperation.m b/AFNetworking/AFURLConnectionOperation.m index 9f89eda..fcc7001 100644 --- a/AFNetworking/AFURLConnectionOperation.m +++ b/AFNetworking/AFURLConnectionOperation.m @@ -142,6 +142,18 @@ static inline NSString * AFKeyPathFromOperationState(AFOperationState state) { [super dealloc]; } +- (void)setCompletionBlock:(void (^)(void))block { + if (!block) { + [super setCompletionBlock:nil]; + } + + __block id _blockSelf = [self retain]; + [super setCompletionBlock:^ { + block(); + [_blockSelf autorelease]; + }]; +} + - (NSInputStream *)inputStream { return self.request.HTTPBodyStream; } @@ -194,8 +206,6 @@ static inline NSString * AFKeyPathFromOperationState(AFOperationState state) { switch (state) { case AFHTTPOperationExecutingState: return YES; - case AFHTTPOperationFinishedState: - return [self isCancelled]; default: return NO; } diff --git a/AFNetworking/UIImageView+AFNetworking.m b/AFNetworking/UIImageView+AFNetworking.m index 6d0a10e..de894d5 100644 --- a/AFNetworking/UIImageView+AFNetworking.m +++ b/AFNetworking/UIImageView+AFNetworking.m @@ -78,7 +78,7 @@ static NSString * const kAFImageRequestOperationObjectKey = @"_af_imageRequestOp - (void)setImageWithURLRequest:(NSURLRequest *)urlRequest placeholderImage:(UIImage *)placeholderImage - success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response,UIImage *image))success + success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure { if (![urlRequest URL] || (![self.af_imageRequestOperation isCancelled] && [[urlRequest URL] isEqual:self.af_imageRequestOperation.request.URL])) { diff --git a/Example/AFNetworking Example.xcodeproj/project.pbxproj b/Example/AFNetworking Example.xcodeproj/project.pbxproj index 8b1bd1c..a07cfe0 100644 --- a/Example/AFNetworking Example.xcodeproj/project.pbxproj +++ b/Example/AFNetworking Example.xcodeproj/project.pbxproj @@ -233,12 +233,12 @@ F874B5C913E0AA6500B28E3E /* AFHTTPRequestOperation.m */, F874B5D413E0AA6500B28E3E /* AFJSONRequestOperation.h */, F874B5CC13E0AA6500B28E3E /* AFJSONRequestOperation.m */, + F8F4B16C143CD1410064C9E6 /* AFPropertyListRequestOperation.h */, + F8F4B16D143CD1410064C9E6 /* AFPropertyListRequestOperation.m */, F8FBFA96142AA237001409DB /* AFHTTPClient.h */, F8FBFA97142AA238001409DB /* AFHTTPClient.m */, F874B5D313E0AA6500B28E3E /* AFImageRequestOperation.h */, F874B5CB13E0AA6500B28E3E /* AFImageRequestOperation.m */, - F8F4B16C143CD1410064C9E6 /* AFPropertyListRequestOperation.h */, - F8F4B16D143CD1410064C9E6 /* AFPropertyListRequestOperation.m */, F874B5D213E0AA6500B28E3E /* AFImageCache.h */, F874B5CA13E0AA6500B28E3E /* AFImageCache.m */, F874B5D513E0AA6500B28E3E /* AFNetworkActivityIndicatorManager.h */, diff --git a/Example/Classes/AFGowallaAPIClient.m b/Example/Classes/AFGowallaAPIClient.m index 76ac24d..e83a320 100644 --- a/Example/Classes/AFGowallaAPIClient.m +++ b/Example/Classes/AFGowallaAPIClient.m @@ -22,6 +22,8 @@ #import "AFGowallaAPIClient.h" +#import "AFJSONRequestOperation.h" + // Replace this with your own API Key, available at http://api.gowalla.com/api/keys/ NSString * const kAFGowallaClientID = @"e7ccb7d3d2414eb2af4663fc91eb2793"; @@ -54,6 +56,8 @@ NSString * const kAFGowallaBaseURLString = @"https://api.gowalla.com/"; // X-UDID HTTP Header [self setDefaultHeader:@"X-UDID" value:[[UIDevice currentDevice] uniqueIdentifier]]; + [self registerHTTPOperationClass:[AFJSONRequestOperation class]]; + return self; } diff --git a/Example/Classes/Views/SpotTableViewCell.m b/Example/Classes/Views/SpotTableViewCell.m index c8feb4c..b576281 100644 --- a/Example/Classes/Views/SpotTableViewCell.m +++ b/Example/Classes/Views/SpotTableViewCell.m @@ -43,6 +43,7 @@ self.detailTextLabel.backgroundColor = self.backgroundColor; self.imageView.backgroundColor = self.backgroundColor; + self.imageView.contentMode = UIViewContentModeScaleAspectFit; self.selectionStyle = UITableViewCellSelectionStyleGray; @@ -73,4 +74,24 @@ self.detailTextLabel.text = nil; } +#pragma mark - UIView + +- (void)layoutSubviews { + [super layoutSubviews]; + CGRect imageViewFrame = self.imageView.frame; + CGRect textLabelFrame = self.textLabel.frame; + CGRect detailTextLabelFrame = self.detailTextLabel.frame; + + imageViewFrame.origin = CGPointMake(10.0f, 10.0f); + imageViewFrame.size = CGSizeMake(50.0f, 50.0f); + textLabelFrame.origin.x = imageViewFrame.size.width + 25.0f; + detailTextLabelFrame.origin.x = textLabelFrame.origin.x; + textLabelFrame.size.width = 240.0f; + detailTextLabelFrame.size.width = textLabelFrame.size.width; + + self.textLabel.frame = textLabelFrame; + self.detailTextLabel.frame = detailTextLabelFrame; + self.imageView.frame = imageViewFrame; +} + @end