Custom HTTP headers for every request made in UIWebViews
NSMutableURLRequest are handy classes for making HTTP requests. They allow you to add custom HTTP headers before sending a request. And it works fine.
However in a project, I have a webview displaying a webpage hosted on a server (I know it sucks but that’s not the point). The content of the webpage had to be slightly different depending on the version of the iPhone app displaying the page.
Until today the version of the page was always the same, whatever the application version. But today, to differentiate older version of the app with newer, we decided to add a custom HTTP header to the initial webview request, something like “app-version:1.1″.
However it turned out that every single link tapped in the webpage is handled by the UIWebView itself and obviously the UIWebView is not adding the new HTTP header furthermore. And we needed the HTTP header to be present in every request made by the webview to the webserver.
So the solution I brought to the problem was to override the default UIWebView. My new NPWebView class inherits UIWebView AND is delegate of the UIWebView. So that it implements the webView:shouldStartLoadWithRequest:navigationType: delegate selector.
First version of the NPWebView class asserted that the passed NSURLRequest was a NSMutableURLRequest and then added a HTTP header to it. And it worked fine!
Until I moved the project to OS4.0, where the trick didn’t work anymore. It seems Apple has modified their implementation of the UIWebview, making any change to the NSURLRequest within the webView:shouldStartLoadWithRequest:navigationType: selector inoperant. So I implemented a new workaround, that works pretty well and should now work for whatever version of the OS exists or will exist in the future.
The new solution is less efficient, I admit it. If you have a better solution, don’t hesitate to drop a comment!
In the webView:shouldStartLoadWithRequest:navigationType: selector, we’re looking in the given request for the HTTP header we want to add, see if it’s there.
- If not, then we copy the request, discard the given one (return NO), add the custom HTTP headers to the copied request and then ask self to load that request.
If present, then we let load the request (return YES).
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)aRequest navigationType:(UIWebViewNavigationType)navigationType {
NSDictionary *headers = [aRequest allHTTPHeaderFields];
BOOL hasWhateverAddedHeader = NO;
for(NSString *key in [headers allKeys]) {
if([[key lowercaseString] isEqualToString:@"my-added-header"]) {
hasWhateverAddedHeader = YES;
break;
}
}
if(!hasWhateverAddedHeader) {
NSMutableURLRequest *newRequest = [aRequest mutableCopy];
[newRequest addValue:@"whatever" forHTTPHeaderField:@"My-Added-Header"];
[self addAppVersionHTTPHeader:newRequest];
[self loadRequest:newRequest];
[newRequest release];
return NO;
}
return YES;
}
But one can argue that my NPWebView class is already the delegate of the UIWebView, thus not allowing any other class to be delegate of the original webview. The answer I brought was to reimplement the setDelegate: selector which will retain another delegate while keeping the true delegate as being the NPWebView class.
The NPWebView class I wrote has more features, like the ability to set a NSDictionary for custom headers and solves the delegate issue.
Hit the link to download a sample project with the NPWebView class (work under Apache License version 2.0).





This looked like it would be very helpful, however I’m running in to a problem.
It seems that any dependencies (css, js, images) that are included in my target htmll page do not trigger the delegate callbacks.
Is there a particular property I need to change on the webview, or is there something else I’m missing?
Thanks!
Hi Justin,
I’m not really sure what you’re trying to do or what you expect your dependencies to trigger, but as long as you use the NPWebView class as a replacement to the standard UIWebView class, and you set your delegate properly, you should get the UIWebViewDelegate callbacks to be fired like they would with a standard UIWebView.
Maybe you can give a little more details on your problem?
Hi Romain,
Thanks a lot for the response. What I’m trying to do is add a Basic authentication header to each request. This works for the first request (the html page I load directly), but not for the following dependency requests that get triggered (.css, .js, .jpg files).
I think it’s a hard problem and your code is not set up to handle this case. But thanks regardless, I’m sure your example will help some other people.
Justin,
I don’t know much about HTTP Authentication, I mostly know that it happens within the HTTP headers.
As far as I know you get an HTTP 401 when you’re trying to reach anonymously a protected resource and it’s likely you fall back on the webView:didFailLoadWithError: selector in this case, instead of webView:shouldStartLoadWithRequest:navigationType: . You might get some chance to achieve what you want to do by tweaking the NPWebView around the delegate methods.
Have a good luck!
for(NSString *key in [headers allKeys])
should be written as
for(NSString *key in headers)
You’re right. I’d correct you: it could be written, not should be
You could also achieve this by adding a category for the NSMutableURLRequest and then apply method swizzling for the newSetValue method.
For a detailed explanation see: http://www.icab.de/blog/2010/04/07/changing-the-headers-for-uiwebkit-http-requests/