PageRenderTime 49ms CodeModel.GetById 8ms app.highlight 37ms RepoModel.GetById 1ms app.codeStats 0ms

/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
  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