|
| 1 | +#import "DataService.h" |
| 2 | + |
| 3 | +#define DATA_SERVICE_DOMAIN @"DataService" |
| 4 | + |
| 5 | +@interface DataService () |
| 6 | + |
| 7 | +@property (nonatomic, retain) NSMutableData *receivedData; |
| 8 | +@property (nonatomic, retain) NSURLConnection *urlConnection; |
| 9 | +@property (nonatomic, copy) void (^onReceiveBlock)(id, NSNumber*, NSDictionary*); |
| 10 | +@property (nonatomic, copy) void (^onErrorBlock)(NSError*); |
| 11 | + |
| 12 | +// Response properties: generate hidden setters |
| 13 | +@property(nonatomic, retain, readwrite) NSDictionary *responseHeaders; |
| 14 | +@property(nonatomic, readwrite) long long expectedContentLength; |
| 15 | +@property(nonatomic, retain, readwrite) NSString *textEncodingName; |
| 16 | +@property(nonatomic, retain, readwrite) NSString *MIMEType; |
| 17 | +@property(nonatomic, retain, readwrite) NSString *suggestedFilename; |
| 18 | + |
| 19 | +@end |
| 20 | + |
| 21 | +@implementation DataService |
| 22 | + |
| 23 | +@synthesize requestURL = _requestURL; |
| 24 | +@synthesize statusCode = _statusCode; |
| 25 | +@synthesize receivedData = _receivedData; |
| 26 | +@synthesize urlConnection = _urlConnection; |
| 27 | +@synthesize onReceiveBlock = _onReceiveBlock; |
| 28 | +@synthesize onErrorBlock = _onErrorBlock; |
| 29 | +@synthesize inProgress = _inProgress; |
| 30 | +@synthesize responseHeaders = _responseHeaders; |
| 31 | +@synthesize expectedContentLength = _expectedContentLength; |
| 32 | +@synthesize textEncodingName = _textEncodingName; |
| 33 | +@synthesize MIMEType = _MIMEType; |
| 34 | +@synthesize suggestedFilename = _suggestedFilename; |
| 35 | + |
| 36 | +#pragma mark - Initialization |
| 37 | + |
| 38 | ++ (DataService*)service{ |
| 39 | + return [[[DataService alloc] init] autorelease]; |
| 40 | +} |
| 41 | + |
| 42 | +#pragma mark - Request |
| 43 | + |
| 44 | +-(void)requestWithURL:(NSURL*)url |
| 45 | + method:(NSString*)method |
| 46 | + headers:(NSDictionary*)headers |
| 47 | + bodyStream:(NSInputStream*)bodyStream |
| 48 | + cachePolicy:(NSURLRequestCachePolicy)cachePolicy |
| 49 | + timeoutInterval:(NSTimeInterval)timeoutInterval |
| 50 | + receiveHandler:(void (^)(id, NSNumber*, NSDictionary*))receiveHandler |
| 51 | + errorHandler:(void (^)(NSError*))errorHandler { |
| 52 | + [self retain]; |
| 53 | + self.onReceiveBlock = receiveHandler; |
| 54 | + self.onErrorBlock = errorHandler; |
| 55 | + self.requestURL = url; |
| 56 | + self.responseHeaders = nil; |
| 57 | + self.expectedContentLength = 0; |
| 58 | + self.textEncodingName = nil; |
| 59 | + self.MIMEType = nil; |
| 60 | + self.suggestedFilename = nil; |
| 61 | + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.requestURL cachePolicy:cachePolicy timeoutInterval:timeoutInterval]; |
| 62 | + for (NSString *header in headers) { |
| 63 | + [request addValue:[headers objectForKey:header] forHTTPHeaderField:header]; |
| 64 | + } |
| 65 | + [request setHTTPShouldUsePipelining:YES]; |
| 66 | + [request setHTTPMethod:method]; |
| 67 | + [request setHTTPBodyStream:bodyStream]; |
| 68 | + [_urlConnection cancel]; |
| 69 | + [_urlConnection release]; |
| 70 | + _urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; |
| 71 | + [_urlConnection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; |
| 72 | + [_urlConnection start]; |
| 73 | + _inProgress = !!_urlConnection; |
| 74 | + if (_urlConnection) { |
| 75 | + self.receivedData = [NSMutableData data]; |
| 76 | + } else { |
| 77 | + NSDictionary *errorInfo = [NSDictionary dictionaryWithObject:@"Failed to create connection" forKey:NSLocalizedDescriptionKey]; |
| 78 | + self.onErrorBlock([NSError errorWithDomain:DATA_SERVICE_DOMAIN code:0 userInfo:errorInfo]); |
| 79 | + self.urlConnection = nil; |
| 80 | + [self release]; |
| 81 | + } |
| 82 | +} |
| 83 | + |
| 84 | +-(void)requestWithURL:(NSURL *)url |
| 85 | + method:(NSString *)method |
| 86 | + headers:(NSDictionary *)headers |
| 87 | + body:(NSData *)body |
| 88 | + receiveHandler:(void (^)(id, NSNumber *, NSDictionary*))receiveHandler |
| 89 | + errorHandler:(void (^)(NSError *))errorHandler { |
| 90 | + NSMutableDictionary *newHeaders = [NSMutableDictionary dictionaryWithDictionary:headers]; |
| 91 | + if (body) { |
| 92 | + [newHeaders setObject:[NSString stringWithFormat:@"%d", [body length]] forKey:@"Content-Length"]; |
| 93 | + } |
| 94 | + [self requestWithURL:url |
| 95 | + method:method |
| 96 | + headers:newHeaders |
| 97 | + bodyStream:body ? [NSInputStream inputStreamWithData:body] : nil |
| 98 | + receiveHandler:receiveHandler |
| 99 | + errorHandler:errorHandler]; |
| 100 | +} |
| 101 | + |
| 102 | +-(void)requestWithURL:(NSURL*)url |
| 103 | + method:(NSString*)method |
| 104 | + headers:(NSDictionary*)headers |
| 105 | + bodyStream:(NSInputStream*)bodyStream |
| 106 | + receiveHandler:(void (^)(id, NSNumber*, NSDictionary*))receiveHandler |
| 107 | + errorHandler:(void (^)(NSError*))errorHandler { |
| 108 | + [self requestWithURL:url |
| 109 | + method:method |
| 110 | + headers:headers |
| 111 | + bodyStream:bodyStream |
| 112 | + cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData |
| 113 | + timeoutInterval:60.0 |
| 114 | + receiveHandler:receiveHandler |
| 115 | + errorHandler:errorHandler]; |
| 116 | +} |
| 117 | + |
| 118 | +#pragma mark - Receive |
| 119 | + |
| 120 | +- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response{ |
| 121 | + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response; |
| 122 | + self.statusCode = [httpResponse statusCode]; |
| 123 | + self.responseHeaders = [httpResponse allHeaderFields]; |
| 124 | + self.expectedContentLength = [httpResponse expectedContentLength]; |
| 125 | + self.textEncodingName = [httpResponse textEncodingName]; |
| 126 | + self.MIMEType = [httpResponse MIMEType]; |
| 127 | + self.suggestedFilename = [httpResponse suggestedFilename]; |
| 128 | + [self.receivedData setLength:0]; |
| 129 | +} |
| 130 | + |
| 131 | +- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data{ |
| 132 | + [self.receivedData appendData:data]; |
| 133 | +} |
| 134 | + |
| 135 | +- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error{ |
| 136 | + _inProgress = NO; |
| 137 | + self.receivedData = nil; |
| 138 | + self.onReceiveBlock = nil; |
| 139 | + self.responseHeaders = nil; |
| 140 | + self.expectedContentLength = 0; |
| 141 | + self.textEncodingName = nil; |
| 142 | + self.MIMEType = nil; |
| 143 | + self.suggestedFilename = nil; |
| 144 | + self.urlConnection = nil; |
| 145 | + if (self.onErrorBlock) { |
| 146 | + // Hold on to the block so that this DataService can be used for new requests in the callback. |
| 147 | + void (^onErrorBlock)(NSError*) = [self.onErrorBlock retain]; |
| 148 | + self.onErrorBlock = nil; |
| 149 | + onErrorBlock(error); |
| 150 | + [onErrorBlock release]; |
| 151 | + } |
| 152 | + [self release]; |
| 153 | +} |
| 154 | + |
| 155 | +- (void)connectionDidFinishLoading:(NSURLConnection*)connection{ |
| 156 | + _inProgress = NO; |
| 157 | + // check to see if the status code is an error code and react appropriately |
| 158 | + if (self.statusCode / 100 == RESPONSE_CLIENT_ERROR / 100 || self.statusCode / 100 == RESPONSE_SERVER_ERROR / 100) { |
| 159 | + NSString *errorString = [[[NSString alloc] initWithData:self.receivedData encoding:NSUTF8StringEncoding] autorelease]; |
| 160 | + NSDictionary *errorInfo = [NSDictionary dictionaryWithObject:errorString forKey:NSLocalizedDescriptionKey]; |
| 161 | + NSError *error = [NSError errorWithDomain:DATA_SERVICE_DOMAIN code:self.statusCode userInfo:errorInfo]; |
| 162 | + [self connection:connection didFailWithError:error]; |
| 163 | + return; |
| 164 | + } |
| 165 | + self.onErrorBlock = nil; |
| 166 | + self.urlConnection = nil; |
| 167 | + // Hold on to required values so that this DataService can be reused from the callback. |
| 168 | + NSData *receivedData = [self.receivedData retain]; |
| 169 | + NSDictionary *responseHeaders = [self.responseHeaders retain]; |
| 170 | + self.receivedData = nil; |
| 171 | + self.responseHeaders = nil; |
| 172 | + self.expectedContentLength = 0; |
| 173 | + self.textEncodingName = nil; |
| 174 | + self.MIMEType = nil; |
| 175 | + self.suggestedFilename = nil; |
| 176 | + if (self.onReceiveBlock) { |
| 177 | + void (^onReceiveBlock)(id, NSNumber*, NSDictionary*) = [self.onReceiveBlock retain]; |
| 178 | + self.onReceiveBlock = nil; |
| 179 | + onReceiveBlock([NSData dataWithData:receivedData], [NSNumber numberWithInteger:self.statusCode], responseHeaders); |
| 180 | + [onReceiveBlock release]; |
| 181 | + } |
| 182 | + [receivedData release]; |
| 183 | + [responseHeaders release]; |
| 184 | + [self release]; |
| 185 | +} |
| 186 | + |
| 187 | +#pragma mark - Cleanup |
| 188 | + |
| 189 | +- (void)cancel{ |
| 190 | + [self.urlConnection cancel]; |
| 191 | + self.urlConnection = nil; |
| 192 | + self.requestURL = nil; |
| 193 | + self.receivedData = nil; |
| 194 | + self.onReceiveBlock = nil; |
| 195 | + self.onErrorBlock = nil; |
| 196 | + self.responseHeaders = nil; |
| 197 | + self.expectedContentLength = 0; |
| 198 | + self.textEncodingName = nil; |
| 199 | + self.MIMEType = nil; |
| 200 | + self.suggestedFilename = nil; |
| 201 | + if (_inProgress) { |
| 202 | + [self release]; |
| 203 | + } |
| 204 | + _inProgress = NO; |
| 205 | +} |
| 206 | + |
| 207 | +- (void)dealloc{ |
| 208 | + self.receivedData = nil; |
| 209 | + self.requestURL = nil; |
| 210 | + self.onReceiveBlock = nil; |
| 211 | + self.onErrorBlock = nil; |
| 212 | + self.urlConnection = nil; |
| 213 | + self.responseHeaders = nil; |
| 214 | + self.expectedContentLength = 0; |
| 215 | + self.textEncodingName = nil; |
| 216 | + self.MIMEType = nil; |
| 217 | + self.suggestedFilename = nil; |
| 218 | + [super dealloc]; |
| 219 | +} |
| 220 | + |
| 221 | +@end |
0 commit comments