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