ASINetwork queues now show the network progress indicator while network operations are in progress
(Based on Ken Collins' stuff here: http://pastie.org/550508) Clean up ASINetworkQueue to use accessors everywhere to make subclassing easier
Showing
2 changed files
with
101 additions
and
73 deletions
| @@ -87,6 +87,13 @@ | @@ -87,6 +87,13 @@ | ||
| 87 | // This method will start the queue | 87 | // This method will start the queue |
| 88 | - (void)go; | 88 | - (void)go; |
| 89 | 89 | ||
| 90 | +// Used on iPhone platform to show / hide the network activity indicator (in the status bar) | ||
| 91 | +// On mac, you could subclass to do something else | ||
| 92 | +- (void)updateNetworkActivityIndicator; | ||
| 93 | + | ||
| 94 | +// Returns YES if the queue is in progress | ||
| 95 | +- (BOOL)isNetworkActive; | ||
| 96 | + | ||
| 90 | 97 | ||
| 91 | @property (assign,setter=setUploadProgressDelegate:) id uploadProgressDelegate; | 98 | @property (assign,setter=setUploadProgressDelegate:) id uploadProgressDelegate; |
| 92 | @property (assign,setter=setDownloadProgressDelegate:) id downloadProgressDelegate; | 99 | @property (assign,setter=setDownloadProgressDelegate:) id downloadProgressDelegate; |
| @@ -9,31 +9,21 @@ | @@ -9,31 +9,21 @@ | ||
| 9 | #import "ASINetworkQueue.h" | 9 | #import "ASINetworkQueue.h" |
| 10 | #import "ASIHTTPRequest.h" | 10 | #import "ASIHTTPRequest.h" |
| 11 | 11 | ||
| 12 | +// Private stuff | ||
| 13 | +@interface ASINetworkQueue () | ||
| 14 | + @property (assign) int requestsCount; | ||
| 15 | + @property (assign) unsigned long long uploadProgressBytes; | ||
| 16 | + @property (assign) unsigned long long uploadProgressTotalBytes; | ||
| 17 | + @property (assign) unsigned long long downloadProgressBytes; | ||
| 18 | + @property (assign) unsigned long long downloadProgressTotalBytes; | ||
| 19 | +@end | ||
| 12 | 20 | ||
| 13 | @implementation ASINetworkQueue | 21 | @implementation ASINetworkQueue |
| 14 | 22 | ||
| 15 | - (id)init | 23 | - (id)init |
| 16 | { | 24 | { |
| 17 | self = [super init]; | 25 | self = [super init]; |
| 18 | - | 26 | + [self setShouldCancelAllRequestsOnFailure:YES]; |
| 19 | - delegate = nil; | ||
| 20 | - requestDidFinishSelector = NULL; | ||
| 21 | - requestDidFailSelector = NULL; | ||
| 22 | - queueDidFinishSelector = NULL; | ||
| 23 | - shouldCancelAllRequestsOnFailure = YES; | ||
| 24 | - | ||
| 25 | - uploadProgressDelegate = nil; | ||
| 26 | - uploadProgressBytes = 0; | ||
| 27 | - uploadProgressTotalBytes = 0; | ||
| 28 | - | ||
| 29 | - downloadProgressDelegate = nil; | ||
| 30 | - downloadProgressBytes = 0; | ||
| 31 | - downloadProgressTotalBytes = 0; | ||
| 32 | - | ||
| 33 | - requestsCount = 0; | ||
| 34 | - | ||
| 35 | - showAccurateProgress = NO; | ||
| 36 | - | ||
| 37 | [self setMaxConcurrentOperationCount:4]; | 27 | [self setMaxConcurrentOperationCount:4]; |
| 38 | [self setSuspended:YES]; | 28 | [self setSuspended:YES]; |
| 39 | 29 | ||
| @@ -54,14 +44,33 @@ | @@ -54,14 +44,33 @@ | ||
| 54 | [super dealloc]; | 44 | [super dealloc]; |
| 55 | } | 45 | } |
| 56 | 46 | ||
| 47 | +- (BOOL)isNetworkActive | ||
| 48 | +{ | ||
| 49 | + return ([self requestsCount] > 0 && ![self isSuspended]); | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +- (void)updateNetworkActivityIndicator | ||
| 53 | +{ | ||
| 54 | +#if TARGET_OS_IPHONE | ||
| 55 | + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:[self isNetworkActive]]; | ||
| 56 | +#endif | ||
| 57 | +} | ||
| 58 | + | ||
| 59 | +- (void)setSuspended:(BOOL)suspend | ||
| 60 | +{ | ||
| 61 | + [super setSuspended:suspend]; | ||
| 62 | + [self updateNetworkActivityIndicator]; | ||
| 63 | +} | ||
| 64 | + | ||
| 65 | + | ||
| 57 | - (void)go | 66 | - (void)go |
| 58 | { | 67 | { |
| 59 | - if (!showAccurateProgress) { | 68 | + if (![self showAccurateProgress]) { |
| 60 | - if (downloadProgressDelegate) { | 69 | + if ([self downloadProgressDelegate]) { |
| 61 | - [self incrementDownloadSizeBy:requestsCount]; | 70 | + [self incrementDownloadSizeBy:[self requestsCount]]; |
| 62 | } | 71 | } |
| 63 | - if (uploadProgressDelegate) { | 72 | + if ([self uploadProgressDelegate]) { |
| 64 | - [self incrementUploadSizeBy:requestsCount]; | 73 | + [self incrementUploadSizeBy:[self requestsCount]]; |
| 65 | } | 74 | } |
| 66 | } | 75 | } |
| 67 | [self setSuspended:NO]; | 76 | [self setSuspended:NO]; |
| @@ -69,12 +78,13 @@ | @@ -69,12 +78,13 @@ | ||
| 69 | 78 | ||
| 70 | - (void)cancelAllOperations | 79 | - (void)cancelAllOperations |
| 71 | { | 80 | { |
| 72 | - requestsCount = 0; | 81 | + [self setRequestsCount:0]; |
| 73 | - uploadProgressBytes = 0; | 82 | + [self setUploadProgressBytes:0]; |
| 74 | - uploadProgressTotalBytes = 0; | 83 | + [self setUploadProgressTotalBytes:0]; |
| 75 | - downloadProgressBytes = 0; | 84 | + [self setDownloadProgressBytes:0]; |
| 76 | - downloadProgressTotalBytes = 0; | 85 | + [self setDownloadProgressTotalBytes:0]; |
| 77 | [super cancelAllOperations]; | 86 | [super cancelAllOperations]; |
| 87 | + [self updateNetworkActivityIndicator]; | ||
| 78 | } | 88 | } |
| 79 | 89 | ||
| 80 | - (void)setUploadProgressDelegate:(id)newDelegate | 90 | - (void)setUploadProgressDelegate:(id)newDelegate |
| @@ -83,13 +93,13 @@ | @@ -83,13 +93,13 @@ | ||
| 83 | 93 | ||
| 84 | // If the uploadProgressDelegate is an NSProgressIndicator, we set it's MaxValue to 1.0 so we can treat it similarly to UIProgressViews | 94 | // If the uploadProgressDelegate is an NSProgressIndicator, we set it's MaxValue to 1.0 so we can treat it similarly to UIProgressViews |
| 85 | SEL selector = @selector(setMaxValue:); | 95 | SEL selector = @selector(setMaxValue:); |
| 86 | - if ([uploadProgressDelegate respondsToSelector:selector]) { | 96 | + if ([[self uploadProgressDelegate] respondsToSelector:selector]) { |
| 87 | double max = 1.0; | 97 | double max = 1.0; |
| 88 | - NSMethodSignature *signature = [[uploadProgressDelegate class] instanceMethodSignatureForSelector:selector]; | 98 | + NSMethodSignature *signature = [[[self uploadProgressDelegate] class] instanceMethodSignatureForSelector:selector]; |
| 89 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; | 99 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; |
| 90 | [invocation setSelector:selector]; | 100 | [invocation setSelector:selector]; |
| 91 | [invocation setArgument:&max atIndex:2]; | 101 | [invocation setArgument:&max atIndex:2]; |
| 92 | - [invocation invokeWithTarget:uploadProgressDelegate]; | 102 | + [invocation invokeWithTarget:[self uploadProgressDelegate]]; |
| 93 | } | 103 | } |
| 94 | } | 104 | } |
| 95 | 105 | ||
| @@ -100,13 +110,13 @@ | @@ -100,13 +110,13 @@ | ||
| 100 | 110 | ||
| 101 | // If the downloadProgressDelegate is an NSProgressIndicator, we set it's MaxValue to 1.0 so we can treat it similarly to UIProgressViews | 111 | // If the downloadProgressDelegate is an NSProgressIndicator, we set it's MaxValue to 1.0 so we can treat it similarly to UIProgressViews |
| 102 | SEL selector = @selector(setMaxValue:); | 112 | SEL selector = @selector(setMaxValue:); |
| 103 | - if ([downloadProgressDelegate respondsToSelector:selector]) { | 113 | + if ([[self downloadProgressDelegate] respondsToSelector:selector]) { |
| 104 | double max = 1.0; | 114 | double max = 1.0; |
| 105 | - NSMethodSignature *signature = [[downloadProgressDelegate class] instanceMethodSignatureForSelector:selector]; | 115 | + NSMethodSignature *signature = [[[self downloadProgressDelegate] class] instanceMethodSignatureForSelector:selector]; |
| 106 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; | 116 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; |
| 107 | [invocation setSelector:@selector(setMaxValue:)]; | 117 | [invocation setSelector:@selector(setMaxValue:)]; |
| 108 | [invocation setArgument:&max atIndex:2]; | 118 | [invocation setArgument:&max atIndex:2]; |
| 109 | - [invocation invokeWithTarget:downloadProgressDelegate]; | 119 | + [invocation invokeWithTarget:[self downloadProgressDelegate]]; |
| 110 | } | 120 | } |
| 111 | } | 121 | } |
| 112 | 122 | ||
| @@ -119,6 +129,8 @@ | @@ -119,6 +129,8 @@ | ||
| 119 | [request setQueuePriority:10]; | 129 | [request setQueuePriority:10]; |
| 120 | [request setShowAccurateProgress:YES]; | 130 | [request setShowAccurateProgress:YES]; |
| 121 | [request setQueue:self]; | 131 | [request setQueue:self]; |
| 132 | + | ||
| 133 | + // Important - we are calling NSOperation's add method - we don't want to add this as a normal request! | ||
| 122 | [super addOperation:request]; | 134 | [super addOperation:request]; |
| 123 | } | 135 | } |
| 124 | } | 136 | } |
| @@ -126,13 +138,15 @@ | @@ -126,13 +138,15 @@ | ||
| 126 | // Only add ASIHTTPRequests to this queue!! | 138 | // Only add ASIHTTPRequests to this queue!! |
| 127 | - (void)addOperation:(NSOperation *)operation | 139 | - (void)addOperation:(NSOperation *)operation |
| 128 | { | 140 | { |
| 129 | - if ([operation isKindOfClass:[ASIHTTPRequest class]]) { | 141 | + if (![operation isKindOfClass:[ASIHTTPRequest class]]) { |
| 142 | + [NSException raise:@"AttemptToAddInvalidRequest" format:@"Attempted to add an object that was not an ASIHTTPRequest to an ASINetworkQueue"]; | ||
| 143 | + } | ||
| 130 | 144 | ||
| 131 | - requestsCount++; | 145 | + [self setRequestsCount:[self requestsCount]+1]; |
| 132 | 146 | ||
| 133 | ASIHTTPRequest *request = (ASIHTTPRequest *)operation; | 147 | ASIHTTPRequest *request = (ASIHTTPRequest *)operation; |
| 134 | 148 | ||
| 135 | - if (showAccurateProgress) { | 149 | + if ([self showAccurateProgress]) { |
| 136 | 150 | ||
| 137 | // If this is a GET request and we want accurate progress, perform a HEAD request first to get the content-length | 151 | // If this is a GET request and we want accurate progress, perform a HEAD request first to get the content-length |
| 138 | if ([[request requestMethod] isEqualToString:@"GET"]) { | 152 | if ([[request requestMethod] isEqualToString:@"GET"]) { |
| @@ -148,109 +162,111 @@ | @@ -148,109 +162,111 @@ | ||
| 148 | // If we want to track uploading for this request accurately, we need to add the size of the post content to the total | 162 | // If we want to track uploading for this request accurately, we need to add the size of the post content to the total |
| 149 | } else if (uploadProgressDelegate) { | 163 | } else if (uploadProgressDelegate) { |
| 150 | [request buildPostBody]; | 164 | [request buildPostBody]; |
| 151 | - uploadProgressTotalBytes += [request postLength]; | 165 | + [self setUploadProgressTotalBytes:[self uploadProgressTotalBytes]+[request postLength]]; |
| 152 | } | 166 | } |
| 153 | } | 167 | } |
| 154 | - [request setShowAccurateProgress:showAccurateProgress]; | 168 | + [request setShowAccurateProgress:[self showAccurateProgress]]; |
| 155 | 169 | ||
| 156 | 170 | ||
| 157 | [request setQueue:self]; | 171 | [request setQueue:self]; |
| 158 | [super addOperation:request]; | 172 | [super addOperation:request]; |
| 159 | - } | 173 | + [self updateNetworkActivityIndicator]; |
| 160 | 174 | ||
| 161 | } | 175 | } |
| 162 | 176 | ||
| 163 | - (void)requestDidFail:(ASIHTTPRequest *)request | 177 | - (void)requestDidFail:(ASIHTTPRequest *)request |
| 164 | { | 178 | { |
| 165 | - requestsCount--; | 179 | + [self setRequestsCount:[self requestsCount]-1]; |
| 166 | - if (requestDidFailSelector) { | 180 | + if ([self requestDidFailSelector]) { |
| 167 | - [delegate performSelector:requestDidFailSelector withObject:request]; | 181 | + [[self delegate] performSelector:[self requestDidFailSelector] withObject:request]; |
| 168 | } | 182 | } |
| 169 | - if (shouldCancelAllRequestsOnFailure && requestsCount > 0) { | 183 | + if ([self shouldCancelAllRequestsOnFailure] && [self requestsCount] > 0) { |
| 170 | [self cancelAllOperations]; | 184 | [self cancelAllOperations]; |
| 171 | } | 185 | } |
| 186 | + [self updateNetworkActivityIndicator]; | ||
| 172 | } | 187 | } |
| 173 | 188 | ||
| 174 | - (void)requestDidFinish:(ASIHTTPRequest *)request | 189 | - (void)requestDidFinish:(ASIHTTPRequest *)request |
| 175 | { | 190 | { |
| 176 | - requestsCount--; | 191 | + [self setRequestsCount:[self requestsCount]-1]; |
| 177 | - if (requestDidFinishSelector) { | 192 | + if ([self requestDidFinishSelector]) { |
| 178 | - [delegate performSelector:requestDidFinishSelector withObject:request]; | 193 | + [[self delegate] performSelector:[self requestDidFinishSelector] withObject:request]; |
| 179 | } | 194 | } |
| 180 | - if (requestsCount == 0) { | 195 | + if ([self requestsCount] == 0) { |
| 181 | - if (queueDidFinishSelector) { | 196 | + if ([self queueDidFinishSelector]) { |
| 182 | - [delegate performSelector:queueDidFinishSelector withObject:self]; | 197 | + [[self delegate] performSelector:[self queueDidFinishSelector] withObject:self]; |
| 183 | } | 198 | } |
| 184 | } | 199 | } |
| 200 | + [self updateNetworkActivityIndicator]; | ||
| 185 | } | 201 | } |
| 186 | 202 | ||
| 187 | 203 | ||
| 188 | - (void)setUploadBufferSize:(unsigned long long)bytes | 204 | - (void)setUploadBufferSize:(unsigned long long)bytes |
| 189 | { | 205 | { |
| 190 | - if (!uploadProgressDelegate) { | 206 | + if (![self uploadProgressDelegate]) { |
| 191 | return; | 207 | return; |
| 192 | } | 208 | } |
| 193 | - uploadProgressTotalBytes -= bytes; | 209 | + [self setUploadProgressTotalBytes:[self uploadProgressTotalBytes] - bytes]; |
| 194 | [self incrementUploadProgressBy:0]; | 210 | [self incrementUploadProgressBy:0]; |
| 195 | } | 211 | } |
| 196 | 212 | ||
| 197 | - (void)incrementUploadSizeBy:(unsigned long long)bytes | 213 | - (void)incrementUploadSizeBy:(unsigned long long)bytes |
| 198 | { | 214 | { |
| 199 | - if (!uploadProgressDelegate) { | 215 | + if (![self uploadProgressDelegate]) { |
| 200 | return; | 216 | return; |
| 201 | } | 217 | } |
| 202 | - uploadProgressTotalBytes += bytes; | 218 | + [self setUploadProgressTotalBytes:[self uploadProgressTotalBytes] + bytes]; |
| 203 | [self incrementUploadProgressBy:0]; | 219 | [self incrementUploadProgressBy:0]; |
| 204 | } | 220 | } |
| 205 | 221 | ||
| 206 | - (void)decrementUploadProgressBy:(unsigned long long)bytes | 222 | - (void)decrementUploadProgressBy:(unsigned long long)bytes |
| 207 | { | 223 | { |
| 208 | - if (!uploadProgressDelegate || uploadProgressTotalBytes == 0) { | 224 | + if (![self uploadProgressDelegate] || [self uploadProgressTotalBytes] == 0) { |
| 209 | return; | 225 | return; |
| 210 | } | 226 | } |
| 211 | - uploadProgressBytes -= bytes; | 227 | + [self setUploadProgressBytes:[self uploadProgressBytes] - bytes]; |
| 212 | 228 | ||
| 213 | - double progress = (uploadProgressBytes*1.0)/(uploadProgressTotalBytes*1.0); | 229 | + double progress = ([self uploadProgressBytes]*1.0)/([self uploadProgressTotalBytes]*1.0); |
| 214 | - [ASIHTTPRequest setProgress:progress forProgressIndicator:uploadProgressDelegate]; | 230 | + [ASIHTTPRequest setProgress:progress forProgressIndicator:[self uploadProgressDelegate]]; |
| 215 | } | 231 | } |
| 216 | 232 | ||
| 217 | 233 | ||
| 218 | - (void)incrementUploadProgressBy:(unsigned long long)bytes | 234 | - (void)incrementUploadProgressBy:(unsigned long long)bytes |
| 219 | { | 235 | { |
| 220 | - if (!uploadProgressDelegate || uploadProgressTotalBytes == 0) { | 236 | + if (![self uploadProgressDelegate] || [self uploadProgressTotalBytes] == 0) { |
| 221 | return; | 237 | return; |
| 222 | } | 238 | } |
| 223 | - uploadProgressBytes += bytes; | 239 | + [self setUploadProgressBytes:[self uploadProgressBytes] + bytes]; |
| 224 | 240 | ||
| 225 | - double progress = (uploadProgressBytes*1.0)/(uploadProgressTotalBytes*1.0); | 241 | + double progress = ([self uploadProgressBytes]*1.0)/([self uploadProgressTotalBytes]*1.0); |
| 226 | - [ASIHTTPRequest setProgress:progress forProgressIndicator:uploadProgressDelegate]; | 242 | + [ASIHTTPRequest setProgress:progress forProgressIndicator:[self uploadProgressDelegate]]; |
| 227 | 243 | ||
| 228 | } | 244 | } |
| 229 | 245 | ||
| 230 | - (void)incrementDownloadSizeBy:(unsigned long long)bytes | 246 | - (void)incrementDownloadSizeBy:(unsigned long long)bytes |
| 231 | { | 247 | { |
| 232 | - if (!downloadProgressDelegate) { | 248 | + if (![self downloadProgressDelegate]) { |
| 233 | return; | 249 | return; |
| 234 | } | 250 | } |
| 235 | - downloadProgressTotalBytes += bytes; | 251 | + [self setDownloadProgressTotalBytes:[self downloadProgressTotalBytes] + bytes]; |
| 236 | [self incrementDownloadProgressBy:0]; | 252 | [self incrementDownloadProgressBy:0]; |
| 237 | } | 253 | } |
| 238 | 254 | ||
| 239 | - (void)incrementDownloadProgressBy:(unsigned long long)bytes | 255 | - (void)incrementDownloadProgressBy:(unsigned long long)bytes |
| 240 | { | 256 | { |
| 241 | - if (!downloadProgressDelegate || downloadProgressTotalBytes == 0) { | 257 | + if (![self downloadProgressDelegate] || [self downloadProgressTotalBytes] == 0) { |
| 242 | return; | 258 | return; |
| 243 | } | 259 | } |
| 244 | - downloadProgressBytes += bytes; | 260 | + [self setDownloadProgressBytes:[self downloadProgressBytes] + bytes]; |
| 245 | - double progress = (downloadProgressBytes*1.0)/(downloadProgressTotalBytes*1.0); | 261 | + double progress = ([self downloadProgressBytes]*1.0)/([self downloadProgressTotalBytes]*1.0); |
| 246 | - [ASIHTTPRequest setProgress:progress forProgressIndicator:downloadProgressDelegate]; | 262 | + [ASIHTTPRequest setProgress:progress forProgressIndicator:[self downloadProgressDelegate]]; |
| 247 | } | 263 | } |
| 248 | 264 | ||
| 249 | // Since this queue takes over as the delegate for all requests it contains, it should forward authorisation requests to its own delegate | 265 | // Since this queue takes over as the delegate for all requests it contains, it should forward authorisation requests to its own delegate |
| 250 | - (void)authorizationNeededForRequest:(ASIHTTPRequest *)request | 266 | - (void)authorizationNeededForRequest:(ASIHTTPRequest *)request |
| 251 | { | 267 | { |
| 252 | - if ([delegate respondsToSelector:@selector(authorizationNeededForRequest:)]) { | 268 | + if ([[self delegate] respondsToSelector:@selector(authorizationNeededForRequest:)]) { |
| 253 | - [delegate performSelector:@selector(authorizationNeededForRequest:) withObject:request]; | 269 | + [[self delegate] performSelector:@selector(authorizationNeededForRequest:) withObject:request]; |
| 254 | } | 270 | } |
| 255 | } | 271 | } |
| 256 | 272 | ||
| @@ -258,7 +274,7 @@ | @@ -258,7 +274,7 @@ | ||
| 258 | - (BOOL)respondsToSelector:(SEL)selector | 274 | - (BOOL)respondsToSelector:(SEL)selector |
| 259 | { | 275 | { |
| 260 | if (selector == @selector(authorizationNeededForRequest:)) { | 276 | if (selector == @selector(authorizationNeededForRequest:)) { |
| 261 | - if ([delegate respondsToSelector:@selector(authorizationNeededForRequest:)]) { | 277 | + if ([[self delegate] respondsToSelector:@selector(authorizationNeededForRequest:)]) { |
| 262 | return YES; | 278 | return YES; |
| 263 | } | 279 | } |
| 264 | return NO; | 280 | return NO; |
| @@ -267,7 +283,12 @@ | @@ -267,7 +283,12 @@ | ||
| 267 | } | 283 | } |
| 268 | 284 | ||
| 269 | 285 | ||
| 270 | - | 286 | +@synthesize requestsCount; |
| 287 | +@synthesize uploadProgressBytes; | ||
| 288 | +@synthesize uploadProgressTotalBytes; | ||
| 289 | +@synthesize downloadProgressBytes; | ||
| 290 | +@synthesize downloadProgressTotalBytes; | ||
| 291 | +@synthesize shouldCancelAllRequestsOnFailure; | ||
| 271 | @synthesize uploadProgressDelegate; | 292 | @synthesize uploadProgressDelegate; |
| 272 | @synthesize downloadProgressDelegate; | 293 | @synthesize downloadProgressDelegate; |
| 273 | @synthesize requestDidFinishSelector; | 294 | @synthesize requestDidFinishSelector; |
-
Please register or login to post a comment