Ben Copsey

ASIWebPageRequests now pass on most delegate calls to their parent's delegate

Improve iPad ASIWebPageRequest sample to show the resources being downloaded
@@ -1636,7 +1636,7 @@ static NSOperationQueue *sharedQueue = nil; @@ -1636,7 +1636,7 @@ static NSOperationQueue *sharedQueue = nil;
1636 { 1636 {
1637 if ([*target respondsToSelector:selector]) { 1637 if ([*target respondsToSelector:selector]) {
1638 NSMethodSignature *signature = nil; 1638 NSMethodSignature *signature = nil;
1639 - signature = [[*target class] instanceMethodSignatureForSelector:selector]; 1639 + signature = [*target methodSignatureForSelector:selector];
1640 NSInvocation *invocation = [[NSInvocation invocationWithMethodSignature:signature] retain]; 1640 NSInvocation *invocation = [[NSInvocation invocationWithMethodSignature:signature] retain];
1641 [invocation setSelector:selector]; 1641 [invocation setSelector:selector];
1642 1642
@@ -43,7 +43,6 @@ static NSMutableArray *requestsUsingXMLParser = nil; @@ -43,7 +43,6 @@ static NSMutableArray *requestsUsingXMLParser = nil;
43 { 43 {
44 } 44 }
45 45
46 -  
47 - (void)requestFinished 46 - (void)requestFinished
48 { 47 {
49 complete = NO; 48 complete = NO;
@@ -114,6 +113,9 @@ static NSMutableArray *requestsUsingXMLParser = nil; @@ -114,6 +113,9 @@ static NSMutableArray *requestsUsingXMLParser = nil;
114 [externalResourceRequest setParentRequest:self]; 113 [externalResourceRequest setParentRequest:self];
115 [externalResourceRequest setReplaceURLsWithDataURLs:[self replaceURLsWithDataURLs]]; 114 [externalResourceRequest setReplaceURLsWithDataURLs:[self replaceURLsWithDataURLs]];
116 [externalResourceRequest setShouldResetDownloadProgress:NO]; 115 [externalResourceRequest setShouldResetDownloadProgress:NO];
  116 + [externalResourceRequest setDelegate:self];
  117 + [externalResourceRequest setUploadProgressDelegate:self];
  118 + [externalResourceRequest setDownloadProgressDelegate:self];
117 if ([self downloadDestinationPath]) { 119 if ([self downloadDestinationPath]) {
118 [externalResourceRequest setDownloadDestinationPath:[self cachePathForRequest:externalResourceRequest]]; 120 [externalResourceRequest setDownloadDestinationPath:[self cachePathForRequest:externalResourceRequest]];
119 } 121 }
@@ -179,42 +181,18 @@ static NSMutableArray *requestsUsingXMLParser = nil; @@ -179,42 +181,18 @@ static NSMutableArray *requestsUsingXMLParser = nil;
179 [externalResourceRequest setParentRequest:self]; 181 [externalResourceRequest setParentRequest:self];
180 [externalResourceRequest setReplaceURLsWithDataURLs:[self replaceURLsWithDataURLs]]; 182 [externalResourceRequest setReplaceURLsWithDataURLs:[self replaceURLsWithDataURLs]];
181 [externalResourceRequest setShouldResetDownloadProgress:NO]; 183 [externalResourceRequest setShouldResetDownloadProgress:NO];
  184 + [externalResourceRequest setDelegate:self];
  185 + [externalResourceRequest setUploadProgressDelegate:self];
  186 + [externalResourceRequest setDownloadProgressDelegate:self];
182 if ([self downloadDestinationPath]) { 187 if ([self downloadDestinationPath]) {
183 [externalResourceRequest setDownloadDestinationPath:[self cachePathForRequest:externalResourceRequest]]; 188 [externalResourceRequest setDownloadDestinationPath:[self cachePathForRequest:externalResourceRequest]];
184 } 189 }
185 [[self externalResourceQueue] addOperation:externalResourceRequest]; 190 [[self externalResourceQueue] addOperation:externalResourceRequest];
186 [externalResourceRequest setShowAccurateProgress:YES]; 191 [externalResourceRequest setShowAccurateProgress:YES];
187 - [self incrementDownloadSizeBy:1];  
188 } 192 }
189 [[self externalResourceQueue] go]; 193 [[self externalResourceQueue] go];
190 } 194 }
191 195
192 -- (void)updateDownloadProgress  
193 -{  
194 - if ([self parentRequest]) {  
195 - [[self parentRequest] updateDownloadProgress];  
196 - return;  
197 - }  
198 - [super updateDownloadProgress];  
199 -}  
200 -  
201 -- (void)setContentLength:(unsigned long long)newContentLength  
202 -{  
203 - if ([self parentRequest]) {  
204 - [[self parentRequest] setContentLength:[[self parentRequest] contentLength]+newContentLength-contentLength];  
205 - }  
206 - [super setContentLength:newContentLength];  
207 -}  
208 -- (void)setTotalBytesRead:(unsigned long long)bytes  
209 -{  
210 - totalBytesRead = bytes;  
211 - if ([self parentRequest]) {  
212 - [[self parentRequest] setTotalBytesRead:[[self parentRequest] totalBytesRead]+totalBytesRead-lastBytesRead];  
213 - lastBytesRead = totalBytesRead;  
214 - return;  
215 - }  
216 -}  
217 -  
218 - (void)externalResourceFetchSucceeded:(ASIHTTPRequest *)externalResourceRequest 196 - (void)externalResourceFetchSucceeded:(ASIHTTPRequest *)externalResourceRequest
219 { 197 {
220 NSString *originalPath = [[externalResourceRequest userInfo] objectForKey:@"Path"]; 198 NSString *originalPath = [[externalResourceRequest userInfo] objectForKey:@"Path"];
@@ -439,6 +417,52 @@ static NSMutableArray *requestsUsingXMLParser = nil; @@ -439,6 +417,52 @@ static NSMutableArray *requestsUsingXMLParser = nil;
439 xmlXPathFreeContext(xpathCtx); 417 xmlXPathFreeContext(xpathCtx);
440 } 418 }
441 419
  420 +- (BOOL)respondsToSelector:(SEL)selector
  421 +{
  422 + if ([self parentRequest]) {
  423 + return [[self parentRequest] respondsToSelector:selector];
  424 + }
  425 + //Ok, now check for selectors we want to pass on to the delegate
  426 + if (selector == @selector(requestStarted:) || selector == @selector(request:didReceiveResponseHeaders:) || selector == @selector(request:willRedirectToURL:) || selector == @selector(requestFinished:) || selector == @selector(requestFailed:) || selector == @selector(request:didReceiveData:) || selector == @selector(authenticationNeededForRequest:) || selector == @selector(proxyAuthenticationNeededForRequest:)) {
  427 + return [delegate respondsToSelector:selector];
  428 + } else if (selector == @selector(request:didReceiveBytes:) || selector == @selector(request:incrementDownloadSizeBy:)) {
  429 + return [downloadProgressDelegate respondsToSelector:selector];
  430 + } else if (selector == @selector(request:didSendBytes:) || selector == @selector(request:incrementUploadSizeBy:)) {
  431 + return [uploadProgressDelegate respondsToSelector:selector];
  432 + }
  433 + return [super respondsToSelector:selector];
  434 +}
  435 +
  436 +- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
  437 +{
  438 + if ([self parentRequest]) {
  439 + return [[self parentRequest] methodSignatureForSelector:selector];
  440 + }
  441 + if (selector == @selector(requestStarted:) || selector == @selector(request:didReceiveResponseHeaders:) || selector == @selector(request:willRedirectToURL:) || selector == @selector(requestFinished:) || selector == @selector(requestFailed:) || selector == @selector(request:didReceiveData:) || selector == @selector(authenticationNeededForRequest:) || selector == @selector(proxyAuthenticationNeededForRequest:)) {
  442 + return [(id)delegate methodSignatureForSelector:selector];
  443 + } else if (selector == @selector(request:didReceiveBytes:) || selector == @selector(request:incrementDownloadSizeBy:)) {
  444 + return [(id)downloadProgressDelegate methodSignatureForSelector:selector];
  445 + } else if (selector == @selector(request:didSendBytes:) || selector == @selector(request:incrementUploadSizeBy:)) {
  446 + return [(id)uploadProgressDelegate methodSignatureForSelector:selector];
  447 + }
  448 + return nil;
  449 +}
  450 +
  451 +- (void)forwardInvocation:(NSInvocation *)anInvocation
  452 +{
  453 + if ([self parentRequest]) {
  454 + return [[self parentRequest] forwardInvocation:anInvocation];
  455 + }
  456 + SEL selector = [anInvocation selector];
  457 + if (selector == @selector(requestStarted:) || selector == @selector(request:didReceiveResponseHeaders:) || selector == @selector(request:willRedirectToURL:) || selector == @selector(requestFinished:) || selector == @selector(requestFailed:) || selector == @selector(request:didReceiveData:) || selector == @selector(authenticationNeededForRequest:) || selector == @selector(proxyAuthenticationNeededForRequest:)) {
  458 + [anInvocation invokeWithTarget:delegate];
  459 + } else if (selector == @selector(request:didReceiveBytes:) || selector == @selector(request:incrementDownloadSizeBy:)) {
  460 + [anInvocation invokeWithTarget:downloadProgressDelegate];
  461 + } else if (selector == @selector(request:didSendBytes:) || selector == @selector(request:incrementUploadSizeBy:)) {
  462 + [anInvocation invokeWithTarget:uploadProgressDelegate];
  463 + }
  464 +}
  465 +
442 + (NSArray *)CSSURLsFromString:(NSString *)string 466 + (NSArray *)CSSURLsFromString:(NSString *)string
443 { 467 {
444 NSMutableArray *urls = [NSMutableArray array]; 468 NSMutableArray *urls = [NSMutableArray array];
@@ -496,6 +520,7 @@ static NSMutableArray *requestsUsingXMLParser = nil; @@ -496,6 +520,7 @@ static NSMutableArray *requestsUsingXMLParser = nil;
496 } 520 }
497 } 521 }
498 522
  523 +
499 @synthesize externalResourceQueue; 524 @synthesize externalResourceQueue;
500 @synthesize resourceList; 525 @synthesize resourceList;
501 @synthesize parentRequest; 526 @synthesize parentRequest;
  1 +//
  2 +// RequestProgressCell.h
  3 +// iPhone
  4 +//
  5 +// Created by Ben Copsey on 03/10/2010.
  6 +// Copyright 2010 All-Seeing Interactive. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +
  12 +@interface RequestProgressCell : UITableViewCell {
  13 + UIProgressView *progressView;
  14 +}
  15 ++ (id)cell;
  16 +
  17 +@property (assign, nonatomic) UIProgressView *progressView;
  18 +@end
  1 +//
  2 +// RequestProgressCell.m
  3 +// iPhone
  4 +//
  5 +// Created by Ben Copsey on 03/10/2010.
  6 +// Copyright 2010 All-Seeing Interactive. All rights reserved.
  7 +//
  8 +
  9 +#import "RequestProgressCell.h"
  10 +
  11 +
  12 +@implementation RequestProgressCell
  13 +
  14 ++ (id)cell
  15 +{
  16 + RequestProgressCell *cell = [[[RequestProgressCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"RequestProgressCell"] autorelease];
  17 + [[cell textLabel] setTextAlignment:UITextAlignmentLeft];
  18 + [[cell textLabel] setFont:[UIFont systemFontOfSize:12]];
  19 + [cell setProgressView:[[[UIProgressView alloc] initWithFrame:CGRectMake(0,0,100,20)] autorelease]];
  20 + [cell setAccessoryView:[cell progressView]];
  21 + return cell;
  22 +}
  23 +
  24 +- (void)layoutSubviews
  25 +{
  26 + [super layoutSubviews];
  27 + CGRect f = [[self accessoryView] frame];
  28 + [[self accessoryView] setFrame:CGRectMake(f.origin.x, f.origin.y+6, f.size.width, f.size.height)];
  29 +
  30 +}
  31 +
  32 +@synthesize progressView;
  33 +@end
@@ -17,8 +17,10 @@ @@ -17,8 +17,10 @@
17 UITextView *responseField; 17 UITextView *responseField;
18 UISwitch *replaceURLsSwitch; 18 UISwitch *replaceURLsSwitch;
19 ASIWebPageRequest *request; 19 ASIWebPageRequest *request;
  20 + NSMutableArray *requestsInProgress;
20 } 21 }
21 - (void)fetchURL:(NSURL *)url; 22 - (void)fetchURL:(NSURL *)url;
22 23
23 @property (retain, nonatomic) ASIWebPageRequest *request; 24 @property (retain, nonatomic) ASIWebPageRequest *request;
  25 +@property (retain, nonatomic) NSMutableArray *requestsInProgress;
24 @end 26 @end
@@ -11,12 +11,13 @@ @@ -11,12 +11,13 @@
11 #import "ASIWebPageRequest.h" 11 #import "ASIWebPageRequest.h"
12 #import "ASIDownloadCache.h" 12 #import "ASIDownloadCache.h"
13 #import "ToggleCell.h" 13 #import "ToggleCell.h"
  14 +#import "RequestProgressCell.h"
  15 +
14 @implementation WebPageViewController 16 @implementation WebPageViewController
15 17
16 - (void)fetchWebPage:(id)sender 18 - (void)fetchWebPage:(id)sender
17 { 19 {
18 [self fetchURL:[NSURL URLWithString:[urlField text]]]; 20 [self fetchURL:[NSURL URLWithString:[urlField text]]];
19 -  
20 } 21 }
21 22
22 - (void)clearCache:(id)sender 23 - (void)clearCache:(id)sender
@@ -27,17 +28,23 @@ @@ -27,17 +28,23 @@
27 28
28 - (void)fetchURL:(NSURL *)url 29 - (void)fetchURL:(NSURL *)url
29 { 30 {
  31 + [urlField resignFirstResponder];
  32 +
  33 + [self setRequestsInProgress:[NSMutableArray array]];
  34 + [[self tableView] reloadSections:[NSIndexSet indexSetWithIndex:2] withRowAnimation:UITableViewRowAnimationBottom];
  35 +
30 // This allows our ASIDownloadCache to masquerade as as NSURLCache 36 // This allows our ASIDownloadCache to masquerade as as NSURLCache
31 // It allows the webView to load the content we downloaded when replaceURLsWithDataURLs is NO 37 // It allows the webView to load the content we downloaded when replaceURLsWithDataURLs is NO
32 [NSURLCache setSharedURLCache:[ASIDownloadCache sharedCache]]; 38 [NSURLCache setSharedURLCache:[ASIDownloadCache sharedCache]];
33 - 39 +
34 [request setDelegate:nil]; 40 [request setDelegate:nil];
35 [request cancel]; 41 [request cancel];
36 [self setRequest:[ASIWebPageRequest requestWithURL:url]]; 42 [self setRequest:[ASIWebPageRequest requestWithURL:url]];
37 - 43 +
38 [request setDidFailSelector:@selector(webPageFetchFailed:)]; 44 [request setDidFailSelector:@selector(webPageFetchFailed:)];
39 [request setDidFinishSelector:@selector(webPageFetchSucceeded:)]; 45 [request setDidFinishSelector:@selector(webPageFetchSucceeded:)];
40 [request setDelegate:self]; 46 [request setDelegate:self];
  47 + [request setDownloadProgressDelegate:self];
41 [request setShowAccurateProgress:NO]; 48 [request setShowAccurateProgress:NO];
42 [request setReplaceURLsWithDataURLs:[replaceURLsSwitch isOn]]; 49 [request setReplaceURLsWithDataURLs:[replaceURLsSwitch isOn]];
43 50
@@ -68,6 +75,7 @@ @@ -68,6 +75,7 @@
68 [urlField setText:[[theRequest url] absoluteString]]; 75 [urlField setText:[[theRequest url] absoluteString]];
69 } 76 }
70 77
  78 +// We'll take over the page load when the user clicks on a link
71 - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)theRequest navigationType:(UIWebViewNavigationType)navigationType 79 - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)theRequest navigationType:(UIWebViewNavigationType)navigationType
72 { 80 {
73 if (navigationType == UIWebViewNavigationTypeLinkClicked) { 81 if (navigationType == UIWebViewNavigationTypeLinkClicked) {
@@ -77,6 +85,32 @@ @@ -77,6 +85,32 @@
77 return YES; 85 return YES;
78 } 86 }
79 87
  88 +
  89 +// At time of writing ASIWebPageRequests do not support automatic progress tracking across all requests needed for a page
  90 +// The code below shows one approach you could use for tracking progress - it creates a new row with a progress indicator for each resource request
  91 +// However, you could use the same approach and keep track of an overal total to show progress
  92 +- (void)requestStarted:(ASIWebPageRequest *)theRequest
  93 +{
  94 + [[self requestsInProgress] addObject:theRequest];
  95 + [[self tableView] beginUpdates];
  96 + [[self tableView] insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:[[self requestsInProgress] count]-1 inSection:2]] withRowAnimation:UITableViewRowAnimationBottom];
  97 + [[self tableView] endUpdates];
  98 +}
  99 +
  100 +- (void)request:(ASIHTTPRequest *)theRequest didReceiveBytes:(long long)newLength
  101 +{
  102 + NSInteger requestNumber = [[self requestsInProgress] indexOfObject:theRequest];
  103 + if (requestNumber != NSNotFound) {
  104 + RequestProgressCell *cell = (RequestProgressCell *)[[self tableView] cellForRowAtIndexPath:[NSIndexPath indexPathForRow:requestNumber inSection:2]];
  105 + [[cell progressView] setProgress:[theRequest totalBytesRead]/([theRequest contentLength]+[theRequest partialDownloadSize])];
  106 + }
  107 +}
  108 +
  109 +- (void)request:(ASIHTTPRequest *)theRequest incrementDownloadSizeBy:(long long)newLength
  110 +{
  111 + [self request:theRequest didReceiveBytes:0];
  112 +}
  113 +
80 /* 114 /*
81 Most of the code below here relates to the table view, and isn't that interesting 115 Most of the code below here relates to the table view, and isn't that interesting
82 */ 116 */
@@ -147,6 +181,18 @@ static NSString *intro = @"ASIWebPageRequest lets you download complete webpages @@ -147,6 +181,18 @@ static NSString *intro = @"ASIWebPageRequest lets you download complete webpages
147 replaceURLsSwitch = [(ToggleCell *)cell toggle]; 181 replaceURLsSwitch = [(ToggleCell *)cell toggle];
148 } 182 }
149 } else if ([indexPath section] == 2) { 183 } else if ([indexPath section] == 2) {
  184 +
  185 + cell = [tableView dequeueReusableCellWithIdentifier:@"RequestProgressCell"];
  186 + if (!cell) {
  187 + cell = [RequestProgressCell cell];
  188 + }
  189 + ASIHTTPRequest *theRequest = [[self requestsInProgress] objectAtIndex:[indexPath row]];
  190 + [[cell textLabel] setText:[[theRequest url] absoluteString]];
  191 + if ([theRequest contentLength] > 0) {
  192 + [[(RequestProgressCell *)cell progressView] setProgress:[theRequest totalBytesRead]/([theRequest contentLength]+[theRequest partialDownloadSize])];
  193 + }
  194 +
  195 + } else if ([indexPath section] == 3) {
150 196
151 cell = [tableView dequeueReusableCellWithIdentifier:@"Response"]; 197 cell = [tableView dequeueReusableCellWithIdentifier:@"Response"];
152 if (!cell) { 198 if (!cell) {
@@ -156,7 +202,6 @@ static NSString *intro = @"ASIWebPageRequest lets you download complete webpages @@ -156,7 +202,6 @@ static NSString *intro = @"ASIWebPageRequest lets you download complete webpages
156 } 202 }
157 [responseField setFrame:CGRectMake(5,5,tableWidth-tablePadding,180)]; 203 [responseField setFrame:CGRectMake(5,5,tableWidth-tablePadding,180)];
158 204
159 -  
160 } 205 }
161 [cell setSelectionStyle:UITableViewCellSelectionStyleNone]; 206 [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
162 return cell; 207 return cell;
@@ -204,6 +249,8 @@ static NSString *intro = @"ASIWebPageRequest lets you download complete webpages @@ -204,6 +249,8 @@ static NSString *intro = @"ASIWebPageRequest lets you download complete webpages
204 { 249 {
205 if (section == 1) { 250 if (section == 1) {
206 return 2; 251 return 2;
  252 + } else if (section == 2) {
  253 + return [requestsInProgress count];
207 } 254 }
208 return 1; 255 return 1;
209 } 256 }
@@ -226,8 +273,10 @@ static NSString *intro = @"ASIWebPageRequest lets you download complete webpages @@ -226,8 +273,10 @@ static NSString *intro = @"ASIWebPageRequest lets you download complete webpages
226 } else { 273 } else {
227 return 50; 274 return 50;
228 } 275 }
229 - } else { 276 + } else if ([indexPath section] == 3) {
230 return 200; 277 return 200;
  278 + } else {
  279 + return 34;
231 } 280 }
232 } 281 }
233 282
@@ -238,8 +287,9 @@ static NSString *intro = @"ASIWebPageRequest lets you download complete webpages @@ -238,8 +287,9 @@ static NSString *intro = @"ASIWebPageRequest lets you download complete webpages
238 287
239 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 288 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
240 { 289 {
241 - return 3; 290 + return 4;
242 } 291 }
243 292
244 @synthesize request; 293 @synthesize request;
  294 +@synthesize requestsInProgress;
245 @end 295 @end
This diff was suppressed by a .gitattributes entry.