I would like to detect when a page load request give to a UIWebView has returned a status code in the 5xx or 4xx range.
I\'ve setup the delegate for the web view a
Currently, UIWebView does not provide any functionality for getting HTTP status codes for the requests it loads. One workaround is to intercept the request loading process of UIWebView using the UIWebViewDelegate methods and use NSURLConnection to detect how the server responds to that request. Then you can take an appropriate action suitable for the situation. This article explains the workaround in detail on a demo project.
And you don't need to continue loading the request after you received a response. You can just cancel the connection in - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response method after learning the HTTP status code. This way you prevent the connection from loading any unnecessary response data. Then you can load the request in UIWebView again or show an appropriate error message to the user depending on the HTTP status code, etc.
Here is the article
and here is the demo project on github
Here's a work-around to get HTTP response code, but with sending just one request to each URL:-
BOOL isLoad;
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
NSLog(@"Requesting: %@ - %d - %@", request.URL.absoluteString, navigationType, request.URL.host);
if (navigationType != UIWebViewNavigationTypeOther) {
//Store last selected URL
self.loadedURL = request.URL.absoluteString;
}
if (!isLoad && [request.URL.absoluteString isEqualToString:loadedURL]) {
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError || ([response respondsToSelector:@selector(statusCode)] && [((NSHTTPURLResponse *)response) statusCode] != 200 && [((NSHTTPURLResponse *)response) statusCode] != 302)) {
//Show error message
[self showErrorMessage];
}else {
isLoad = YES;
[_wbView loadData:data MIMEType:[response MIMEType]
textEncodingName:[response textEncodingName]
baseURL:[response URL]];
}
}];
return NO;
}
isLoad = NO;
return YES;
}
Sadly at present it looks like the best available option is to use -stringByEvaluatingJavaScriptFromString: to run some sort of small script that queries the document for its status code.
What about implementing webViewDidFinishLoad:
on your UIWebViewDelegate
, and using the request
property of the UIWebView to access the headers you're interested in? Something like this (untested):
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSString* theStatus = [[webView request] valueForHTTPHeaderField:@"Status"];
}
I struggled very hard on this topic when things are on Swift 3.0 now. I even created a custom URLProtocol and tried to intercept all web requests, just to realize eventually that it was unnecessary. The reason for the confusion for me is because that they moved the didReceiveResponse function:
optional public func connection(_ connection: NSURLConnection, didReceive response: URLResponse)
to NSURLConnectionDataDelegate, which inherits from NSURLConnectionDelegate.
Anyway, Here is the Swift 3.0 version that works:
// You first need to have NSURLConnectionDataDelegate on your UIWebView
// MARK: - get HTTP status code
// setup urlconnectiondelegate
// so that the didReceiveResponse is called
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
let conn: NSURLConnection? = NSURLConnection(request: request, delegate: self)
if conn == nil {
print("cannot create connection")
}
return true;
}
// intercept the actual http status code
func connection(_ connection: NSURLConnection, didReceive response: URLResponse) {
let httpResponse: HTTPURLResponse = response as! HTTPURLResponse;
print(httpResponse.statusCode)
}
As I just posted on another thread, it is also possible to intercept any NSURLRequest
at the level of the NSURLProtocol
and create your NSURLResponse
there, instead of in your UIWebView delegate/controller. The reason why this is preferable in my opinion is that it maintains the back/forward navigation stack of the UIWebView. The outline of the approach can be found in this excellent blog post by Rob Napier:
http://robnapier.net/blog/offline-uiwebview-nsurlprotocol-588
and there's code on GitHub:
https://github.com/rnapier/RNCachingURLProtocol