2010-01-28

IPhone: Open a link into Safari from within web view

Here is the situation:
You have an application in which you use web view to show some content. The client decides that he wants marketing materials, so they provide you with HTML containing links. How do you prevent the web view of loading this links into your app, and force safari to open ?


The answer is - add this method to your web view delegate:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request


 navigationType:(UIWebViewNavigationType)navigationType {
    // if the new content is due to click on a link
    if (navigationType == UIWebViewNavigationTypeLinkClicked) {
        // standard way to launch external app, in this case safari
        [[UIApplication sharedApplication] openURL:request.URL];
        
        // tell the web view that we have handle it, so it should stop loading
        return false;
    }
    
    // otherwise, tell the web view to load the content
    return true;
}


IPhone: Error logging




So far I didn't have a real need of good error logging, as most of the apps were isolated, and not to much API that results in a NSError was used. However lately I was working on a library which uses CoreData. The data model is completely dynamic, and even a small misconfiguration of the library might result in some NSError later in the code.
In this case logging the error object itself is not very informative:

NSLog(@"error description: %@", err);
The log will contain the err object but it would be in the form:

error description: Error Domain=ERROR DOMAIN Code=1000 UserInfo=0x1032e70
Although we have the domain and the error code the real info is in the UserInfo dictionary. Core data uses a lot of API that may result in a NSError and it is this dictionary which stores the human readable info. So a proper logging should include the domain, code and the user info:

NSLog(@"error description: %@ [%d]; %@", [e domain], [e code], [e userInfo]);
Resulting in:

error description: ERROR DOMAIN [1000]; { theKey = "Real problem description is hidden here :)"; }
What you should now take care for, is this common BUG template:

NSError *err;
[object someMethodThatMightResultInError:&err];
if (err) {
    ... some error handling ...
}
Usually the method does not guarantee that if there is no error it will initialize the err variable to nil, so you will finish executing the error handling even when there is no error (most probably EXC_BAD_ACCESS since the err message contains garbage)
The solution is of-course very simple:

NSError *err = nil;
...