我的iPhone Objective-C代码如何在UIWebView中获得Javascript错误的通知?

我需要我的iPhone Objective-C代码捕获UIWebView中的Javascript错误。 这包括未捕获的exception,加载文件时的语法错误,未定义的variables引用等。

这是针对开发环境的,所以不需要是SDK-Kosher。 事实上,它只是真的需要在模拟器上工作。

我已经发现使用了一些隐藏的WebKit技巧,例如将Obj-C对象暴露给JS,并拦截警报popup窗口,但是这仍然是我无法理解的。

[注意:发布后,我find了使用debugging代理的一种方法。 有更低的开销,使用错误控制台/ Web检查器?

我现在已经find了一种在WebView中使用脚本debugging器钩子(注意,不是UIWebView)的方法。 我首先必须子类化UIWebView并添加一个像这样的方法:

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

接下来,您应该创build一个YourScriptDebugDelegate类,其中包含如下所示的方法:

 // 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]); } 

这可能会对运行时造成很大影响,因为debugging代理也可以提供用于进入和退出堆栈帧的方法,以及执行每行代码。

有关WebScriptDebugDelegate的Objective-C ++定义,请参见http://www.koders.com/noncode/fid7DE7ECEB052C3531743728D41A233A951C79E0AE.aspx

其他方法:

 // just entered a stack frame (ie 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 (ie return from a function) - (void)webView:(WebView *)webView willLeaveCallFrame:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno forWebFrame:(WebFrame *)webFrame; 

请注意,这是隐藏在一个私人的框架,所以不要试图把它提交到应用程序商店的代码,并准备一些hackery得到它的工作。

我创build了一个可以添加到项目中的漂亮的小插件类别…它基于Robert Sanders解决scheme。 荣誉。

你可以在这里下载它:

UIWebView的+debugging

这应该使它更容易debugging你的UIWebView 🙂

我使用了罗伯特·桑德斯(Robert Sanders)提出的伟大解决scheme: 我的iPhone Objective-C代码如何在UIWebView中得到Javascript错误的通知?

webkit的钩子在iPhone上也可以正常工作。 而不是标准的UIWebView我分配派生的MyUIWebView。 我还需要在MyWebScriptObjectDelegate.h中定义隐藏的类:

@class WebView;
@class WebFrame;
@class WebScriptCallFrame;

在ios sdk 4.1中的function:

 - (void)webView:(id)webView windowScriptObjectAvailable:(id)newWindowScriptObject 

弃用 ,而不是它我使用的function:

 - (void)webView:(id)sender didClearWindowObject:(id)windowObject forFrame:(WebFrame*)frame 

此外,我得到一些恼人的警告,如“NSObject可能不响应-windowScriptObject”,因为类接口是隐藏的。 我忽视他们,它很好。

直接的方式:把这个代码放在使用UIWebView的控制器/视图之上

 #ifdef DEBUG @interface DebugWebDelegate : NSObject @end @implementation DebugWebDelegate @class WebView; @class WebScriptCallFrame; @class WebFrame; - (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]); } @end @interface DebugWebView : UIWebView id windowScriptObject; id privateWebView; @end @implementation DebugWebView - (void)webView:(id)sender didClearWindowObject:(id)windowObject forFrame:(WebFrame*)frame { [sender setScriptDebugDelegate:[[DebugWebDelegate alloc] init]]; } @end #endif 

然后像这样实例化它:

 #ifdef DEBUG myWebview = [[DebugWebView alloc] initWithFrame:frame]; #else myWebview = [[UIWebView alloc] initWithFrame:frame]; #endif 

使用#ifdef DEBUG确保它不会在发布版本中使用,但是我也build议在不使用它的时候注释它,因为它会对性能产生影响。 信贷到罗伯特·桑德斯和Prcela的原始代码

另外,如果使用ARC,则可能需要添加“-fno-objc-arc”以防止出现一些构build错误。

如果您有Safari v 6+(我不确定您需要什么版本的iOS),那么在开发过程中可以使用的一种方法是使用Safari开发工具并通过它挂接到UIWebView。

  1. 在Safari中:启用“开发”菜单(首选项>高级>显示菜单栏中的显影菜单)
  2. 通过电缆将手机插入电脑。
  3. 项目清单
  4. 加载应用程序(通过xcode或只是启动它),并转到您要debugging的屏幕。
  5. 回到Safari浏览器,打开Develop菜单,在该菜单中查找设备名称(我的名字叫iPhone 5),应该在User Agent下。
  6. select它,你应该看到在你的应用程序当前可见的Web视图的下拉菜单。
  7. 如果你在屏幕上有多个webview,你可以尝试通过在开发菜单中滚动应用程序的名称来区分它们。 相应的UIWebView将变成蓝色。
  8. select应用程序的名称,开发窗口打开,你可以检查控制台。 你甚至可以通过它发出JS命令。

我创build了一个SDK犹太错误记者,其中包括:

  1. 错误消息
  2. 发生错误的文件的名称
  3. 发生错误的行号
  4. 包含传递参数的JavaScript调用堆栈

它是来自sourceForge项目的QuickConnectPhone框架的一部分

甚至有一个示例应用程序显示如何向Xcodeterminal发送错误消息。

所有你需要做的就是用try catch包围你的JavaScript代码,包括函数定义等等。 它应该看起来像这样。

 try{ //put your code here } catch(err){ logError(err); } 

编译错误并不能很好地工作,但可以与其他所有工具一起工作。 即使是匿名function。

开发博客是在这里,包括wiki,sourceForge,谷歌组和twitter的链接。 也许这会帮助你。

我已经在固件1.x中做了这个,但不是2.x. 这里是我在1.x中使用的代码,它至less应该帮助你。

 // Dismiss Javascript alerts and telephone confirms /*- (void)alertSheet:(UIAlertSheet*)sheet buttonClicked:(int)button { if (button == 1) { [sheet setContext: nil]; } [sheet dismiss]; }*/ // Javascript errors and logs - (void) webView: (WebView*)webView addMessageToConsole: (NSDictionary*)dictionary { NSLog(@"Javascript log: %@", dictionary); } // Javascript alerts - (void) webView: (WebView*)webView runJavaScriptAlertPanelWithMessage: (NSString*) message initiatedByFrame: (WebFrame*) frame { NSLog(@"Javascript Alert: %@", message); UIAlertSheet *alertSheet = [[UIAlertSheet alloc] init]; [alertSheet setTitle: @"Javascript Alert"]; [alertSheet addButtonWithTitle: @"OK"]; [alertSheet setBodyText:message]; [alertSheet setDelegate: self]; [alertSheet setContext: self]; [alertSheet popupAlertAnimated:YES]; } 

请参阅iOS7中的exception处理: http : //www.bignerdranch.com/blog/javascriptcore-example/

 [context setExceptionHandler:^(JSContext *context, JSValue *value) { NSLog(@"%@", value); }]; 

首先设置WebViewJavascriptBridge ,然后重写console.error函数。

在JavaScript中

  window.originConsoleError = console.error; console.error = (msg) => { window.originConsoleError(msg); bridge.callHandler("sendConsoleLogToNative", { action:action, message:message }, null) }; 

在Objective-C中

 [self.bridge registerHandler:@"sendConsoleLogToNative" handler:^(id data, WVJBResponseCallback responseCallback) { NSString *action = data[@"action"]; NSString *msg = data[@"message"]; if (isStringValid(action)){ if ([@"console.error" isEqualToString:action]){ NSLog(@"JS error :%@",msg); } } }]; 

对于某些情况,更简单的解决scheme可能是将Firebug Lite添加到网页中。