2013-01-22 6 views
10

Io uso UIWebView per caricare web da un webLink e UIWebViewDelegate per controllare lo stato di errore:UIWebView non va a didFailLoadWithError quando non si trova webLink?

[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:webLink]]]; 


- (void)webViewDidStartLoad:(UIWebView *)webView{ 
    NSLog(@"START LOAD"); 

} 

- (void)webViewDidFinishLoad:(UIWebView *)webView{ 
    NSLog(@"FINISH LOAD"); 
} 

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{ 
    NSLog(@"ERROR : %@",error); 
} 

Ma quando webLink non trovato, che non vanno a didFailLoadWithError, vai a startLoad e didFinishLoad. Come andare la situazione quando webLink non trovato? Per favore aiuto!

+0

A proposito, il carico non funziona o si verifica un errore 404 (che, dal punto di vista della vista Web, non è un errore). – Rob

+0

@Rob Grazie. Errore 404, ma non so come controllare – Jack

risposta

20

Sfortunatamente, 404 (o codici simili) non sono considerati errori da UIWebView, poiché è stata ricevuta una risposta HTML. Peggio ancora, lo UIWebView non cattura i codici di risposta per noi, quindi devi farlo manualmente, tramite NSURLConnection. Ecco un modo per affrontare il problema:

@interface ViewController() <UIWebViewDelegate, NSURLConnectionDataDelegate> 

@property (nonatomic) BOOL validatedRequest; 
@property (nonatomic, strong) NSURL *originalUrl; 

@end 

@implementation ViewController 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    // since `shouldStartLoadWithRequest` only validates when a user clicks on a link, we'll bypass that 
    // here and go right to the `NSURLConnection`, which will validate the request, and if good, it will 
    // load the web view for us. 

    self.originalUrl = [NSURL URLWithString:@"http://www.stackoverflow.com"]; 
    NSURLRequest *request = [NSURLRequest requestWithURL:self.originalUrl]; 
    [NSURLConnection connectionWithRequest:request delegate:self]; 
} 

#pragma mark - UIWebViewDelegate 

// you will see this called for 404 errors 

- (void)webViewDidFinishLoad:(UIWebView *)webView 
{ 
    self.validatedRequest = NO; // reset this for the next link the user clicks on 
} 

// you will not see this called for 404 errors 

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error 
{ 
    NSLog(@"%s error=%@", __FUNCTION__, error); 
} 

// this is where you could, intercept HTML requests and route them through 
// NSURLConnection, to see if the server responds successfully. 

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType 
{ 
    // we're only validating links we click on; if we validated that successfully, though, let's just go open it 
    // nb: we're only validating links we click on because some sites initiate additional html requests of 
    // their own, and don't want to get involved in mediating each and every server request; we're only 
    // going to concern ourselves with those links the user clicks on. 

    if (self.validatedRequest || navigationType != UIWebViewNavigationTypeLinkClicked) 
     return YES; 

    // if user clicked on a link and we haven't validated it yet, let's do so 

    self.originalUrl = request.URL; 

    [NSURLConnection connectionWithRequest:request delegate:self]; 

    // and if we're validating, don't bother to have the web view load it yet ... 
    // the `didReceiveResponse` will do that for us once the connection has been validated 

    return NO; 
} 

#pragma mark - NSURLConnectionDataDelegate method 

// This code inspired by http://www.ardalahmet.com/2011/08/18/how-to-detect-and-handle-http-status-codes-in-uiwebviews/ 
// Given that some ISPs do redirects that one might otherwise prefer to see handled as errors, I'm also checking 
// to see if the original URL's host matches the response's URL. This logic may be too restrictive (some valid redirects 
// will be rejected, such as www.adobephotoshop.com which redirects you to www.adobe.com), but does capture the ISP 
// redirect problem I am concerned about. 

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response 
{  
    NSString *originalUrlHostName = self.originalUrl.host; 
    NSString *responseUrlHostName = response.URL.host; 

    NSRange originalInResponse = [responseUrlHostName rangeOfString:originalUrlHostName]; // handle where we went to "apple.com" and got redirected to "www.apple.com" 
    NSRange responseInOriginal = [originalUrlHostName rangeOfString:responseUrlHostName]; // handle where we went to "www.stackoverflow.com" and got redirected to "stackoverflow.com" 

    if (originalInResponse.location == NSNotFound && responseInOriginal.location == NSNotFound) { 
     NSLog(@"%s you were redirected from %@ to %@", __FUNCTION__, self.originalUrl.absoluteString, response.URL.absoluteString); 
    } 

    if ([response isKindOfClass:[NSHTTPURLResponse class]]) { 
     NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode]; 

     if (statusCode < 200 || statusCode >= 300) { 
      NSLog(@"%s request to %@ failed with statusCode=%d", __FUNCTION__, response.URL.absoluteString, statusCode); 
     } else { 
      self.validatedRequest = YES; 

      [self.webView loadRequest:connection.originalRequest]; 
     } 
    } 

    [connection cancel]; 
} 

@end 

nota, nella mia implementazione, non sto solo controllando per codici di stato, ma sto anche controllando per redirect (che si può o non può decidere di fare) . Lo faccio perché alcune ISP intercettano le richieste HTTP e, se il sito di destinazione non viene trovato, ti reindirizzano alla loro pagina web di ricerca (che credo sia un po 'inquietante sapendo che il mio ISP sta controllando ogni sito web che cerco). E se hai a che fare con gli iPhone che si connettono tramite wifi, devi fare i conti con questi capricci.

Così, per esempio, il mio codice di cui sopra è alla ricerca di "http://www.applecom/pages" (in cui ho volutamente omesso il periodo di ".com", che dovrebbe fallire una ricerca DNS), ma per i quali il mio ISP, Verizon, intercettato la richiesta e reindirizzata dalla connessione HTTP alla propria pagina di ricerca e, come tale, la mia app sta segnalando:

2013-01-21 23: 14: 21,896 webtest [24198: C07] - [ViewController collegamento: didReceiveResponse :] sei stato reindirizzato da http://www.applecom/pages a http://search.dnsassist.verizon.net/assist.php?url=www.applecom

Puoi pensare a quale tipo di reindirizzamento è accettabile (ad esempio, se vai su "www.adobephotoshop.com" e ti reindirizza a "www.adobe.com") e quali tipi non lo sono (ad es. se vado a "www.applecom" e mi reindirizza a "search.dnsassist.verizon.net". Potrei essere preoccupato per un problema abbastanza ristretto (che mi riguarda a causa del mio ISP), ma è qualcosa da contemplare.

+0

Grazie mille! Funziona perfettamente in UIView personalizzato. – Jack

+0

@CaoQuy A proposito, ho aggiornato la mia risposta con un sottile problema di reindirizzamento, in cui alcuni ISP (in particolare Verizon) intercettano gli errori DNS e li reindirizzano alla propria pagina di ricerca. Potresti decidere che non ti serve/vuoi occuparti di questo, ma lo presento nell'interesse della piena divulgazione. – Rob

+0

@Rob in IB, devi collegare anche UIWebView al delegato per farlo funzionare? – Supertecnoboff