46

我需要让我的 iPhone Objective-C 代码在 UIWebView 中捕获 Javascript 错误。这包括未捕获的异常、加载文件时的语法错误、未定义的变量引用等。

这是一个开发环境,所以它不需要是 SDK-kosher。事实上,它只需要在模拟器上工作。

我已经发现使用了一些隐藏的 WebKit 技巧来例如将 Obj-C 对象暴露给 JS 并拦截警报弹出窗口,但是这个仍然让我望而却步。

[注意:发布此内容后,我确实找到了一种使用调试委托的方法。有没有一种方法可以降低开销,使用错误控制台/网络检查器?]

4

10 回答 10

28

我现在找到了一种在 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=%@\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]);
}

这可能会对运行时产生很大影响,因为调试委托还可以提供用于进入和退出堆栈帧以及执行每一行代码的方法。

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

那些其他方法:

// 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;

请注意,这一切都隐藏在一个私有框架中,所以不要试图将它放在你提交给 App Store 的代码中,并准备好一些黑客来让它工作。

于 2008-10-10T22:57:58.653 回答
13

我创建了一个不错的小插件类别,您可以将其添加到您的项目中......它基于 Robert Sanders 解决方案。荣誉。

你可以在这里下载:

UIWebView+调试

这应该会让你更容易调试 UIWebView :)

于 2012-07-16T00:10:27.800 回答
11

我使用了 Robert Sanders 提出的出色解决方案:我的 iPhone Objective-C 代码如何在 UIWebView 中收到有关 Javascript 错误的通知?

webkit 的那个钩子在iPhone上也可以正常工作。我分配了派生的 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”,因为类接口是隐藏的。我忽略了它们,效果很好。

于 2010-11-05T17:02:54.640 回答
9

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

  1. 在 Safari 中:启用开发菜单(首选项 > 高级 > 在菜单栏中显示开发菜单)
  2. 通过数据线将手机插入计算机。
  3. 项目清单
  4. 加载应用程序(通过 xcode 或直接启动它)并转到您要调试的屏幕。
  5. 回到 Safari,打开 Develop 菜单,在该菜单中查找您的设备名称(我的称为 iPhone 5),应该在 User Agent 下方。
  6. 选择它,您应该会看到当前在您的应用程序中可见的 Web 视图的下拉列表。
  7. 如果屏幕上有多个 webview,您可以尝试通过在开发菜单中滚动应用程序的名称来区分它们。对应的 UIWebView 会变成蓝色。
  8. 选择应用程序的名称,开发窗口打开,您可以检查控制台。你甚至可以通过它发出 JS 命令。
于 2013-03-18T18:53:41.977 回答
8

直截了当的方式:将此代码放在使用 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”以防止一些构建错误。

于 2011-10-08T01:35:43.297 回答
4

我创建了一个 SDK 犹太洁食错误报告器,其中包括:

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

它是sourceForge 项目提供的 QuickConnectiPhone 框架的一部分

甚至还有一个示例应用程序展示了如何向 Xcode 终端发送错误消息。

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

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

它不适用于编译错误,但适用于所有其他错误。甚至是匿名函数。

开发博客在 这里,包括指向 wiki、sourceForge、google 组和 twitter 的链接。也许这会帮助你。

于 2009-04-21T20:58:36.453 回答
0

我在固件 1.x 中做到了这一点,但在 2.x 中没有。这是我在 1.x 中使用的代码,它至少应该对您有所帮助。

// 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];
}
于 2008-10-10T22:14:06.280 回答
0

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

[context setExceptionHandler:^(JSContext *context, JSValue *value) {
    NSLog(@"%@", value);
}];
于 2015-02-22T22:21:19.513 回答
0

首先设置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);
        }
    }
}];
于 2017-04-13T03:37:45.413 回答
-3

在某些情况下,更简单的解决方案可能是将Firebug Lite添加到网页中。

于 2012-11-16T09:34:53.593 回答