Ben Copsey

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
@@ -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;