Code, Code, Revolution!
In each new version of iOS the browser gets a new user-agent string. I’ve been searching for a way to get the user-agent string from the API but haven’t been able to find a straight forward way to do it. Here’s my solution:
BSWebViewUserAgent.h
#import <Foundation/Foundation.h>
@interface BSWebViewUserAgent : NSObject <UIWebViewDelegate> {
NSString *userAgent;
UIWebView *webView;
}
@property (nonatomic, retain) NSString *userAgent;
-(NSString*)userAgentString;
@endBSWebViewUserAgent.m
#import "BSWebViewUserAgent.h"
@implementation BSWebViewUserAgent
@synthesize userAgent;
-(NSString*)userAgentString
{
webView = [[UIWebView alloc] init];
webView.delegate = self;
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"www.google.com"]]];
// Wait for the web view to load our bogus request and give us the secret user agent.
while (self.userAgent == nil)
{
// This executes another run loop.
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
return self.userAgent;
}
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
self.userAgent = [request valueForHTTPHeaderField:@"User-Agent"];
// Return no, we don't care about executing an actual request.
return NO;
}
- (void)dealloc
{
[webView release];
[userAgent release];
[super dealloc];
}
@endI use a UIWebView, listen to its delegates and load a bogus URL. When shouldStartLoadWithRequest is called the supplied request contains the user-agent string in the header. Because UIWebView loadRequest is asynchronous and we’re not really loading a request because we return NO in shouldStartLoadWithRequest there’s a loop waiting for the user-agent to be set. To wait for an asynchronous task to complete NSRunLoop is used. Each call to NSRunLoop runMode makes the application execute another loop, in this case executing the loadRequest call for our UIWebView.
Using the class is simple:
BSWebViewUserAgent *agent = [[BSWebViewUserAgent alloc] init]; NSLog(@"User-agent: %@", [agent userAgentString]); [agent release];
This executes very fast, but if you have found an easier/faster way of extracting the user-agent, please let me know.
With this blog I try to provide useful tips and solutions for programming .NET, Objective-C and more. My name is Björn Sållarp, and I love writing code.
It's now available on AppStore. It's free and open source. Read more about the app here: Swedish / English
iPhone/iPad – Wait for asynchronous tasks to complete | blog.sallarp.com
July 27th, 2010 at 11:36 am
[...] a rare case but sometimes you need to do this. I used this techinque in my previous post on how to extract the user-agent string from a UIWebView. Check it out for a more complete [...]
Greg Combs
September 25th, 2010 at 8:43 pm
I just drop this in my webview delegate, in webViewDidFinishLoad:
NSString *userAgent = [self.m_webView.request valueForHTTPHeaderField:@"User-Agent"];
Greg Combs
September 25th, 2010 at 8:47 pm
I should add that I store that string in the delegate once per startup, then I send it to my analytics tracker, (in my case Localytics) as a tagged event … that way I can update my served web pages back home.
UIWebView user-agent weirdness and how to change user-agent value programmatically | blog.sallarp.com
December 5th, 2010 at 10:48 pm
[...] change between requests the authenticated session will terminate. That’s why I wrote about how to extract the user-agent string from UIWebView earlier. In “Mitt Saldo” i make authentication requests using ASIHttpRequest and for [...]
Trott
November 16th, 2011 at 7:53 pm
This is great; thanks for sharing it!
I tried this and it hung for me when using URLWithString:@”www.google.com”. I changed it to a complete URL (URLWithString:@”http://www.example.com/” and all was well.
@Greg Combs: Your one-liner will work great if you don’t mind getting the user agent after the fact. Björn’s technique is useful though if you want to retrieve the user agent ahead of time so you can, for example, tack on your own app’s identifier (which, I believe, is valid and correct behavior).
Björn Sållarp
November 16th, 2011 at 8:03 pm
@Trott
In most cases you should definitely set your own user agent. However, sometimes you want to impersonate the real Safari browser user agent and avoid hard-coding the user agent into the app. Though, I have noticed that since writing this blog post the UIWebView will not always return the user agent of Safari. In some iOS versions they have differed slightly.
// Björn