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).




