我需要让我的iPhone Objective-C代码在UIWebView中捕获Javascript错误。这包括未捕获的异常、加载文件时的语法错误、未定义的变量引用等。
这是一个开发环境,所以它不需要符合SDK的要求。实际上,它只需要在模拟器上运行就可以了。
我已经使用了一些隐藏的 WebKit 技巧,例如将 Obj-C 对象暴露给 JS 和拦截警报弹窗,但是这个我仍然无法解决。
[注:发布此后,我找到了使用调试委托的一种方法。 是否有一种使用错误控制台/网络检查器开销较低的方法?]
我需要让我的iPhone Objective-C代码在UIWebView中捕获Javascript错误。这包括未捕获的异常、加载文件时的语法错误、未定义的变量引用等。
这是一个开发环境,所以它不需要符合SDK的要求。实际上,它只需要在模拟器上运行就可以了。
我已经使用了一些隐藏的 WebKit 技巧,例如将 Obj-C 对象暴露给 JS 和拦截警报弹窗,但是这个我仍然无法解决。
[注:发布此后,我找到了使用调试委托的一种方法。 是否有一种使用错误控制台/网络检查器开销较低的方法?]
我现在已经找到了一种使用WebView中的脚本调试器挂接的方法(注意,不是UIWebView)。我首先必须子类化UIWebView并添加一个如下所示的方法:
- (void)webView:(id)webView windowScriptObjectAvailable:(id)newWindowScriptObject {
// save these goodies
windowScriptObject = newWindowScriptObject;
privateWebView = webView;
if (scriptDebuggingEnabled) {
[webView setScriptDebugDelegate:[[YourScriptDebugDelegate alloc] init]];
}
}
接下来,您应该创建一个名为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=%@
source=%@", 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]);
}
这可能会对运行时间产生很大的影响,因为调试委托还可以提供要调用的方法以进入和退出堆栈帧,并执行每行代码。
请查看http://www.koders.com/noncode/fid7DE7ECEB052C3531743728D41A233A951C79E0AE.aspx了解WebScriptDebugDelegate的Objective-C++定义。
那些其他方法:
// 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;
请注意,所有这些都隐藏在私有框架中,因此不要尝试将其放入您提交给应用商店的代码中,准备好进行一些黑客操作才能使它正常工作。
I created a nice little drop-in category that you can add to your project... It is based on Robert Sanders solution. Kudos.
你可以在这里下载它:
UIWebView+Debug: UIWebView+调试
这应该让调试您的UIWebView变得更加容易 :)
我采用了Robert Sanders提出的优秀解决方案:如何让我的iPhone Objective-C代码在UIWebView中接收JavaScript错误通知?
那个钩子对于 iPhone 上的 Webkit 也能正常工作。我分配了派生的 MyUIWebView,而不是标准的 UIWebView。我还需要在 MyWebScriptObjectDelegate.h 中定义隐藏的类:
@class WebView;
@class WebFrame;
@class WebScriptCallFrame;
在iOS SDK 4.1中,该功能为:
- (void)webView:(id)webView windowScriptObjectAvailable:(id)newWindowScriptObject
已经不被推荐使用,我使用了函数:
- (void)webView:(id)sender didClearWindowObject:(id)windowObject forFrame:(WebFrame*)frame
此外,我遇到一些烦人的警告,如“NSObject 可能无法响应 -windowScriptObject”,因为类接口已隐藏。我忽略它们,结果很好。
如果您拥有 Safari v6+(我不确定需要哪个iOS版本),在开发过程中可行的一种方法是使用Safari开发工具并通过它连接到UIWebView。
直接的方法:将此代码放置在使用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 可以确保其不会包含在发布版本中,但我还建议在不使用时将其注释掉,因为它会对性能产生影响。原始代码由 Robert Sanders 和 Prcela 提供。
如果使用ARC,您可能需要添加“-fno-objc-arc”来避免一些构建错误。
我创建了一个符合犹太饮食法的错误报告工具包,其中包括:
这是sourceForge项目中可用的QuickConnectiPhone框架的一部分。
甚至有一个示例应用程序,说明如何将错误消息发送到Xcode终端。
你所需要做的就是将你的 JavaScript 代码包括函数定义等用 try catch 包裹起来。它应该如下所示。
try{
//put your code here
}
catch(err){
logError(err);
}
It doesn t work really well with compilation errors but works with all others. Even anonymous functions.
The development blog is here is here and includes links to the wiki, sourceForge, the google group, and twitter. Maybe this would help you out.
I have done this in firmware 1.x but not 2.x. Here is the code I used in 1.x, it should at least help you on your way.
// 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];
}
See exception handling in iOS7: http://www.bignerdranch.com/blog/javascriptcore-example/
[context setExceptionHandler:^(JSContext *context, JSValue *value) {
NSLog(@"%@", value);
}];
First setup WebViewJavascriptBridge , then override console.error function.
在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);
}
}
}];