Javascript console.log () in einer iOS UIWebView

84

Beim Schreiben einer iPhone / iPad-App mit einer UIWebView ist die Konsole nicht sichtbar. Diese ausgezeichnete Antwort zeigt, wie man Fehler abfängt, aber ich möchte auch die console.log () verwenden.

TinkerTank
quelle
1
Schreiben Sie es zuerst in den Browser, aktivieren Sie die Entwicklertools und sehen Sie sich dann die Konsolenausgabe an.
Beatgammit

Antworten:

182

Nachdem er sich heute mit einem geschätzten Kollegen beraten hatte, machte er mich auf das Safari Developer Toolkit aufmerksam und darauf, wie dieses mit UIWebViews im iOS-Simulator für die Konsolenausgabe (und das Debuggen!) Verbunden werden kann.

Schritte:

  1. Öffnen Sie die Safari-Einstellungen -> Registerkarte "Erweitert" -> Aktivieren Sie das Kontrollkästchen "Entwicklungsmenü in Menüleiste anzeigen".
  2. Starten Sie die App mit UIWebView in iOS Simulator
  3. Safari -> Entwickeln -> i (Pad / Pod) Simulator -> [the name of your UIWebView file]

Sie können jetzt komplexes (in meinem Fall flot ) Javascript und andere Dinge in UIWebViews ablegen und nach Belieben debuggen.

BEARBEITEN: Wie von @Joshua J McKinnon hervorgehoben, funktioniert diese Strategie auch beim Debuggen von UIWebViews auf einem Gerät. Aktivieren Sie einfach den Web Inspector in Ihren Geräteeinstellungen: Einstellungen-> Safari-> Erweitert-> Web Inspector (Prost @Jeremy Wiebe)

UPDATE: WKWebView wird ebenfalls unterstützt

NSTJ
quelle
13
Beachten Sie, dass diese Strategie auch beim Debuggen auf echten iOS-Geräten funktioniert.
Joshua J. McKinnon
2
+100 wenn ich könnte. Das ist wunderbar, es funktioniert auch für Apps mit Telefonlücken!
Andy Novocin
2
Wenn ich mit einem iPad versuche, auf Safari zum Entwicklungsmenü zu gehen, stehen keine Geräte zur Auswahl. Wenn ich auf dem Simulator bereitstelle, funktioniert es wie ein Zauber.
Floydian
10
@Floydian Sie müssen Web Inspector auf dem Gerät aktivieren. Einstellungen-> Safari-> Erweitert-> Web Inspector.
Jeremy Wiebe
2
Meine App wird nicht in meinem Entwicklungsmenü angezeigt. Ich habe den Webinspektor aktiviert. Safari wird angezeigt, aber meine App (die aktuell ist und 2 UIWebviews anzeigt) wird nicht erkannt. Irgendwelche Ideen?
Narco
83

Ich habe eine Lösung, um mich mit Javascript bei der Apps-Debug-Konsole anzumelden. Es ist ein bisschen grob, aber es funktioniert.

Zuerst definieren wir die Funktion console.log () in Javascript, die einen Iframe mit einer ios-log: url öffnet und sofort entfernt.

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

Jetzt müssen wir diese URL im UIWebViewDelegate in der iOS-App mit der Funktion shouldStartLoadWithRequest abfangen.

- (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;
}
TinkerTank
quelle
1
siehe die einfache Idee von NSTJ unten.
Ashwin S
in Swift 4 vielleicht? : D
Konstantinos Natsios
36

Hier ist die Swift-Lösung: (Es ist ein kleiner Hack, den Kontext zu ermitteln.)

  1. Sie erstellen die UIWebView.

  2. Rufen Sie den internen Kontext ab und überschreiben Sie die Javascript-Funktion console.log () .

    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")
Leslie Godwin
quelle
3
+100! sparte mir Tonnen von Zeit, großer Hack, erfordert 0 Änderungen im JS-Code. Vielen Dank!! Nur meine 2 Cent für zukünftige Leser: Vergessen Sie nicht, das JavaScriptCoreFramework mit Ihrem Projekt und importseiner Webview-Swift-Datei zu verknüpfen .
Mindbomb
Funktioniert für mich mit Swift 4 ... Sie müssen "log" in NSString..context.objectForKeyedSubscript ("console") umwandeln. SetObject (unsafeBitCast (logFunction, to: AnyObject.self), forKeyedSubscript: "log" als NSString)
Serge
28

Ab iOS7 können Sie die native Javascript-Brücke verwenden. Etwas so Einfaches wie das Folgende

 #import <JavaScriptCore/JavaScriptCore.h>

JSContext *ctx = [webview valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
ctx[@"console"][@"log"] = ^(JSValue * msg) {
NSLog(@"JavaScript %@ log message: %@", [JSContext currentContext], msg);
    };
Ji Fang
quelle
Wo ist aus Interesse der ideale Ort, um diesen Code zu platzieren?
Leslie Godwin
OK, habe es herausgefunden. Kurz nachdem Sie das erstellt haben UIWebview, können Sie alles JSContexteinrichten.
Leslie Godwin
4
Funktioniert es JSContextnoch in iOS 8+ mit WKWebView?
Nikolai Samteladze
Ich habe es hineingesteckt - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationTypeund es funktioniert perfekt!
Artur Bartczak
@NikolaiSamteladze: Ich habe es mit WKWebViewund iOS 11.4.1 versucht und er kann nicht finden documentViewund stürzt ab. Ich habe diese Antwort gesehen und es scheint, dass dies nicht möglich ist.
Testen
10

NativeBridge ist sehr hilfreich für die Kommunikation von einer UIWebView zu Objective-C. Sie können damit Konsolenprotokolle übergeben und Objective-C-Funktionen aufrufen.

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);
};

Der Vorteil dieser Technik besteht darin, dass beispielsweise Zeilenumbrüche in Protokollnachrichten beibehalten werden.

tangent405
quelle
+1. Hinweis für Apache Cordova- Benutzer - Cordova verarbeitet bereits console.log, aber die window.onerrorFunktion in dieser Antwort ist sehr nützlich!
mpontillo
Für Appcelerator / Titanium-Entwickler: Auch dies funktioniert, um Ihre Ti.UI.WebView
Byters
0

Versuchte Leslie Godwins Lösung, bekam aber diesen Fehler:

'objectForKeyedSubscript' is unavailable: use subscripting

Für Swift 2.2 hat Folgendes für mich funktioniert:

Sie müssen JavaScriptCore importieren, damit dieser Code kompiliert werden kann:

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")
}

Wenn Sie dann in Ihrem Javascript-Code console.log ("_ your_log_") aufrufen, wird in der Xcode-Konsole gedruckt.

Besser noch, fügen Sie diesen Code als Erweiterung zu UIWebView hinzu:

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")
        }
    }
}

Rufen Sie diese Methode dann während Ihres UIWebView-Initialisierungsschritts auf:

let webView = UIWebView(frame: CGRectZero)
webView.hijackConsoleLog()
parag
quelle
0

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)
          }
      }
 }
Barath
quelle