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 @@
// 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;
... ...
... ... @@ -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,14 +44,33 @@
[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];
... ... @@ -69,12 +78,13 @@
- (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 @@
// 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 @@
// 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,6 +129,8 @@
[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];
}
}
... ... @@ -126,131 +138,135 @@
// 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];
}
}
... ... @@ -258,7 +274,7 @@
- (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 @@
}
@synthesize requestsCount;
@synthesize uploadProgressBytes;
@synthesize uploadProgressTotalBytes;
@synthesize downloadProgressBytes;
@synthesize downloadProgressTotalBytes;
@synthesize shouldCancelAllRequestsOnFailure;
@synthesize uploadProgressDelegate;
@synthesize downloadProgressDelegate;
@synthesize requestDidFinishSelector;
... ...