English 中文(简体)
我的iPhone Objective-C代码如何能够在UIWebView中接收到Javascript错误的通知?
原标题:
  • 时间:2008-10-10 21:39:21
  •  标签:

我需要让我的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。

  1. In Safari: Enable the Develop Menu (Preferences > Advanced > Show Develop menu in menu bar)
  2. Plug your phone into the computer via the cable.
  3. List item
  4. Load up the app (either through xcode or just launch it) and go to the screen you want to debug.
  5. Back in Safari, open the Develop menu, look for the name of your device in that menu (mine is called iPhone 5), should be right under User Agent.
  6. Select it and you should see a drop down of the web views currently visible in your app.
  7. If you have more than one webview on the screen you can try to tell them apart by rolling over the name of the app in the develop menu. The corresponding UIWebView will turn blue.
  8. Select the name of the app, the develop window opens and you can inspect the console. You can even issue JS commands through it.

直接的方法:将此代码放置在使用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”来避免一些构建错误。

我创建了一个符合犹太饮食法的错误报告工具包,其中包括:

  1. The error message
  2. The name of the file the error happens in
  3. The line number the error happens on
  4. The JavaScript callstack including parameters passed

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




相关问题
热门标签