今天与一位受人尊敬的同事商量后,他向我介绍了Safari开发者工具包,以及如何将其与iOS模拟器中的UIWebViews连接起来进行控制台输出(和调试)。
步骤:
[您的UIWebView文件名称]
现在,您可以将复杂的(在我的情况下是flot)JavaScript和其他内容放入UIWebViews中,并随意进行调试。
编辑:如@Joshua J McKinnon所指出,此策略也适用于在设备上调试UIWebViews。只需在设备设置中启用Web检查器:设置->Safari->高级->Web检查器(感谢@Jeremy Wiebe)
更新:WKWebView也得到支持。
我有一个使用JavaScript记录应用程序调试控制台的解决方案。这有点粗糙,但它有效。
首先,我们在JavaScript中定义console.log()函数,该函数打开并立即删除带有ios-log:URL的iframe。
// Debug
console = new Object();
console.log = function(log) {
var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", "ios-log:#iOS#" + log);
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
};
console.debug = console.log;
console.info = console.log;
console.warn = console.log;
console.error = console.log;
现在我们需要在iOS应用程序中使用shouldStartLoadWithRequest函数在UIWebViewDelegate中捕获这个URL。
- (BOOL)webView:(UIWebView *)webView2
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType {
NSString *requestString = [[[request URL] absoluteString] stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
//NSLog(requestString);
if ([requestString hasPrefix:@"ios-log:"]) {
NSString* logString = [[requestString componentsSeparatedByString:@":#iOS#"] objectAtIndex:1];
NSLog(@"UIWebView console: %@", logString);
return NO;
}
return YES;
}
下面是 Swift 解决方案: (这是一个有点 hack 的方法来获取上下文)
创建 UIWebView。
获取内部上下文并覆盖 console.log() JavaScript 函数。
self.webView = UIWebView()
self.webView.delegate = self
let context = self.webView.valueForKeyPath("documentView.webView.mainFrame.javaScriptContext") as! JSContext
let logFunction : @convention(block) (String) -> Void =
{
(msg: String) in
NSLog("Console: %@", msg)
}
context.objectForKeyedSubscript("console").setObject(unsafeBitCast(logFunction, AnyObject.self),
forKeyedSubscript: "log")
从iOS7开始,您可以使用原生的Javascript桥接。像下面这样简单。
#import <JavaScriptCore/JavaScriptCore.h>
JSContext *ctx = [webview valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
ctx[@"console"][@"log"] = ^(JSValue * msg) {
NSLog(@"JavaScript %@ log message: %@", [JSContext currentContext], msg);
};
JSContext
仍然能够在 iOS 8+ 中与 WKWebView
一起使用吗? - Nikolai Samteladze- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
中,它完美地运行了! - Artur BartczakNativeBridge是一款非常有用的工具,可以在UIWebView和Objective-C之间进行通信。您可以使用它将控制台日志传递给Objective-C函数并调用它。
https://github.com/ochameau/NativeBridge
console = new Object();
console.log = function(log) {
NativeBridge.call("logToConsole", [log]);
};
console.debug = console.log;
console.info = console.log;
console.warn = console.log;
console.error = console.log;
window.onerror = function(error, url, line) {
console.log('ERROR: '+error+' URL:'+url+' L:'+line);
};
Swift 5
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.evaluateJavaScript("your javascript string") { (value, error) in
if let errorMessage = (error! as NSError).userInfo["WKJavaScriptExceptionMessage"] as? String {
print(errorMessage)
}
}
}
'objectForKeyedSubscript' is unavailable: use subscripting
对于Swift 2.2,以下是我测试通过的代码:
您需要导入JavaScriptCore才能编译此代码:
import JavaScriptCore
if let context = webView.valueForKeyPath("documentView.webView.mainFrame.javaScriptContext") {
context.evaluateScript("var console = { log: function(message) { _consoleLog(message) } }")
let consoleLog: @convention(block) String -> Void = { message in
print("javascript_log: " + message)
}
context.setObject(unsafeBitCast(consoleLog, AnyObject.self), forKeyedSubscript: "_consoleLog")
}
然后在你的JavaScript代码中,调用console.log("_your_log_")将会在Xcode控制台中打印。
更好的方法是将此代码作为UIWebView的扩展添加:
import JavaScriptCore
extension UIWebView {
public func hijackConsoleLog() {
if let context = valueForKeyPath("documentView.webView.mainFrame.javaScriptContext") {
context.evaluateScript("var console = { log: function(message) { _consoleLog(message) } }")
let consoleLog: @convention(block) String -> Void = { message in
print("javascript_log: " + message)
}
context.setObject(unsafeBitCast(consoleLog, AnyObject.self), forKeyedSubscript: "_consoleLog")
}
}
}
并且在你的UIWebView初始化步骤中调用此方法:
let webView = UIWebView(frame: CGRectZero)
webView.hijackConsoleLog()