Skip to content

Instantly share code, notes, and snippets.

@codeswimmer
Forked from akkabin/AsyncURLWrapper.h
Created September 22, 2012 06:28
Show Gist options
  • Save codeswimmer/3765358 to your computer and use it in GitHub Desktop.
Save codeswimmer/3765358 to your computer and use it in GitHub Desktop.
AsyncURLWrapper
#import <Foundation/Foundation.h>
@class AsyncURLWrapper;
@protocol AsyncURLWrapperDelegate <NSObject>
@optional
- (void) didFinish: (NSData*) data sender: (AsyncURLWrapper*) urlWrapper response: (NSHTTPURLResponse*) response;
- (void) didFail: (NSError*) error sender: (AsyncURLWrapper*) urlWrapper;
- (void) beforeRetry: (NSError*) error sender: (AsyncURLWrapper*) urlWrapper;
- (void) didUpdateProgress: (float) percentCompleted sender: (AsyncURLWrapper*) urlWrapper;
@end
@interface AsyncURLWrapper : NSObject<NSURLConnectionDelegate> {
id<AsyncURLWrapperDelegate> _delegate;
NSMutableData * _data;
NSNumber* _expectedContentLength;
NSURLRequest* _lastRequest;
int _tries;
int _retries;
NSURLConnection* _connection;
NSHTTPURLResponse* _lastResponse;
}
@property (retain) NSTimer* timer;
- (void) cancelAndUnload;
- (void) cancel;
- (id) initWithDelegate: (id<AsyncURLWrapperDelegate>) delegate retries: (int) retries;
- (id) initWithRequest: (NSURLRequest*) request delegate: (id<AsyncURLWrapperDelegate>) delegate retries: (int) retries timeout: (NSTimeInterval) timeout;
- (id) initWithRequest: (NSURLRequest*) request delegate: (id<AsyncURLWrapperDelegate>) d retries: (int) retries expectedLength: (NSNumber*) expectedContentLength timeout: (NSTimeInterval) timeout;
- (void) executeWithRequest: (NSURLRequest*) request timeout: (NSTimeInterval) timeout;
+ (NSString*) asUTF8String: (NSData*) data;
@end
#import "AsyncURLWrapper.h"
#import <QuartzCore/QuartzCore.h>
@implementation AsyncURLWrapper
@synthesize timer = _timer;
#pragma mark - Utils
- (void) connectWithRequest: (NSURLRequest*) request
{
_data = [[NSMutableData alloc] init];
_lastRequest = request;
_connection=[[NSURLConnection alloc] initWithRequest: request delegate:self startImmediately:YES];
if (_connection) {
NSLog(@"connection initialized");
} else {
NSLog(@"connection failed");
NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
NSString* errorMsg = @"Could not initiate connection";
[errorDetail setValue:errorMsg forKey:NSLocalizedDescriptionKey];
[_timer invalidate];
self.timer = nil;
[_delegate didFail:[NSError errorWithDomain:@"asynchronous blob" code:1214 userInfo:errorDetail] sender:self]; //TODO: code and errormsgs should be linked
}
}
-(void) retry: (NSError*) error
{
@synchronized(self) { //We synchronize over self for _tries in case you get calls for retry at the same time
if (_tries < _retries) {
_tries += 1;
[_delegate beforeRetry:error sender:self];
[self connectWithRequest:_lastRequest];
} else {
[_timer invalidate];
self.timer = nil;
[_delegate didFail:error sender:self];
}
}
}
- (void)timeoutAndFail:(NSTimer*)timer
{
[self cancel];
if (timer == _timer) {
NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
NSString* errorMsg = @"Timed out";
[errorDetail setValue:errorMsg forKey:NSLocalizedDescriptionKey];
[_delegate didFail:[NSError errorWithDomain:@"asynchronous blob" code:1215 userInfo:errorDetail] sender:self];
}
}
#pragma mark - NSURLConnection delegates
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[_timer invalidate];
[_delegate didFail:error sender:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//assuming this is HTTP
@synchronized(self) {
_lastResponse = (NSHTTPURLResponse *)response;
NSInteger statusCode = [_lastResponse statusCode];
if (200 != statusCode) {
[self cancel];
NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
//TODO: code and errormsgs should be linked
NSString* errorMsg = [[NSString alloc] initWithFormat:@"Did not get status code 200. Got: %d", statusCode];
[errorDetail setValue:errorMsg forKey:NSLocalizedDescriptionKey];
if (403 == statusCode || 404 == statusCode) {
[self retry: [NSError errorWithDomain:@"asynchronous blob" code:1213 userInfo:errorDetail]];
} else {
[_timer invalidate];
self.timer = nil;
[_delegate didFail:[NSError errorWithDomain:@"asynchronous blob" code:1212 userInfo:errorDetail] sender:self];
}
}else {
if ([response expectedContentLength] > 0) {
_expectedContentLength = [[NSNumber alloc] initWithLongLong:[response expectedContentLength]];
}
NSLog(@"Expected content length: %@", _expectedContentLength);
}
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[_delegate didUpdateProgress: [data length] * 1.0f/ [_expectedContentLength longLongValue] sender:self];
[_data appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[_timer invalidate];
self.timer = nil;
@synchronized(self) {
[_delegate didFinish:_data sender:self response: _lastResponse];
}
}
#pragma mark - Impl
- (void) cancel
{
[_timer invalidate];
self.timer = nil;
[_connection cancel];
}
- (void) cancelAndUnload
{
[self cancel];
_delegate = nil;
[_timer invalidate];
self.timer = nil;
_data = nil;
_expectedContentLength = nil;
_lastRequest = nil;
_connection = nil;
_lastResponse = nil;
}
- (id) initWithDelegate: (id<AsyncURLWrapperDelegate>) delegate retries: (int) retries
{
self = [super init];
_delegate = delegate;
_retries = retries;
return self;
}
- (id) initWithRequest: (NSURLRequest*) request delegate: (id<AsyncURLWrapperDelegate>) delegate retries: (int) retries expectedLength: (NSNumber*) expectedContentLength timeout: (NSTimeInterval) timeout
{
self = [super init];
_expectedContentLength = expectedContentLength;
_delegate = delegate;
@synchronized(self) {
_retries = retries;
}
[self executeWithRequest:request timeout:timeout];
return self;
}
- (id) initWithRequest: (NSURLRequest*) request delegate: (id<AsyncURLWrapperDelegate>) delegate retries: (int) retries timeout: (NSTimeInterval) timeout
{
return [self initWithRequest:request delegate:delegate retries: retries expectedLength:nil timeout:timeout];
}
- (void) executeWithRequest: (NSURLRequest*) request timeout: (NSTimeInterval) timeout
{
@synchronized(self) {
_tries = 0;
}
[_timer invalidate];
self.timer = nil;
_timer = [NSTimer timerWithTimeInterval:timeout target:self selector:@selector(timeoutAndFail:) userInfo:nil repeats:NO];
[self connectWithRequest:request];
}
+ (NSString*) asUTF8String: (NSData*) data
{
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment