Hooking UIWebView

UIWebViews are an easy way to embed a web browser into your application. You can also use them to perform (what I think is) a neat trick by assigning a UIWebView instance a delegate which implements webView:shouldStartLoadWithRequest:navigationType:.

Hooking Loads

The webView:shouldStartLoadWithRequest:navigationType: method of a UIWebView's delegate is called before each page is loaded. If this method returns YES, then loading proceeds normally. If it returns NO, then loading is aborted. You can use this behavior to ‘override’ the content associated with certain URLs.

For example, code like this:

- (BOOL)webView:(UIWebView*)aWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
{
	if ([self shouldHookURL:request.URL])
	{
		// I'd like to use a proper baseURL, but that tends to cause an infinite event cascade
		[aWebView loadHTMLString:[self pageForURL:request.URL] baseURL:nil];
		return NO;
	}
	else
	{
		return YES;
	}
}

will replace certain URLs with content generated by the delegate. The code assumes that the delegate implements two additional methods:

  • - (BOOL)shouldHookURL:(NSURL*)url
  • - (NSString*)pageForURL:(NSURL*)url

Trivial implementations of these might look like this:

- (BOOL)shouldHookURL:(NSURL*)url
{
	return YES;
}

- (NSString*)pageForURL:(NSURL*)url
{
	return @"<html><head><meta name='viewport' content='width=device-width'><title>HW</title></head><body><p style='text-align: center;'>Hello, World!</p></body></html>";
}

Caveats

There are a number of problems with this technique:

  1. The use of the loadHTMLString:baseURL: method of UIWebView interferes with that class’ built-in forward/back navigation features. (You have to work around this by implementing your own navigation stack.)
  2. You can’t supply the “real” URL of the resource to loadHTMLString:baseURL:; if you supply the URL passed to webView:shouldStartLoadWithRequest:navigationType:, you cause the program to enter (what amounts to) an infinite loop. (You can address this by using two URLs for each synthesized resource, or by passing nil, as I do in the example.)
  3. It’s sort of a nasty hack

This does work well enough that I used (a more complex application of) this technique to implement the précis views in my iKnowPeople application (now available on iTunes!). Due to these issues, however, I am moving away from hooking and towards a solution based on cocoahttpserver.

Share and Enjoy:
  • Twitter
  • Facebook
  • Digg
  • Reddit
  • HackerNews
  • del.icio.us
  • Google Bookmarks
  • Slashdot
This entry was posted in iPhone. Bookmark the permalink.

Comments are closed.