/BlocksKit/NSURLConnection+BlocksKit.m

http://github.com/zwaldowski/BlocksKit · 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. #import "NSURLConnection+BlocksKit.h"
  6. #import "NSObject+AssociatedObjects.h"
  7. #import "A2BlockDelegate+BlocksKit.h"
  8. #import <objc/runtime.h>
  9. #pragma mark Private
  10. static char kResponseDataKey;
  11. static char kResponseKey;
  12. static char kResponseLengthKey;
  13. @interface NSURLConnection (BlocksKitPrivate)
  14. @property (nonatomic, retain) NSMutableData *bk_responseData;
  15. @property (nonatomic, retain) NSURLResponse *bk_response;
  16. @property (nonatomic) NSUInteger bk_responseLength;
  17. @end
  18. @implementation NSURLConnection (BlocksKitPrivate)
  19. - (NSMutableData *)bk_responseData {
  20. return [self associatedValueForKey:&kResponseDataKey];
  21. }
  22. - (void)setBk_responseData:(NSMutableData *)responseData {
  23. [self associateValue:responseData withKey:&kResponseDataKey];
  24. }
  25. - (NSURLResponse *)bk_response {
  26. return [self associatedValueForKey:&kResponseKey];
  27. }
  28. - (void)setBk_response:(NSURLResponse *)response {
  29. return [self associateValue:response withKey:&kResponseKey];
  30. }
  31. - (NSUInteger)bk_responseLength {
  32. return [[self associatedValueForKey:&kResponseLengthKey] unsignedIntegerValue];
  33. }
  34. - (void)setBk_responseLength:(NSUInteger)responseLength {
  35. NSNumber *value = [NSNumber numberWithUnsignedInteger:responseLength];
  36. return [self associateValue:value withKey:&kResponseLengthKey];
  37. }
  38. @end
  39. #pragma mark - BKURLConnectionInformalDelegate - iOS 4.3 & Snow Leopard support
  40. @protocol BKURLConnectionInformalDelegate <NSObject>
  41. - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
  42. - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
  43. - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite;
  44. - (void)connectionDidFinishLoading:(NSURLConnection *)connection;
  45. - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
  46. @end
  47. @interface A2DynamicBKURLConnectionInformalDelegate : A2DynamicDelegate <BKURLConnectionInformalDelegate>
  48. @end
  49. @implementation A2DynamicBKURLConnectionInformalDelegate
  50. - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
  51. id realDelegate = self.realDelegate;
  52. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveResponse:)])
  53. [realDelegate connection:connection didReceiveResponse:response];
  54. connection.bk_responseLength = 0;
  55. [connection.bk_responseData setLength:0];
  56. connection.bk_response = response;
  57. void (^block)(NSURLConnection *, NSURLResponse *) = [self blockImplementationForMethod:_cmd];
  58. if (block)
  59. block(connection, response);
  60. }
  61. - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
  62. connection.bk_responseLength += data.length;
  63. void (^block)(CGFloat) = connection.downloadBlock;
  64. if (block && connection.bk_response && connection.bk_response.expectedContentLength != NSURLResponseUnknownLength)
  65. block((CGFloat)connection.bk_responseLength / (CGFloat)connection.bk_response.expectedContentLength);
  66. id realDelegate = self.realDelegate;
  67. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveData:)]) {
  68. [realDelegate connection:connection didReceiveData:data];
  69. return;
  70. }
  71. NSMutableData *responseData = connection.bk_responseData;
  72. if (!responseData) {
  73. responseData = [NSMutableData data];
  74. connection.bk_responseData = responseData;
  75. }
  76. [responseData appendData:data];
  77. }
  78. - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite {
  79. id realDelegate = self.realDelegate;
  80. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:)])
  81. [realDelegate connection:connection didSendBodyData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
  82. void (^block)(CGFloat) = connection.uploadBlock;
  83. if (block)
  84. block((CGFloat)totalBytesWritten/(CGFloat)totalBytesExpectedToWrite);
  85. }
  86. - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
  87. id realDelegate = self.realDelegate;
  88. if (realDelegate && [realDelegate respondsToSelector:@selector(connectionDidFinishLoading:)])
  89. [realDelegate connectionDidFinishLoading:connection];
  90. if (!connection.bk_responseData.length)
  91. connection.bk_responseData = nil;
  92. void(^block)(NSURLConnection *, NSURLResponse *, NSData *) = connection.successBlock;
  93. if (block)
  94. block(connection, connection.bk_response, connection.bk_responseData);
  95. }
  96. - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
  97. id realDelegate = self.realDelegate;
  98. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didFailWithError:)])
  99. [realDelegate connection:connection didFailWithError:error];
  100. connection.bk_responseLength = 0;
  101. [connection.bk_responseData setLength:0];
  102. void (^block)(NSURLConnection *, NSError *) = [self blockImplementationForMethod:_cmd];
  103. if (block)
  104. block(connection, error);
  105. }
  106. @end
  107. #pragma mark - NSURLConnectionDelegate - iOS 5.0 & Lion support
  108. #ifdef NSURLConnectionDataDelegate
  109. @interface A2DynamicNSURLConnectionDelegate : A2DynamicDelegate <NSURLConnectionDataDelegate>
  110. #else
  111. @interface A2DynamicNSURLConnectionDelegate : A2DynamicDelegate <NSURLConnectionDelegate>
  112. #endif
  113. @end
  114. @implementation A2DynamicNSURLConnectionDelegate
  115. - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
  116. id realDelegate = self.realDelegate;
  117. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveResponse:)])
  118. [realDelegate connection:connection didReceiveResponse:response];
  119. connection.bk_responseLength = 0;
  120. if (connection.bk_responseData)
  121. [connection.bk_responseData setLength:0];
  122. connection.bk_response = response;
  123. void (^block)(NSURLConnection *, NSURLResponse *) = [self blockImplementationForMethod:_cmd];
  124. if (block)
  125. block(connection, response);
  126. }
  127. - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
  128. connection.bk_responseLength += data.length;
  129. void (^block)(CGFloat) = connection.downloadBlock;
  130. if (block && connection.bk_response && connection.bk_response.expectedContentLength != NSURLResponseUnknownLength)
  131. block((CGFloat)connection.bk_responseLength / (CGFloat)connection.bk_response.expectedContentLength);
  132. id realDelegate = self.realDelegate;
  133. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveData:)]) {
  134. [realDelegate connection:connection didReceiveData:data];
  135. return;
  136. }
  137. NSMutableData *responseData = connection.bk_responseData;
  138. if (!responseData) {
  139. responseData = [NSMutableData data];
  140. connection.bk_responseData = responseData;
  141. }
  142. [responseData appendData:data];
  143. }
  144. - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite {
  145. id realDelegate = self.realDelegate;
  146. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:)])
  147. [realDelegate connection:connection didSendBodyData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
  148. void (^block)(CGFloat) = connection.uploadBlock;
  149. if (block)
  150. block((CGFloat)totalBytesWritten/(CGFloat)totalBytesExpectedToWrite);
  151. }
  152. - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
  153. id realDelegate = self.realDelegate;
  154. if (realDelegate && [realDelegate respondsToSelector:@selector(connectionDidFinishLoading:)])
  155. [realDelegate connectionDidFinishLoading:connection];
  156. if (!connection.bk_responseData.length)
  157. connection.bk_responseData = nil;
  158. void(^block)(NSURLConnection *, NSURLResponse *, NSData *) = connection.successBlock;
  159. if (block)
  160. block(connection, connection.bk_response, connection.bk_responseData);
  161. }
  162. - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
  163. id realDelegate = self.realDelegate;
  164. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didFailWithError:)])
  165. [realDelegate connection:connection didFailWithError:error];
  166. connection.bk_responseLength = 0;
  167. [connection.bk_responseData setLength:0];
  168. void (^block)(NSURLConnection *, NSError *) = [self blockImplementationForMethod:_cmd];
  169. if (block)
  170. block(connection, error);
  171. }
  172. @end
  173. #pragma mark - Category
  174. static NSString *const kSuccessBlockKey = @"NSURLConnectionDidFinishLoading";
  175. static NSString *const kUploadBlockKey = @"NSURLConnectionDidSendData";
  176. static NSString *const kDownloadBlockKey = @"NSURLConnectionDidRecieveData";
  177. @implementation NSURLConnection (BlocksKit)
  178. @dynamic delegate, responseBlock, failureBlock;
  179. + (void)load {
  180. @autoreleasepool {
  181. [self registerDynamicDelegate];
  182. [self linkCategoryBlockProperty:@"responseBlock" withDelegateMethod:@selector(connection:didReceiveResponse:)];
  183. [self linkCategoryBlockProperty:@"failureBlock" withDelegateMethod:@selector(connection:didFailWithError:)];
  184. }
  185. }
  186. #pragma mark Initializers
  187. + (NSURLConnection*)connectionWithRequest:(NSURLRequest *)request {
  188. return [[[[self class] alloc] initWithRequest:request] autorelease];
  189. }
  190. + (NSURLConnection *)startConnectionWithRequest:(NSURLRequest *)request successHandler:(void(^)(NSURLConnection *, NSURLResponse *, NSData *))success failureHandler:(void(^)(NSURLConnection *, NSError *))failure {
  191. NSURLConnection *connection = [[[self class] alloc] initWithRequest:request];
  192. connection.successBlock = success;
  193. connection.failureBlock = failure;
  194. [connection start];
  195. return [connection autorelease];
  196. }
  197. - (id)initWithRequest:(NSURLRequest *)request {
  198. return [self initWithRequest:request completionHandler:NULL];
  199. }
  200. - (id)initWithRequest:(NSURLRequest *)request completionHandler:(void(^)(NSURLConnection *, NSURLResponse *, NSData *))block {
  201. Protocol *delegateProtocol = objc_getProtocol("NSURLConnectionDelegate");
  202. if (!delegateProtocol)
  203. delegateProtocol = @protocol(BKURLConnectionInformalDelegate);
  204. if ((self = [self initWithRequest:request delegate:[self dynamicDelegateForProtocol:delegateProtocol] startImmediately:NO]))
  205. self.successBlock = block;
  206. return self;
  207. }
  208. - (void)startWithCompletionBlock:(void(^)(NSURLConnection *, NSURLResponse *, NSData *))block {
  209. self.successBlock = block;
  210. [self start];
  211. }
  212. #pragma mark Properties
  213. - (void(^)(NSURLConnection *, NSURLResponse *, NSData *))successBlock {
  214. return [[self.dynamicDelegate handlers] objectForKey:kSuccessBlockKey];
  215. }
  216. - (void)setSuccessBlock:(void(^)(NSURLConnection *, NSURLResponse *, NSData *))block {
  217. if (block)
  218. [[self.dynamicDelegate handlers] setObject:block forKey:kSuccessBlockKey];
  219. else
  220. [[self.dynamicDelegate handlers] removeObjectForKey:kSuccessBlockKey];
  221. }
  222. - (void(^)(CGFloat))uploadBlock {
  223. return [[self.dynamicDelegate handlers] objectForKey:kUploadBlockKey];
  224. }
  225. - (void)setUploadBlock:(void(^)(CGFloat))block {
  226. if (block)
  227. [[self.dynamicDelegate handlers] setObject:block forKey:kUploadBlockKey];
  228. else
  229. [[self.dynamicDelegate handlers] removeObjectForKey:kUploadBlockKey];
  230. }
  231. - (void(^)(CGFloat))downloadBlock {
  232. return [[self.dynamicDelegate handlers] objectForKey:kDownloadBlockKey];
  233. }
  234. - (void)setDownloadBlock:(void(^)(CGFloat))block {
  235. if (block)
  236. [[self.dynamicDelegate handlers] setObject:block forKey:kDownloadBlockKey];
  237. else
  238. [[self.dynamicDelegate handlers] removeObjectForKey:kDownloadBlockKey];
  239. }
  240. @end