Skip to content

Commit

Permalink
ASINetwork queues now show the network progress indicator while netwo…
Browse files Browse the repository at this point in the history
…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
pokeb committed Jul 19, 2009
1 parent 85b5c95 commit d97f191
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 92 deletions.
7 changes: 7 additions & 0 deletions Classes/ASINetworkQueue.h
Expand Up @@ -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;
Expand Down
205 changes: 113 additions & 92 deletions Classes/ASINetworkQueue.m
Expand Up @@ -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];

Expand All @@ -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
Expand All @@ -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]];
}
}

Expand All @@ -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]];
}
}

Expand All @@ -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;
Expand All @@ -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;
Expand Down

0 comments on commit d97f191

Please sign in to comment.