Javascript console.log() in an iOS UIWebView
Solution 1
I have a solution to log, using javascript, to the apps debug console. It's a bit crude, but it works.
First, we define the console.log() function in javascript, which opens and immediately removes an iframe with a ios-log: url.
// 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;
Now we have to catch this URL in the UIWebViewDelegate in the iOS app using the shouldStartLoadWithRequest function.
- (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;
}
Solution 2
After consulting with an esteemed colleague today he alerted me to the Safari Developer Toolkit, and how this can be connected to UIWebViews in the iOS Simulator for console output (and debugging!).
Steps:
- Open Safari Preferences -> "Advanced" tab -> enable checkbox "Show Develop menu in menu bar"
- Start app with UIWebView in iOS Simulator
- Safari -> Develop -> i(Pad/Pod) Simulator ->
[the name of your UIWebView file]
You can now drop complex (in my case, flot) Javascript and other stuff into UIWebViews and debug at will.
EDIT: As pointed out by @Joshua J McKinnon this strategy also works when debugging UIWebViews on a device. Simply enable Web Inspector on your device settings: Settings->Safari->Advanced->Web Inspector (cheers @Jeremy Wiebe)
UPDATE: WKWebView is supported too
Solution 3
Here's the Swift solution: (It's a bit of a hack to get the context)
You create the UIWebView.
Get the internal context and override the console.log() javascript function.
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")
Solution 4
Starting from iOS7, you can use native Javascript bridge. Something as simple as following
#import <JavaScriptCore/JavaScriptCore.h>
JSContext *ctx = [webview valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
ctx[@"console"][@"log"] = ^(JSValue * msg) {
NSLog(@"JavaScript %@ log message: %@", [JSContext currentContext], msg);
};
Solution 5
NativeBridge is very helpful for communicating from a UIWebView to Objective-C. You can use it to pass console logs and call Objective-C functions.
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);
};
The advantage of this technique is that things like newlines in log messages are preserved.
TinkerTank
Updated on November 12, 2020Comments
-
TinkerTank over 3 years
When writing a iPhone / iPad app with a UIWebView, the console isn't visible. this excellent answer shows how to trap errors, but I would like to use the console.log() as well.
-
mpontillo almost 11 years+1. Note to Apache Cordova users - Cordova already handles
console.log
, but thewindow.onerror
function in this answer is very useful! -
Joshua J. McKinnon over 10 yearsNote, this strategy also works when debugging on real iOS devices.
-
Andy Novocin over 10 years+100 if I could. This is wonderful, it works for phone gap apps too!
-
Roderic Campbell over 10 yearsThis should be made the answer. This is wonderful and I agree with the above, this should get all the up votes
-
Floydian over 10 yearsCan anyone tell me please how can it be done with iOS devices?
-
NSTJ over 10 years@Floydian what seems to be the problem?
-
Floydian about 10 yearsTrying with an iPad, when I go to the develop menu on Safari, there are no Devices to choose. When I deploy on the simulator, it works like a charm.
-
Jeremy Wiebe about 10 years@Floydian you have to enable Web Inspector on the device. Settings->Safari->Advanced->Web Inspector.
-
Ashwin S about 10 yearssee the simple idea of NSTJ below.
-
juanpaco over 9 yearsWhenever I set a breakpoint in a JS file this way, the debugger breaks there, but then the step buttons don't work. They don't respond to clicks. has anyone else seen this?
-
Leslie Godwin over 8 yearsOut of interest, where is the ideal place to put this code?
-
Leslie Godwin over 8 yearsOK, figured it out. Just after you created the
UIWebview
you can setup anyJSContext
stuff. -
mindbomb over 8 years+100! saved me TONS of time, great hack, requires 0 changes in JS code. Thanks!! Just my 2 cents for future readers: don't forget to link
JavaScriptCore
framework to your project andimport
it in your webview swift file. -
narco over 8 yearsmy app is not showing up in my develop menu. I have enabled web inspector. Safari shows up, but my app (which is current;y displaying 2 UIWebviews) is not detected.. any ideas?
-
Nikolai Samteladze over 8 yearsDoes
JSContext
still work in iOS 8+ withWKWebView
? -
ar34z over 8 yearsDon't miss out on @Floydian comment: you need to enable Web Inspector on the device as well.
-
Artur Bartczak about 8 yearsI put it into
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
and it works perfectly! -
Byters over 6 yearsFor Appcelerator/Titanium developers: this too works to debug your Ti.UI.WebView
-
Konstantinos Natsios about 6 yearsin swift 4 maybe? :D
-
Serge over 5 yearsWorks for me with Swift 4... you haveto cast "log" to NSString..context.objectForKeyedSubscript("console").setObject(unsafeBitCast(logFunction, to: AnyObject.self), forKeyedSubscript: "log" as NSString)
-
Curtis over 5 yearsAny way to get this to work in the simulator? I don't have i(Pad/Pod) Simulator in my Develop menu in Safari.
-
NSTJ over 5 years@Curtis it should definitely work in the Simulator+Safari - are you still having the issue?
-
Curtis over 5 yearsYes I have Xcode 10.1, and when I run my app in the simulator and go to Safari and click Develop, there is no Simulator option. If I run on my iPhone, it does show that iPhone in the Develop menu.
-
testing over 5 years@NikolaiSamteladze: I tried with
WKWebView
and iOS 11.4.1 and he can't finddocumentView
and crashes. I saw this answer and it seems that, it isn't possible this way. -
maxi-code about 5 years@Curtis I had that issue too. It's working for me now. It's strange but I have to open a new instance of safari and also close the simulator and run the app again