![](/img/trans.png)
[英]How can I call objective-c code from within an html file in a UIWebView?
[英]How can my iPhone Objective-C code get notified of Javascript errors in a UIWebView?
我需要让我的 iPhone Objective-C 代码在 UIWebView 中捕获 Javascript 错误。 这包括未捕获的异常、加载文件时的语法错误、未定义的变量引用等。
这是一个开发环境,所以它不需要是 SDK-kosher。 事实上,它只需要在模拟器上工作。
我已经发现使用了一些隐藏的 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=%@\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 的代码中,并准备好接受一些黑客来使其工作。
我创建了一个不错的小插件类别,您可以将其添加到您的项目中...它基于 Robert Sanders 解决方案。 荣誉。
你可以在这里下载:
这应该会让你更容易调试 UIWebView :)
我使用了 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”,因为类接口是隐藏的。 我忽略它们,效果很好。
如果您拥有 Safari v 6+(我不确定您需要什么 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”以防止一些构建错误。
我创建了一个 SDK kosher 错误报告器,其中包括:
它是sourceForge 项目提供的 QuickConnectiPhone 框架的一部分
甚至还有一个示例应用程序展示了如何向 Xcode 终端发送错误消息。
你需要做的就是用 try catch 包围你的 JavaScript 代码,包括函数定义等。 它应该是这样的。
try{
//put your code here
}
catch(err){
logError(err);
}
对于编译错误,它不能很好地工作,但可以与所有其他错误一起工作。 甚至匿名函数。
开发博客在这里,包括指向 wiki、sourceForge、google group 和 twitter 的链接。 也许这会帮助你。
我在固件 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];
}
请参阅 iOS7 中的异常处理: 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);
}
}
}];
对于某些情况,一个更简单的解决方案可能是将Firebug Lite添加到网页中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.