UIWebView не идет в didFailLoadWithError, когда webLink не найден?

Я использую UIWebView для загрузки веб-страниц из webLink и UIWebViewDelegate для управления состоянием ошибки:

[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);
}

Но когда webLink не найден, он не переходит в didFailLoadWithError, он переходит в startLoad и didFinishLoad. Как выйти из ситуации, когда webLink не найден? Пожалуйста, помогите!

Ответ 1

К сожалению, 404 (или аналогичные коды) не считаются ошибками UIWebView, потому что получен ответ HTML. Хуже того, UIWebView не отображает коды ответов для нас, поэтому вам нужно сделать это вручную, используя NSURLConnection. Здесь один из способов справиться с этим:

@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 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 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 host matches the response 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

Обратите внимание, что в моей реализации я не только проверяю коды состояния, но также проверяю перенаправления (которые вы можете или не хотите делать). Я делаю это, потому что некоторые ISP-запросы перехватывают HTTP-запросы, и если целевой сайт не найден, перенаправляйте вас на свою собственную веб-страницу поиска (что, по-моему, немного странно, зная, что мой интернет-провайдер проверяет каждый веб-сайт, который я ищу). И если вы имеете дело с iPhone, которые подключаются через Wi-Fi, вам приходится иметь дело с этими капризами.

Так, например, мой код выше ищет " http://www.applecom/pages" (в котором я умышленно пропустил период ". com", что должно привести к сбою DNS-поиска), но для которого мой интернет-провайдер Verizon перехватил запрос и перенаправил HTTP-соединение на свою собственную страницу поиска, и как таковое мое приложение сообщает:

2013-01-21 23: 14: 21.896 webtest [24198: c07] - [Соединение ViewController: didReceiveResponse:] вы были перенаправлены из http://www.applecom/pages до http://search.dnsassist.verizon.net/assist.php?url=www.applecom

Возможно, вам захочется подумать о том, какие переадресации приемлемы (например, если вы перейдете на сайт "www.adobephotoshop.com" и перенаправляет вас на "www.adobe.com" ), а какие нет (например, если я перейду к "www.applecom" и перенаправит меня на "search.dnsassist.verizon.net". Возможно, я беспокоюсь о довольно узкой проблеме (которая влияет на меня из-за моего провайдера), но это что-то для размышления.