Permalink
Browse files

ASINetwork queues now show the network progress indicator while netwo…

…rk 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
  • Loading branch information...
1 parent 85b5c95 commit d97f19106720dbff93ee9dac9cc68e3da6eb26db @pokeb committed Jul 19, 2009
Showing with 120 additions and 92 deletions.
  1. +7 −0 Classes/ASINetworkQueue.h
  2. +113 −92 Classes/ASINetworkQueue.m
@@ -87,6 +87,13 @@
// This method will start the queue
- (void)go;
+// Used on iPhone platform to show / hide the network activity indicator (in the status bar)
+// On mac, you could subclass to do something else
+- (void)updateNetworkActivityIndicator;
+
+// Returns YES if the queue is in progress
+- (BOOL)isNetworkActive;
+
@property (assign,setter=setUploadProgressDelegate:) id uploadProgressDelegate;
@property (assign,setter=setDownloadProgressDelegate:) id downloadProgressDelegate;
View
@@ -9,31 +9,21 @@
#import "ASINetworkQueue.h"
#import "ASIHTTPRequest.h"
+// Private stuff
+@interface ASINetworkQueue ()
+ @property (assign) int requestsCount;
+ @property (assign) unsigned long long uploadProgressBytes;
+ @property (assign) unsigned long long uploadProgressTotalBytes;
+ @property (assign) unsigned long long downloadProgressBytes;
+ @property (assign) unsigned long long downloadProgressTotalBytes;
+@end
@implementation ASINetworkQueue
- (id)init
{
self = [super init];
-
- delegate = nil;
- requestDidFinishSelector = NULL;
- requestDidFailSelector = NULL;
- queueDidFinishSelector = NULL;
- shouldCancelAllRequestsOnFailure = YES;
-
- uploadProgressDelegate = nil;
- uploadProgressBytes = 0;
- uploadProgressTotalBytes = 0;
-
- downloadProgressDelegate = nil;
- downloadProgressBytes = 0;
- downloadProgressTotalBytes = 0;
-
- requestsCount = 0;
-
- showAccurateProgress = NO;
-
+ [self setShouldCancelAllRequestsOnFailure:YES];
[self setMaxConcurrentOperationCount:4];
[self setSuspended:YES];
@@ -54,27 +44,47 @@ - (void)dealloc
[super dealloc];
}
+- (BOOL)isNetworkActive
+{
+ return ([self requestsCount] > 0 && ![self isSuspended]);
+}
+
+- (void)updateNetworkActivityIndicator
+{
+#if TARGET_OS_IPHONE
+ [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:[self isNetworkActive]];
+#endif
+}
+
+- (void)setSuspended:(BOOL)suspend
+{
+ [super setSuspended:suspend];
+ [self updateNetworkActivityIndicator];
+}
+
+
- (void)go
{
- if (!showAccurateProgress) {
- if (downloadProgressDelegate) {
- [self incrementDownloadSizeBy:requestsCount];
+ if (![self showAccurateProgress]) {
+ if ([self downloadProgressDelegate]) {
+ [self incrementDownloadSizeBy:[self requestsCount]];
}
- if (uploadProgressDelegate) {
- [self incrementUploadSizeBy:requestsCount];
+ if ([self uploadProgressDelegate]) {
+ [self incrementUploadSizeBy:[self requestsCount]];
}
}
[self setSuspended:NO];
}
- (void)cancelAllOperations
{
- requestsCount = 0;
- uploadProgressBytes = 0;
- uploadProgressTotalBytes = 0;
- downloadProgressBytes = 0;
- downloadProgressTotalBytes = 0;
+ [self setRequestsCount:0];
+ [self setUploadProgressBytes:0];
+ [self setUploadProgressTotalBytes:0];
+ [self setDownloadProgressBytes:0];
+ [self setDownloadProgressTotalBytes:0];
[super cancelAllOperations];
+ [self updateNetworkActivityIndicator];
}
- (void)setUploadProgressDelegate:(id)newDelegate
@@ -83,13 +93,13 @@ - (void)setUploadProgressDelegate:(id)newDelegate
// If the uploadProgressDelegate is an NSProgressIndicator, we set it's MaxValue to 1.0 so we can treat it similarly to UIProgressViews
SEL selector = @selector(setMaxValue:);
- if ([uploadProgressDelegate respondsToSelector:selector]) {
+ if ([[self uploadProgressDelegate] respondsToSelector:selector]) {
double max = 1.0;
- NSMethodSignature *signature = [[uploadProgressDelegate class] instanceMethodSignatureForSelector:selector];
+ NSMethodSignature *signature = [[[self uploadProgressDelegate] class] instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setSelector:selector];
[invocation setArgument:&max atIndex:2];
- [invocation invokeWithTarget:uploadProgressDelegate];
+ [invocation invokeWithTarget:[self uploadProgressDelegate]];
}
}
@@ -100,13 +110,13 @@ - (void)setDownloadProgressDelegate:(id)newDelegate
// If the downloadProgressDelegate is an NSProgressIndicator, we set it's MaxValue to 1.0 so we can treat it similarly to UIProgressViews
SEL selector = @selector(setMaxValue:);
- if ([downloadProgressDelegate respondsToSelector:selector]) {
+ if ([[self downloadProgressDelegate] respondsToSelector:selector]) {
double max = 1.0;
- NSMethodSignature *signature = [[downloadProgressDelegate class] instanceMethodSignatureForSelector:selector];
+ NSMethodSignature *signature = [[[self downloadProgressDelegate] class] instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setSelector:@selector(setMaxValue:)];
[invocation setArgument:&max atIndex:2];
- [invocation invokeWithTarget:downloadProgressDelegate];
+ [invocation invokeWithTarget:[self downloadProgressDelegate]];
}
}
@@ -119,146 +129,152 @@ - (void)addHEADOperation:(NSOperation *)operation
[request setQueuePriority:10];
[request setShowAccurateProgress:YES];
[request setQueue:self];
+
+ // Important - we are calling NSOperation's add method - we don't want to add this as a normal request!
[super addOperation:request];
}
}
// Only add ASIHTTPRequests to this queue!!
- (void)addOperation:(NSOperation *)operation
{
- if ([operation isKindOfClass:[ASIHTTPRequest class]]) {
-
- requestsCount++;
+ if (![operation isKindOfClass:[ASIHTTPRequest class]]) {
+ [NSException raise:@"AttemptToAddInvalidRequest" format:@"Attempted to add an object that was not an ASIHTTPRequest to an ASINetworkQueue"];
+ }
- ASIHTTPRequest *request = (ASIHTTPRequest *)operation;
+ [self setRequestsCount:[self requestsCount]+1];
+
+ ASIHTTPRequest *request = (ASIHTTPRequest *)operation;
+
+ if ([self showAccurateProgress]) {
- if (showAccurateProgress) {
+ // If this is a GET request and we want accurate progress, perform a HEAD request first to get the content-length
+ if ([[request requestMethod] isEqualToString:@"GET"]) {
+ ASIHTTPRequest *HEADRequest = [[[ASIHTTPRequest alloc] initWithURL:[request url]] autorelease];
+ [HEADRequest setMainRequest:request];
+ [self addHEADOperation:HEADRequest];
- // If this is a GET request and we want accurate progress, perform a HEAD request first to get the content-length
- if ([[request requestMethod] isEqualToString:@"GET"]) {
- ASIHTTPRequest *HEADRequest = [[[ASIHTTPRequest alloc] initWithURL:[request url]] autorelease];
- [HEADRequest setMainRequest:request];
- [self addHEADOperation:HEADRequest];
-
- //Tell the request not to reset the progress indicator when it gets a content-length, as we will get the length from the HEAD request
- [request setShouldResetProgressIndicators:NO];
-
- [request addDependency:HEADRequest];
+ //Tell the request not to reset the progress indicator when it gets a content-length, as we will get the length from the HEAD request
+ [request setShouldResetProgressIndicators:NO];
- // If we want to track uploading for this request accurately, we need to add the size of the post content to the total
- } else if (uploadProgressDelegate) {
- [request buildPostBody];
- uploadProgressTotalBytes += [request postLength];
- }
- }
- [request setShowAccurateProgress:showAccurateProgress];
-
+ [request addDependency:HEADRequest];
- [request setQueue:self];
- [super addOperation:request];
+ // If we want to track uploading for this request accurately, we need to add the size of the post content to the total
+ } else if (uploadProgressDelegate) {
+ [request buildPostBody];
+ [self setUploadProgressTotalBytes:[self uploadProgressTotalBytes]+[request postLength]];
+ }
}
+ [request setShowAccurateProgress:[self showAccurateProgress]];
+
+ [request setQueue:self];
+ [super addOperation:request];
+ [self updateNetworkActivityIndicator];
+
}
- (void)requestDidFail:(ASIHTTPRequest *)request
{
- requestsCount--;
- if (requestDidFailSelector) {
- [delegate performSelector:requestDidFailSelector withObject:request];
+ [self setRequestsCount:[self requestsCount]-1];
+ if ([self requestDidFailSelector]) {
+ [[self delegate] performSelector:[self requestDidFailSelector] withObject:request];
}
- if (shouldCancelAllRequestsOnFailure && requestsCount > 0) {
+ if ([self shouldCancelAllRequestsOnFailure] && [self requestsCount] > 0) {
[self cancelAllOperations];
}
+ [self updateNetworkActivityIndicator];
}
- (void)requestDidFinish:(ASIHTTPRequest *)request
{
- requestsCount--;
- if (requestDidFinishSelector) {
- [delegate performSelector:requestDidFinishSelector withObject:request];
+ [self setRequestsCount:[self requestsCount]-1];
+ if ([self requestDidFinishSelector]) {
+ [[self delegate] performSelector:[self requestDidFinishSelector] withObject:request];
}
- if (requestsCount == 0) {
- if (queueDidFinishSelector) {
- [delegate performSelector:queueDidFinishSelector withObject:self];
+ if ([self requestsCount] == 0) {
+ if ([self queueDidFinishSelector]) {
+ [[self delegate] performSelector:[self queueDidFinishSelector] withObject:self];
}
}
+ [self updateNetworkActivityIndicator];
}
- (void)setUploadBufferSize:(unsigned long long)bytes
{
- if (!uploadProgressDelegate) {
+ if (![self uploadProgressDelegate]) {
return;
}
- uploadProgressTotalBytes -= bytes;
+ [self setUploadProgressTotalBytes:[self uploadProgressTotalBytes] - bytes];
[self incrementUploadProgressBy:0];
}
- (void)incrementUploadSizeBy:(unsigned long long)bytes
{
- if (!uploadProgressDelegate) {
+ if (![self uploadProgressDelegate]) {
return;
}
- uploadProgressTotalBytes += bytes;
+ [self setUploadProgressTotalBytes:[self uploadProgressTotalBytes] + bytes];
[self incrementUploadProgressBy:0];
}
- (void)decrementUploadProgressBy:(unsigned long long)bytes
{
- if (!uploadProgressDelegate || uploadProgressTotalBytes == 0) {
+ if (![self uploadProgressDelegate] || [self uploadProgressTotalBytes] == 0) {
return;
}
- uploadProgressBytes -= bytes;
+ [self setUploadProgressBytes:[self uploadProgressBytes] - bytes];
- double progress = (uploadProgressBytes*1.0)/(uploadProgressTotalBytes*1.0);
- [ASIHTTPRequest setProgress:progress forProgressIndicator:uploadProgressDelegate];
+ double progress = ([self uploadProgressBytes]*1.0)/([self uploadProgressTotalBytes]*1.0);
+ [ASIHTTPRequest setProgress:progress forProgressIndicator:[self uploadProgressDelegate]];
}
- (void)incrementUploadProgressBy:(unsigned long long)bytes
{
- if (!uploadProgressDelegate || uploadProgressTotalBytes == 0) {
+ if (![self uploadProgressDelegate] || [self uploadProgressTotalBytes] == 0) {
return;
}
- uploadProgressBytes += bytes;
+ [self setUploadProgressBytes:[self uploadProgressBytes] + bytes];
- double progress = (uploadProgressBytes*1.0)/(uploadProgressTotalBytes*1.0);
- [ASIHTTPRequest setProgress:progress forProgressIndicator:uploadProgressDelegate];
+ double progress = ([self uploadProgressBytes]*1.0)/([self uploadProgressTotalBytes]*1.0);
+ [ASIHTTPRequest setProgress:progress forProgressIndicator:[self uploadProgressDelegate]];
}
- (void)incrementDownloadSizeBy:(unsigned long long)bytes
{
- if (!downloadProgressDelegate) {
+ if (![self downloadProgressDelegate]) {
return;
}
- downloadProgressTotalBytes += bytes;
+ [self setDownloadProgressTotalBytes:[self downloadProgressTotalBytes] + bytes];
[self incrementDownloadProgressBy:0];
}
- (void)incrementDownloadProgressBy:(unsigned long long)bytes
{
- if (!downloadProgressDelegate || downloadProgressTotalBytes == 0) {
+ if (![self downloadProgressDelegate] || [self downloadProgressTotalBytes] == 0) {
return;
}
- downloadProgressBytes += bytes;
- double progress = (downloadProgressBytes*1.0)/(downloadProgressTotalBytes*1.0);
- [ASIHTTPRequest setProgress:progress forProgressIndicator:downloadProgressDelegate];
+ [self setDownloadProgressBytes:[self downloadProgressBytes] + bytes];
+ double progress = ([self downloadProgressBytes]*1.0)/([self downloadProgressTotalBytes]*1.0);
+ [ASIHTTPRequest setProgress:progress forProgressIndicator:[self downloadProgressDelegate]];
}
// Since this queue takes over as the delegate for all requests it contains, it should forward authorisation requests to its own delegate
- (void)authorizationNeededForRequest:(ASIHTTPRequest *)request
{
- if ([delegate respondsToSelector:@selector(authorizationNeededForRequest:)]) {
- [delegate performSelector:@selector(authorizationNeededForRequest:) withObject:request];
+ if ([[self delegate] respondsToSelector:@selector(authorizationNeededForRequest:)]) {
+ [[self delegate] performSelector:@selector(authorizationNeededForRequest:) withObject:request];
}
}
- (BOOL)respondsToSelector:(SEL)selector
{
if (selector == @selector(authorizationNeededForRequest:)) {
- if ([delegate respondsToSelector:@selector(authorizationNeededForRequest:)]) {
+ if ([[self delegate] respondsToSelector:@selector(authorizationNeededForRequest:)]) {
return YES;
}
return NO;
@@ -267,7 +283,12 @@ - (BOOL)respondsToSelector:(SEL)selector
}
-
+@synthesize requestsCount;
+@synthesize uploadProgressBytes;
+@synthesize uploadProgressTotalBytes;
+@synthesize downloadProgressBytes;
+@synthesize downloadProgressTotalBytes;
+@synthesize shouldCancelAllRequestsOnFailure;
@synthesize uploadProgressDelegate;
@synthesize downloadProgressDelegate;
@synthesize requestDidFinishSelector;

1 comment on commit d97f191

Contributor

metaskills commented on d97f191 Jul 19, 2009

Good stuff, I like how you took into account if the queue was suspended too.

Please sign in to comment.