123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- //
- // NSURLConnection+BlocksKit.m
- // BlocksKit
- //
- #import <objc/runtime.h>
- #import "A2DynamicDelegate.h"
- #import "NSObject+A2BlockDelegate.h"
- #import "NSObject+A2DynamicDelegate.h"
- #import "NSURLConnection+BlocksKit.h"
- #pragma mark Private
- static const void *BKResponseDataKey = &BKResponseDataKey;
- static const void *BKResponseKey = &BKResponseKey;
- static const void *BKResponseLengthKey = &BKResponseLengthKey;
- @interface NSURLConnection (BlocksKitPrivate)
- @property (nonatomic, retain, setter = bk_setResponseData:) NSMutableData *bk_responseData;
- @property (nonatomic, retain, setter = bk_setResponse:) NSURLResponse *bk_response;
- @property (nonatomic, setter = bk_setResponseLength:) NSUInteger bk_responseLength;
- @end
- @implementation NSURLConnection (BlocksKitPrivate)
- - (NSMutableData *)bk_responseData
- {
- return objc_getAssociatedObject(self, BKResponseDataKey);
- }
- - (void)bk_setResponseData:(NSMutableData *)responseData
- {
- objc_setAssociatedObject(self, BKResponseDataKey, responseData, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (NSURLResponse *)bk_response
- {
- return objc_getAssociatedObject(self, BKResponseKey);
- }
- - (void)bk_setResponse:(NSURLResponse *)response
- {
- objc_setAssociatedObject(self, BKResponseKey, response, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (NSUInteger)bk_responseLength
- {
- return [objc_getAssociatedObject(self, BKResponseLengthKey) unsignedIntegerValue];
- }
- - (void)bk_setResponseLength:(NSUInteger)responseLength
- {
- objc_setAssociatedObject(self, BKResponseLengthKey, @(responseLength), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- @end
- #pragma mark - BKURLConnectionInformalDelegate - iOS 4.3 & Snow Leopard support
- @protocol BKURLConnectionInformalDelegate <NSObject>
- - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
- - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
- - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite;
- - (void)connectionDidFinishLoading:(NSURLConnection *)connection;
- - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
- - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
- - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace;
- @end
- @interface A2DynamicBKURLConnectionInformalDelegate : A2DynamicDelegate <BKURLConnectionInformalDelegate>
- @end
- @implementation A2DynamicBKURLConnectionInformalDelegate
- - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
- {
- id realDelegate = self.realDelegate;
- if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveResponse:)])
- [realDelegate connection:connection didReceiveResponse:response];
- connection.bk_responseLength = 0;
- [connection.bk_responseData setLength:0];
- connection.bk_response = response;
- void (^block)(NSURLConnection *, NSURLResponse *) = [self blockImplementationForMethod:_cmd];
- if (block) block(connection, response);
- }
- - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
- {
- connection.bk_responseLength += data.length;
- void (^block)(double) = connection.bk_downloadBlock;
- if (block && connection.bk_response && connection.bk_response.expectedContentLength != NSURLResponseUnknownLength)
- block((double)connection.bk_responseLength / (double)connection.bk_response.expectedContentLength);
- id realDelegate = self.realDelegate;
- if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveData:)]) {
- [realDelegate connection:connection didReceiveData:data];
- return;
- }
- NSMutableData *responseData = connection.bk_responseData;
- if (!responseData) {
- responseData = [NSMutableData data];
- connection.bk_responseData = responseData;
- }
- [responseData appendData:data];
- }
- - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
- {
- id realDelegate = self.realDelegate;
- if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:)])
- [realDelegate connection:connection didSendBodyData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
- void (^block)(double) = connection.bk_uploadBlock;
- if (block) block((double)totalBytesWritten/(double)totalBytesExpectedToWrite);
- }
- - (void)connectionDidFinishLoading:(NSURLConnection *)connection
- {
- id realDelegate = self.realDelegate;
- if (realDelegate && [realDelegate respondsToSelector:@selector(connectionDidFinishLoading:)])
- [realDelegate connectionDidFinishLoading:connection];
- if (!connection.bk_responseData.length)
- connection.bk_responseData = nil;
- void (^block)(NSURLConnection *, NSURLResponse *, NSData *) = connection.bk_successBlock;
- if (block) block(connection, connection.bk_response, connection.bk_responseData);
- }
- - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
- {
- id realDelegate = self.realDelegate;
- if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didFailWithError:)])
- [realDelegate connection:connection didFailWithError:error];
- connection.bk_responseLength = 0;
- [connection.bk_responseData setLength:0];
- void (^block)(NSURLConnection *, NSError *) = [self blockImplementationForMethod:_cmd];
- if (block) block(connection, error);
- }
- - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
- {
- id realDelegate = self.realDelegate;
- if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveAuthenticationChallenge:)])
- [realDelegate connection:connection didReceiveAuthenticationChallenge:challenge];
- }
- - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
- {
- id realDelegate = self.realDelegate;
- if (realDelegate && [realDelegate respondsToSelector:@selector(connection:canAuthenticateAgainstProtectionSpace:)])
- return [realDelegate connection:connection canAuthenticateAgainstProtectionSpace:protectionSpace];
- return NO;
- }
- @end
- #pragma mark - NSURLConnectionDelegate - iOS 5.0 & Lion support
- @interface A2DynamicNSURLConnectionDelegate : A2DynamicDelegate <NSURLConnectionDelegate>
- @end
- @implementation A2DynamicNSURLConnectionDelegate
- - (BOOL)conformsToProtocol:(Protocol *)protocol
- {
- Protocol *dataDelegateProtocol = objc_getProtocol("NSURLConnectionDataDelegate");
- return (protocol_isEqual(protocol, dataDelegateProtocol) || protocol_isEqual(protocol, self.protocol) || [super conformsToProtocol:protocol]);
- }
- - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
- {
- id realDelegate = self.realDelegate;
- if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didFailWithError:)])
- [realDelegate connection:connection didFailWithError:error];
- connection.bk_responseLength = 0;
- [connection.bk_responseData setLength:0];
- void (^block)(NSURLConnection *, NSError *) = [self blockImplementationForMethod:_cmd];
- if (block) block(connection, error);
- }
- - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
- {
- id realDelegate = self.realDelegate;
- if (realDelegate && [realDelegate respondsToSelector:@selector(connectionShouldUseCredentialStorage:)])
- return [realDelegate connectionShouldUseCredentialStorage:connection];
- return YES;
- }
- - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
- {
- id realDelegate = self.realDelegate;
- if (realDelegate && [realDelegate respondsToSelector:@selector(connection:willSendRequestForAuthenticationChallenge:)])
- [realDelegate connection:connection willSendRequestForAuthenticationChallenge:challenge];
- }
- #pragma mark - NSURLConnectionDataDelegate
- - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response
- {
- id realDelegate = self.realDelegate;
- if (realDelegate && [realDelegate respondsToSelector:@selector(connection:willSendRequest:redirectResponse:)])
- return [realDelegate connection:connection willSendRequest:request redirectResponse:response];
- return request;
- }
- - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
- {
- id realDelegate = self.realDelegate;
- if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveResponse:)])
- [realDelegate connection:connection didReceiveResponse:response];
- connection.bk_responseLength = 0;
- if (connection.bk_responseData)
- [connection.bk_responseData setLength:0];
- connection.bk_response = response;
- void (^block)(NSURLConnection *, NSURLResponse *) = [self blockImplementationForMethod:_cmd];
- if (block) block(connection, response);
- }
- - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
- {
- connection.bk_responseLength += data.length;
- void (^block)(double) = connection.bk_downloadBlock;
- if (block && connection.bk_response && connection.bk_response.expectedContentLength != NSURLResponseUnknownLength)
- block((double)connection.bk_responseLength / (double)connection.bk_response.expectedContentLength);
- id realDelegate = self.realDelegate;
- if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveData:)]) {
- [realDelegate connection:connection didReceiveData:data];
- return;
- }
- NSMutableData *responseData = connection.bk_responseData;
- if (!responseData) {
- responseData = [NSMutableData data];
- connection.bk_responseData = responseData;
- }
- [responseData appendData:data];
- }
- - (NSInputStream *)connection:(NSURLConnection *)connection needNewBodyStream:(NSURLRequest *)request
- {
- id realDelegate = self.realDelegate;
- if (realDelegate && [realDelegate respondsToSelector:@selector(connection:needNewBodyStream:)])
- return [realDelegate connection:connection needNewBodyStream:request];
- return nil;
- }
- - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
- {
- id realDelegate = self.realDelegate;
- if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:)])
- [realDelegate connection:connection didSendBodyData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
- void (^block)(double) = connection.bk_uploadBlock;
- if (block)
- block((double)totalBytesWritten/(double)totalBytesExpectedToWrite);
- }
- - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
- {
- id realDelegate = self.realDelegate;
- if (realDelegate && [realDelegate respondsToSelector:@selector(connection:willCacheResponse:)])
- return [realDelegate connection:connection willCacheResponse:cachedResponse];
- return cachedResponse;
- }
- - (void)connectionDidFinishLoading:(NSURLConnection *)connection
- {
- id realDelegate = self.realDelegate;
- if (realDelegate && [realDelegate respondsToSelector:@selector(connectionDidFinishLoading:)])
- [realDelegate connectionDidFinishLoading:connection];
- if (!connection.bk_responseData.length)
- connection.bk_responseData = nil;
- void (^block)(NSURLConnection *, NSURLResponse *, NSData *) = connection.bk_successBlock;
- if (block)
- block(connection, connection.bk_response, connection.bk_responseData);
- }
- #pragma mark - Deprecated iOS 4.x authentication methods
- - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
- {
- id realDelegate = self.realDelegate;
- if (realDelegate && [realDelegate respondsToSelector:@selector(connection:didReceiveAuthenticationChallenge:)])
- [realDelegate connection:connection didReceiveAuthenticationChallenge:challenge];
- }
- - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
- {
- id realDelegate = self.realDelegate;
- if (realDelegate && [realDelegate respondsToSelector:@selector(connection:canAuthenticateAgainstProtectionSpace:)])
- return [realDelegate connection:connection canAuthenticateAgainstProtectionSpace:protectionSpace];
- return NO;
- }
- @end
- #pragma mark - Category
- static NSString *const kSuccessBlockKey = @"NSURLConnectionDidFinishLoading";
- static NSString *const kFailureBlockKey = @"NSURLConnectionDidFailWithError";
- static NSString *const kUploadBlockKey = @"NSURLConnectionDidSendData";
- static NSString *const kDownloadBlockKey = @"NSURLConnectionDidRecieveData";
- @implementation NSURLConnection (BlocksKit)
- @dynamic delegate, bk_responseBlock, bk_failureBlock;
- + (void)load
- {
- @autoreleasepool {
- [self bk_registerDynamicDelegate];
- [self bk_linkDelegateMethods:@{ @"bk_responseBlock": @"connection:didReceiveResponse:", @"bk_failureBlock": @"connection:didFailWithError:" }];
- }
- }
- #pragma mark Initializers
- + (NSURLConnection*)bk_connectionWithRequest:(NSURLRequest *)request
- {
- return [[[self class] alloc] bk_initWithRequest:request];
- }
- + (NSURLConnection *)bk_startConnectionWithRequest:(NSURLRequest *)request successHandler:(void (^)(NSURLConnection *, NSURLResponse *, NSData *))success failureHandler:(void (^)(NSURLConnection *, NSError *))failure
- {
- Protocol *delegateProtocol = objc_getProtocol("NSURLConnectionDelegate");
- if (!delegateProtocol)
- delegateProtocol = @protocol(BKURLConnectionInformalDelegate);
- NSURLConnection *ret = [[self class] alloc];
- A2DynamicDelegate *dd = [ret bk_dynamicDelegateForProtocol:delegateProtocol];
- if (success)
- dd.handlers[kSuccessBlockKey] = [success copy];
- if (failure)
- [dd implementMethod:@selector(connection:didFailWithError:) withBlock:[failure copy]];
- return [ret initWithRequest:request delegate:dd startImmediately:YES];
- }
- - (id)bk_initWithRequest:(NSURLRequest *)request
- {
- return (self = [self bk_initWithRequest:request completionHandler:nil]);
- }
- - (id)bk_initWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLConnection *, NSURLResponse *, NSData *))block
- {
- Protocol *delegateProtocol = objc_getProtocol("NSURLConnectionDelegate");
- if (!delegateProtocol)
- delegateProtocol = @protocol(BKURLConnectionInformalDelegate);
- A2DynamicDelegate *dd = [self bk_dynamicDelegateForProtocol:delegateProtocol];
- if (block)
- dd.handlers[kSuccessBlockKey] = [block copy];
- return (self = [self initWithRequest:request delegate:dd startImmediately:NO]);
- }
- - (void)bk_startWithCompletionBlock:(void (^)(NSURLConnection *, NSURLResponse *, NSData *))block
- {
- self.bk_successBlock = block;
- [self start];
- }
- #pragma mark Properties
- - (void (^)(NSURLConnection *, NSURLResponse *, NSData *))bk_successBlock {
- return [self.bk_dynamicDelegate handlers][kSuccessBlockKey];
- }
- - (void)bk_setSuccessBlock:(void (^)(NSURLConnection *, NSURLResponse *, NSData *))block {
- if (block)
- [self.bk_dynamicDelegate handlers][kSuccessBlockKey] = [block copy];
- else
- [[self.bk_dynamicDelegate handlers] removeObjectForKey:kSuccessBlockKey];
- }
- - (void (^)(double))bk_uploadBlock {
- return [self.bk_dynamicDelegate handlers][kUploadBlockKey];
- }
- - (void)bk_setUploadBlock:(void (^)(double))block {
- if (block)
- [self.bk_dynamicDelegate handlers][kUploadBlockKey] = [block copy];
- else
- [[self.bk_dynamicDelegate handlers] removeObjectForKey:kUploadBlockKey];
- }
- - (void (^)(double))bk_downloadBlock {
- return [self.bk_dynamicDelegate handlers][kDownloadBlockKey];
- }
- - (void)bk_setDownloadBlock:(void (^)(double))block {
- if (block)
- [self.bk_dynamicDelegate handlers][kDownloadBlockKey] = [block copy];
- else
- [[self.bk_dynamicDelegate handlers] removeObjectForKey:kDownloadBlockKey];
- }
- @end
|