NSURLConnection+BlocksKit.m 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. //
  2. // NSURLConnection+BlocksKit.m
  3. // BlocksKit
  4. //
  5. #import <objc/runtime.h>
  6. #import "A2DynamicDelegate.h"
  7. #import "NSObject+A2BlockDelegate.h"
  8. #import "NSObject+A2DynamicDelegate.h"
  9. #import "NSURLConnection+BlocksKit.h"
  10. #pragma mark Private
  11. static const void *BKResponseDataKey = &BKResponseDataKey;
  12. static const void *BKResponseKey = &BKResponseKey;
  13. static const void *BKResponseLengthKey = &BKResponseLengthKey;
  14. @interface NSURLConnection (BlocksKitPrivate)
  15. @property (nonatomic, retain, setter = bk_setResponseData:) NSMutableData *bk_responseData;
  16. @property (nonatomic, retain, setter = bk_setResponse:) NSURLResponse *bk_response;
  17. @property (nonatomic, setter = bk_setResponseLength:) NSUInteger bk_responseLength;
  18. @end
  19. @implementation NSURLConnection (BlocksKitPrivate)
  20. - (NSMutableData *)bk_responseData
  21. {
  22. return objc_getAssociatedObject(self, BKResponseDataKey);
  23. }
  24. - (void)bk_setResponseData:(NSMutableData *)responseData
  25. {
  26. objc_setAssociatedObject(self, BKResponseDataKey, responseData, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  27. }
  28. - (NSURLResponse *)bk_response
  29. {
  30. return objc_getAssociatedObject(self, BKResponseKey);
  31. }
  32. - (void)bk_setResponse:(NSURLResponse *)response
  33. {
  34. objc_setAssociatedObject(self, BKResponseKey, response, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  35. }
  36. - (NSUInteger)bk_responseLength
  37. {
  38. return [objc_getAssociatedObject(self, BKResponseLengthKey) unsignedIntegerValue];
  39. }
  40. - (void)bk_setResponseLength:(NSUInteger)responseLength
  41. {
  42. objc_setAssociatedObject(self, BKResponseLengthKey, @(responseLength), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  43. }
  44. @end
  45. #pragma mark - BKURLConnectionInformalDelegate - iOS 4.3 & Snow Leopard support
  46. @protocol BKURLConnectionInformalDelegate <NSObject>
  47. - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
  48. - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
  49. - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite;
  50. - (void)connectionDidFinishLoading:(NSURLConnection *)connection;
  51. - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
  52. - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
  53. - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace;
  54. @end
  55. @interface A2DynamicBKURLConnectionInformalDelegate : A2DynamicDelegate <BKURLConnectionInformalDelegate>
  56. @end
  57. @implementation A2DynamicBKURLConnectionInformalDelegate
  58. - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
  59. {
  60. id realDelegate = self.realDelegate;
  61. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveResponse:)])
  62. [realDelegate connection:connection didReceiveResponse:response];
  63. connection.bk_responseLength = 0;
  64. [connection.bk_responseData setLength:0];
  65. connection.bk_response = response;
  66. void (^block)(NSURLConnection *, NSURLResponse *) = [self blockImplementationForMethod:_cmd];
  67. if (block) block(connection, response);
  68. }
  69. - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
  70. {
  71. connection.bk_responseLength += data.length;
  72. void (^block)(double) = connection.bk_downloadBlock;
  73. if (block && connection.bk_response && connection.bk_response.expectedContentLength != NSURLResponseUnknownLength)
  74. block((double)connection.bk_responseLength / (double)connection.bk_response.expectedContentLength);
  75. id realDelegate = self.realDelegate;
  76. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveData:)]) {
  77. [realDelegate connection:connection didReceiveData:data];
  78. return;
  79. }
  80. NSMutableData *responseData = connection.bk_responseData;
  81. if (!responseData) {
  82. responseData = [NSMutableData data];
  83. connection.bk_responseData = responseData;
  84. }
  85. [responseData appendData:data];
  86. }
  87. - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
  88. {
  89. id realDelegate = self.realDelegate;
  90. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:)])
  91. [realDelegate connection:connection didSendBodyData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
  92. void (^block)(double) = connection.bk_uploadBlock;
  93. if (block) block((double)totalBytesWritten/(double)totalBytesExpectedToWrite);
  94. }
  95. - (void)connectionDidFinishLoading:(NSURLConnection *)connection
  96. {
  97. id realDelegate = self.realDelegate;
  98. if (realDelegate && [realDelegate respondsToSelector:@selector(connectionDidFinishLoading:)])
  99. [realDelegate connectionDidFinishLoading:connection];
  100. if (!connection.bk_responseData.length)
  101. connection.bk_responseData = nil;
  102. void (^block)(NSURLConnection *, NSURLResponse *, NSData *) = connection.bk_successBlock;
  103. if (block) block(connection, connection.bk_response, connection.bk_responseData);
  104. }
  105. - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
  106. {
  107. id realDelegate = self.realDelegate;
  108. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didFailWithError:)])
  109. [realDelegate connection:connection didFailWithError:error];
  110. connection.bk_responseLength = 0;
  111. [connection.bk_responseData setLength:0];
  112. void (^block)(NSURLConnection *, NSError *) = [self blockImplementationForMethod:_cmd];
  113. if (block) block(connection, error);
  114. }
  115. - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
  116. {
  117. id realDelegate = self.realDelegate;
  118. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveAuthenticationChallenge:)])
  119. [realDelegate connection:connection didReceiveAuthenticationChallenge:challenge];
  120. }
  121. - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
  122. {
  123. id realDelegate = self.realDelegate;
  124. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:canAuthenticateAgainstProtectionSpace:)])
  125. return [realDelegate connection:connection canAuthenticateAgainstProtectionSpace:protectionSpace];
  126. return NO;
  127. }
  128. @end
  129. #pragma mark - NSURLConnectionDelegate - iOS 5.0 & Lion support
  130. @interface A2DynamicNSURLConnectionDelegate : A2DynamicDelegate <NSURLConnectionDelegate>
  131. @end
  132. @implementation A2DynamicNSURLConnectionDelegate
  133. - (BOOL)conformsToProtocol:(Protocol *)protocol
  134. {
  135. Protocol *dataDelegateProtocol = objc_getProtocol("NSURLConnectionDataDelegate");
  136. return (protocol_isEqual(protocol, dataDelegateProtocol) || protocol_isEqual(protocol, self.protocol) || [super conformsToProtocol:protocol]);
  137. }
  138. - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
  139. {
  140. id realDelegate = self.realDelegate;
  141. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didFailWithError:)])
  142. [realDelegate connection:connection didFailWithError:error];
  143. connection.bk_responseLength = 0;
  144. [connection.bk_responseData setLength:0];
  145. void (^block)(NSURLConnection *, NSError *) = [self blockImplementationForMethod:_cmd];
  146. if (block) block(connection, error);
  147. }
  148. - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
  149. {
  150. id realDelegate = self.realDelegate;
  151. if (realDelegate && [realDelegate respondsToSelector:@selector(connectionShouldUseCredentialStorage:)])
  152. return [realDelegate connectionShouldUseCredentialStorage:connection];
  153. return YES;
  154. }
  155. - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
  156. {
  157. id realDelegate = self.realDelegate;
  158. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:willSendRequestForAuthenticationChallenge:)])
  159. [realDelegate connection:connection willSendRequestForAuthenticationChallenge:challenge];
  160. }
  161. #pragma mark - NSURLConnectionDataDelegate
  162. - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response
  163. {
  164. id realDelegate = self.realDelegate;
  165. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:willSendRequest:redirectResponse:)])
  166. return [realDelegate connection:connection willSendRequest:request redirectResponse:response];
  167. return request;
  168. }
  169. - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
  170. {
  171. id realDelegate = self.realDelegate;
  172. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveResponse:)])
  173. [realDelegate connection:connection didReceiveResponse:response];
  174. connection.bk_responseLength = 0;
  175. if (connection.bk_responseData)
  176. [connection.bk_responseData setLength:0];
  177. connection.bk_response = response;
  178. void (^block)(NSURLConnection *, NSURLResponse *) = [self blockImplementationForMethod:_cmd];
  179. if (block) block(connection, response);
  180. }
  181. - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
  182. {
  183. connection.bk_responseLength += data.length;
  184. void (^block)(double) = connection.bk_downloadBlock;
  185. if (block && connection.bk_response && connection.bk_response.expectedContentLength != NSURLResponseUnknownLength)
  186. block((double)connection.bk_responseLength / (double)connection.bk_response.expectedContentLength);
  187. id realDelegate = self.realDelegate;
  188. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveData:)]) {
  189. [realDelegate connection:connection didReceiveData:data];
  190. return;
  191. }
  192. NSMutableData *responseData = connection.bk_responseData;
  193. if (!responseData) {
  194. responseData = [NSMutableData data];
  195. connection.bk_responseData = responseData;
  196. }
  197. [responseData appendData:data];
  198. }
  199. - (NSInputStream *)connection:(NSURLConnection *)connection needNewBodyStream:(NSURLRequest *)request
  200. {
  201. id realDelegate = self.realDelegate;
  202. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:needNewBodyStream:)])
  203. return [realDelegate connection:connection needNewBodyStream:request];
  204. return nil;
  205. }
  206. - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
  207. {
  208. id realDelegate = self.realDelegate;
  209. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:)])
  210. [realDelegate connection:connection didSendBodyData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
  211. void (^block)(double) = connection.bk_uploadBlock;
  212. if (block)
  213. block((double)totalBytesWritten/(double)totalBytesExpectedToWrite);
  214. }
  215. - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
  216. {
  217. id realDelegate = self.realDelegate;
  218. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:willCacheResponse:)])
  219. return [realDelegate connection:connection willCacheResponse:cachedResponse];
  220. return cachedResponse;
  221. }
  222. - (void)connectionDidFinishLoading:(NSURLConnection *)connection
  223. {
  224. id realDelegate = self.realDelegate;
  225. if (realDelegate && [realDelegate respondsToSelector:@selector(connectionDidFinishLoading:)])
  226. [realDelegate connectionDidFinishLoading:connection];
  227. if (!connection.bk_responseData.length)
  228. connection.bk_responseData = nil;
  229. void (^block)(NSURLConnection *, NSURLResponse *, NSData *) = connection.bk_successBlock;
  230. if (block)
  231. block(connection, connection.bk_response, connection.bk_responseData);
  232. }
  233. #pragma mark - Deprecated iOS 4.x authentication methods
  234. - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
  235. {
  236. id realDelegate = self.realDelegate;
  237. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveAuthenticationChallenge:)])
  238. [realDelegate connection:connection didReceiveAuthenticationChallenge:challenge];
  239. }
  240. - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
  241. {
  242. id realDelegate = self.realDelegate;
  243. if (realDelegate && [realDelegate respondsToSelector:@selector(connection:canAuthenticateAgainstProtectionSpace:)])
  244. return [realDelegate connection:connection canAuthenticateAgainstProtectionSpace:protectionSpace];
  245. return NO;
  246. }
  247. @end
  248. #pragma mark - Category
  249. static NSString *const kSuccessBlockKey = @"NSURLConnectionDidFinishLoading";
  250. static NSString *const kFailureBlockKey = @"NSURLConnectionDidFailWithError";
  251. static NSString *const kUploadBlockKey = @"NSURLConnectionDidSendData";
  252. static NSString *const kDownloadBlockKey = @"NSURLConnectionDidRecieveData";
  253. @implementation NSURLConnection (BlocksKit)
  254. @dynamic delegate, bk_responseBlock, bk_failureBlock;
  255. + (void)load
  256. {
  257. @autoreleasepool {
  258. [self bk_registerDynamicDelegate];
  259. [self bk_linkDelegateMethods:@{ @"bk_responseBlock": @"connection:didReceiveResponse:", @"bk_failureBlock": @"connection:didFailWithError:" }];
  260. }
  261. }
  262. #pragma mark Initializers
  263. + (NSURLConnection*)bk_connectionWithRequest:(NSURLRequest *)request
  264. {
  265. return [[[self class] alloc] bk_initWithRequest:request];
  266. }
  267. + (NSURLConnection *)bk_startConnectionWithRequest:(NSURLRequest *)request successHandler:(void (^)(NSURLConnection *, NSURLResponse *, NSData *))success failureHandler:(void (^)(NSURLConnection *, NSError *))failure
  268. {
  269. Protocol *delegateProtocol = objc_getProtocol("NSURLConnectionDelegate");
  270. if (!delegateProtocol)
  271. delegateProtocol = @protocol(BKURLConnectionInformalDelegate);
  272. NSURLConnection *ret = [[self class] alloc];
  273. A2DynamicDelegate *dd = [ret bk_dynamicDelegateForProtocol:delegateProtocol];
  274. if (success)
  275. dd.handlers[kSuccessBlockKey] = [success copy];
  276. if (failure)
  277. [dd implementMethod:@selector(connection:didFailWithError:) withBlock:[failure copy]];
  278. return [ret initWithRequest:request delegate:dd startImmediately:YES];
  279. }
  280. - (id)bk_initWithRequest:(NSURLRequest *)request
  281. {
  282. return (self = [self bk_initWithRequest:request completionHandler:nil]);
  283. }
  284. - (id)bk_initWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLConnection *, NSURLResponse *, NSData *))block
  285. {
  286. Protocol *delegateProtocol = objc_getProtocol("NSURLConnectionDelegate");
  287. if (!delegateProtocol)
  288. delegateProtocol = @protocol(BKURLConnectionInformalDelegate);
  289. A2DynamicDelegate *dd = [self bk_dynamicDelegateForProtocol:delegateProtocol];
  290. if (block)
  291. dd.handlers[kSuccessBlockKey] = [block copy];
  292. return (self = [self initWithRequest:request delegate:dd startImmediately:NO]);
  293. }
  294. - (void)bk_startWithCompletionBlock:(void (^)(NSURLConnection *, NSURLResponse *, NSData *))block
  295. {
  296. self.bk_successBlock = block;
  297. [self start];
  298. }
  299. #pragma mark Properties
  300. - (void (^)(NSURLConnection *, NSURLResponse *, NSData *))bk_successBlock {
  301. return [self.bk_dynamicDelegate handlers][kSuccessBlockKey];
  302. }
  303. - (void)bk_setSuccessBlock:(void (^)(NSURLConnection *, NSURLResponse *, NSData *))block {
  304. if (block)
  305. [self.bk_dynamicDelegate handlers][kSuccessBlockKey] = [block copy];
  306. else
  307. [[self.bk_dynamicDelegate handlers] removeObjectForKey:kSuccessBlockKey];
  308. }
  309. - (void (^)(double))bk_uploadBlock {
  310. return [self.bk_dynamicDelegate handlers][kUploadBlockKey];
  311. }
  312. - (void)bk_setUploadBlock:(void (^)(double))block {
  313. if (block)
  314. [self.bk_dynamicDelegate handlers][kUploadBlockKey] = [block copy];
  315. else
  316. [[self.bk_dynamicDelegate handlers] removeObjectForKey:kUploadBlockKey];
  317. }
  318. - (void (^)(double))bk_downloadBlock {
  319. return [self.bk_dynamicDelegate handlers][kDownloadBlockKey];
  320. }
  321. - (void)bk_setDownloadBlock:(void (^)(double))block {
  322. if (block)
  323. [self.bk_dynamicDelegate handlers][kDownloadBlockKey] = [block copy];
  324. else
  325. [[self.bk_dynamicDelegate handlers] removeObjectForKey:kDownloadBlockKey];
  326. }
  327. @end