Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can my iPhone Objective-C code get notified of Javascript errors in a UIWebView?

I need to have my iPhone Objective-C code catch Javascript errors in a UIWebView. That includes uncaught exceptions, syntax errors when loading files, undefined variable references, etc.

This is for a development environment, so it doesn't need to be SDK-kosher. In fact, it only really needs to work on the simulator.

I've already found used some of the hidden WebKit tricks to e.g. expose Obj-C objects to JS and to intercept alert popups, but this one is still eluding me.

[NOTE: after posting this I did find one way using a debugging delegate. Is there a way with lower overhead, using the error console / web inspector?]

like image 510
Robert Sanders Avatar asked Oct 10 '08 21:10

Robert Sanders


2 Answers

I have now found one way using the script debugger hooks in WebView (note, NOT UIWebView). I first had to subclass UIWebView and add a method like this:

- (void)webView:(id)webView windowScriptObjectAvailable:(id)newWindowScriptObject {     // save these goodies     windowScriptObject = newWindowScriptObject;     privateWebView = webView;      if (scriptDebuggingEnabled) {         [webView setScriptDebugDelegate:[[YourScriptDebugDelegate alloc] init]];     } } 

Next you should create a YourScriptDebugDelegate class that contains methods like these:

// in YourScriptDebugDelegate  - (void)webView:(WebView *)webView       didParseSource:(NSString *)source  baseLineNumber:(unsigned)lineNumber         fromURL:(NSURL *)url        sourceId:(int)sid     forWebFrame:(WebFrame *)webFrame {     NSLog(@"NSDD: called didParseSource: sid=%d, url=%@", sid, url); }  // some source failed to parse - (void)webView:(WebView *)webView  failedToParseSource:(NSString *)source  baseLineNumber:(unsigned)lineNumber         fromURL:(NSURL *)url       withError:(NSError *)error     forWebFrame:(WebFrame *)webFrame {     NSLog(@"NSDD: called failedToParseSource: url=%@ line=%d error=%@\nsource=%@", url, lineNumber, error, source); }  - (void)webView:(WebView *)webView   exceptionWasRaised:(WebScriptCallFrame *)frame        sourceId:(int)sid            line:(int)lineno     forWebFrame:(WebFrame *)webFrame {     NSLog(@"NSDD: exception: sid=%d line=%d function=%@, caller=%@, exception=%@",            sid, lineno, [frame functionName], [frame caller], [frame exception]); } 

There is probably a large runtime impact for this, as the debug delegate can also supply methods to be called for entering and exiting a stack frame, and for executing each line of code.

See http://www.koders.com/noncode/fid7DE7ECEB052C3531743728D41A233A951C79E0AE.aspx for the Objective-C++ definition of WebScriptDebugDelegate.

Those other methods:

// just entered a stack frame (i.e. called a function, or started global scope) - (void)webView:(WebView *)webView    didEnterCallFrame:(WebScriptCallFrame *)frame       sourceId:(int)sid           line:(int)lineno    forWebFrame:(WebFrame *)webFrame;  // about to execute some code - (void)webView:(WebView *)webView willExecuteStatement:(WebScriptCallFrame *)frame       sourceId:(int)sid           line:(int)lineno    forWebFrame:(WebFrame *)webFrame;  // about to leave a stack frame (i.e. return from a function) - (void)webView:(WebView *)webView   willLeaveCallFrame:(WebScriptCallFrame *)frame       sourceId:(int)sid           line:(int)lineno    forWebFrame:(WebFrame *)webFrame; 

Note that this is all hidden away in a private framework, so don't try to put this in code you submit to the App Store, and be prepared for some hackery to get it to work.

like image 98
Francisco Noriega Avatar answered Oct 25 '22 05:10

Francisco Noriega


I created a nice little drop-in category that you can add to your project... It is based on Robert Sanders solution. Kudos.

You can dowload it here:

UIWebView+Debug

This should make it a lot easier to debug you UIWebView :)

like image 40
Pablo Avatar answered Oct 25 '22 05:10

Pablo