/BlocksKit/NSURLConnection+BlocksKit.m
Objective C | 318 lines | 236 code | 78 blank | 4 comment | 46 complexity | 98512c530890b078846cfcb560f566fc MD5 | raw file
1// 2// NSURLConnection+BlocksKit.m 3// BlocksKit 4// 5 6#import "NSURLConnection+BlocksKit.h" 7#import "NSObject+AssociatedObjects.h" 8#import "A2BlockDelegate+BlocksKit.h" 9#import <objc/runtime.h> 10 11#pragma mark Private 12 13static char kResponseDataKey; 14static char kResponseKey; 15static char kResponseLengthKey; 16 17@interface NSURLConnection (BlocksKitPrivate) 18@property (nonatomic, retain) NSMutableData *bk_responseData; 19@property (nonatomic, retain) NSURLResponse *bk_response; 20@property (nonatomic) NSUInteger bk_responseLength; 21@end 22 23@implementation NSURLConnection (BlocksKitPrivate) 24 25- (NSMutableData *)bk_responseData { 26 return [self associatedValueForKey:&kResponseDataKey]; 27} 28 29- (void)setBk_responseData:(NSMutableData *)responseData { 30 [self associateValue:responseData withKey:&kResponseDataKey]; 31} 32 33- (NSURLResponse *)bk_response { 34 return [self associatedValueForKey:&kResponseKey]; 35} 36 37- (void)setBk_response:(NSURLResponse *)response { 38 return [self associateValue:response withKey:&kResponseKey]; 39} 40 41- (NSUInteger)bk_responseLength { 42 return [[self associatedValueForKey:&kResponseLengthKey] unsignedIntegerValue]; 43} 44 45- (void)setBk_responseLength:(NSUInteger)responseLength { 46 NSNumber *value = [NSNumber numberWithUnsignedInteger:responseLength]; 47 return [self associateValue:value withKey:&kResponseLengthKey]; 48} 49 50@end 51 52#pragma mark - BKURLConnectionInformalDelegate - iOS 4.3 & Snow Leopard support 53 54@protocol BKURLConnectionInformalDelegate <NSObject> 55- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response; 56- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data; 57- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite; 58- (void)connectionDidFinishLoading:(NSURLConnection *)connection; 59- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error; 60@end 61 62@interface A2DynamicBKURLConnectionInformalDelegate : A2DynamicDelegate <BKURLConnectionInformalDelegate> 63 64@end 65 66@implementation A2DynamicBKURLConnectionInformalDelegate 67 68- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { 69 id realDelegate = self.realDelegate; 70 if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveResponse:)]) 71 [realDelegate connection:connection didReceiveResponse:response]; 72 73 connection.bk_responseLength = 0; 74 [connection.bk_responseData setLength:0]; 75 76 connection.bk_response = response; 77 78 void (^block)(NSURLConnection *, NSURLResponse *) = [self blockImplementationForMethod:_cmd]; 79 if (block) 80 block(connection, response); 81} 82 83- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { 84 connection.bk_responseLength += data.length; 85 86 void (^block)(CGFloat) = connection.downloadBlock; 87 if (block && connection.bk_response && connection.bk_response.expectedContentLength != NSURLResponseUnknownLength) 88 block((CGFloat)connection.bk_responseLength / (CGFloat)connection.bk_response.expectedContentLength); 89 90 id realDelegate = self.realDelegate; 91 if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveData:)]) { 92 [realDelegate connection:connection didReceiveData:data]; 93 return; 94 } 95 96 NSMutableData *responseData = connection.bk_responseData; 97 if (!responseData) { 98 responseData = [NSMutableData data]; 99 connection.bk_responseData = responseData; 100 } 101 102 [responseData appendData:data]; 103} 104 105- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite { 106 id realDelegate = self.realDelegate; 107 if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:)]) 108 [realDelegate connection:connection didSendBodyData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite]; 109 110 void (^block)(CGFloat) = connection.uploadBlock; 111 if (block) 112 block((CGFloat)totalBytesWritten/(CGFloat)totalBytesExpectedToWrite); 113} 114 115- (void)connectionDidFinishLoading:(NSURLConnection *)connection { 116 id realDelegate = self.realDelegate; 117 if (realDelegate && [realDelegate respondsToSelector:@selector(connectionDidFinishLoading:)]) 118 [realDelegate connectionDidFinishLoading:connection]; 119 120 if (!connection.bk_responseData.length) 121 connection.bk_responseData = nil; 122 123 void(^block)(NSURLConnection *, NSURLResponse *, NSData *) = connection.successBlock; 124 if (block) 125 block(connection, connection.bk_response, connection.bk_responseData); 126} 127 128- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 129 id realDelegate = self.realDelegate; 130 if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didFailWithError:)]) 131 [realDelegate connection:connection didFailWithError:error]; 132 133 connection.bk_responseLength = 0; 134 [connection.bk_responseData setLength:0]; 135 136 void (^block)(NSURLConnection *, NSError *) = [self blockImplementationForMethod:_cmd]; 137 if (block) 138 block(connection, error); 139} 140 141@end 142 143#pragma mark - NSURLConnectionDelegate - iOS 5.0 & Lion support 144 145#ifdef NSURLConnectionDataDelegate 146@interface A2DynamicNSURLConnectionDelegate : A2DynamicDelegate <NSURLConnectionDataDelegate> 147#else 148@interface A2DynamicNSURLConnectionDelegate : A2DynamicDelegate <NSURLConnectionDelegate> 149#endif 150 151@end 152 153@implementation A2DynamicNSURLConnectionDelegate 154 155- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { 156 id realDelegate = self.realDelegate; 157 if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveResponse:)]) 158 [realDelegate connection:connection didReceiveResponse:response]; 159 160 connection.bk_responseLength = 0; 161 162 if (connection.bk_responseData) 163 [connection.bk_responseData setLength:0]; 164 165 connection.bk_response = response; 166 167 void (^block)(NSURLConnection *, NSURLResponse *) = [self blockImplementationForMethod:_cmd]; 168 if (block) 169 block(connection, response); 170} 171 172- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { 173 connection.bk_responseLength += data.length; 174 175 void (^block)(CGFloat) = connection.downloadBlock; 176 if (block && connection.bk_response && connection.bk_response.expectedContentLength != NSURLResponseUnknownLength) 177 block((CGFloat)connection.bk_responseLength / (CGFloat)connection.bk_response.expectedContentLength); 178 179 id realDelegate = self.realDelegate; 180 if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveData:)]) { 181 [realDelegate connection:connection didReceiveData:data]; 182 return; 183 } 184 185 NSMutableData *responseData = connection.bk_responseData; 186 if (!responseData) { 187 responseData = [NSMutableData data]; 188 connection.bk_responseData = responseData; 189 } 190 191 [responseData appendData:data]; 192} 193 194- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite { 195 id realDelegate = self.realDelegate; 196 if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:)]) 197 [realDelegate connection:connection didSendBodyData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite]; 198 199 void (^block)(CGFloat) = connection.uploadBlock; 200 if (block) 201 block((CGFloat)totalBytesWritten/(CGFloat)totalBytesExpectedToWrite); 202} 203 204- (void)connectionDidFinishLoading:(NSURLConnection *)connection { 205 id realDelegate = self.realDelegate; 206 if (realDelegate && [realDelegate respondsToSelector:@selector(connectionDidFinishLoading:)]) 207 [realDelegate connectionDidFinishLoading:connection]; 208 209 if (!connection.bk_responseData.length) 210 connection.bk_responseData = nil; 211 212 void(^block)(NSURLConnection *, NSURLResponse *, NSData *) = connection.successBlock; 213 if (block) 214 block(connection, connection.bk_response, connection.bk_responseData); 215} 216 217- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 218 id realDelegate = self.realDelegate; 219 if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didFailWithError:)]) 220 [realDelegate connection:connection didFailWithError:error]; 221 222 connection.bk_responseLength = 0; 223 [connection.bk_responseData setLength:0]; 224 225 void (^block)(NSURLConnection *, NSError *) = [self blockImplementationForMethod:_cmd]; 226 if (block) 227 block(connection, error); 228} 229 230@end 231 232#pragma mark - Category 233 234static NSString *const kSuccessBlockKey = @"NSURLConnectionDidFinishLoading"; 235static NSString *const kUploadBlockKey = @"NSURLConnectionDidSendData"; 236static NSString *const kDownloadBlockKey = @"NSURLConnectionDidRecieveData"; 237 238@implementation NSURLConnection (BlocksKit) 239 240@dynamic delegate, responseBlock, failureBlock; 241 242+ (void)load { 243 @autoreleasepool { 244 [self registerDynamicDelegate]; 245 [self linkCategoryBlockProperty:@"responseBlock" withDelegateMethod:@selector(connection:didReceiveResponse:)]; 246 [self linkCategoryBlockProperty:@"failureBlock" withDelegateMethod:@selector(connection:didFailWithError:)]; 247 } 248} 249 250#pragma mark Initializers 251 252+ (NSURLConnection*)connectionWithRequest:(NSURLRequest *)request { 253 return [[[[self class] alloc] initWithRequest:request] autorelease]; 254} 255 256+ (NSURLConnection *)startConnectionWithRequest:(NSURLRequest *)request successHandler:(void(^)(NSURLConnection *, NSURLResponse *, NSData *))success failureHandler:(void(^)(NSURLConnection *, NSError *))failure { 257 NSURLConnection *connection = [[[self class] alloc] initWithRequest:request]; 258 connection.successBlock = success; 259 connection.failureBlock = failure; 260 [connection start]; 261 return [connection autorelease]; 262} 263 264- (id)initWithRequest:(NSURLRequest *)request { 265 return [self initWithRequest:request completionHandler:NULL]; 266} 267 268- (id)initWithRequest:(NSURLRequest *)request completionHandler:(void(^)(NSURLConnection *, NSURLResponse *, NSData *))block { 269 Protocol *delegateProtocol = objc_getProtocol("NSURLConnectionDelegate"); 270 if (!delegateProtocol) 271 delegateProtocol = @protocol(BKURLConnectionInformalDelegate); 272 if ((self = [self initWithRequest:request delegate:[self dynamicDelegateForProtocol:delegateProtocol] startImmediately:NO])) 273 self.successBlock = block; 274 return self; 275} 276 277- (void)startWithCompletionBlock:(void(^)(NSURLConnection *, NSURLResponse *, NSData *))block { 278 self.successBlock = block; 279 [self start]; 280} 281 282#pragma mark Properties 283 284- (void(^)(NSURLConnection *, NSURLResponse *, NSData *))successBlock { 285 return [[self.dynamicDelegate handlers] objectForKey:kSuccessBlockKey]; 286} 287 288- (void)setSuccessBlock:(void(^)(NSURLConnection *, NSURLResponse *, NSData *))block { 289 if (block) 290 [[self.dynamicDelegate handlers] setObject:block forKey:kSuccessBlockKey]; 291 else 292 [[self.dynamicDelegate handlers] removeObjectForKey:kSuccessBlockKey]; 293 294} 295 296- (void(^)(CGFloat))uploadBlock { 297 return [[self.dynamicDelegate handlers] objectForKey:kUploadBlockKey]; 298} 299 300- (void)setUploadBlock:(void(^)(CGFloat))block { 301 if (block) 302 [[self.dynamicDelegate handlers] setObject:block forKey:kUploadBlockKey]; 303 else 304 [[self.dynamicDelegate handlers] removeObjectForKey:kUploadBlockKey]; 305} 306 307- (void(^)(CGFloat))downloadBlock { 308 return [[self.dynamicDelegate handlers] objectForKey:kDownloadBlockKey]; 309} 310 311- (void)setDownloadBlock:(void(^)(CGFloat))block { 312 if (block) 313 [[self.dynamicDelegate handlers] setObject:block forKey:kDownloadBlockKey]; 314 else 315 [[self.dynamicDelegate handlers] removeObjectForKey:kDownloadBlockKey]; 316} 317 318@end