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:
- The use of the
loadHTMLString:baseURL:
method ofUIWebView
interferes with that class’ built-in forward/back navigation features. (You have to work around this by implementing your own navigation stack.) - You can’t supply the “real” URL of the resource to
loadHTMLString:baseURL:
; if you supply the URL passed towebView: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.) - 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