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.


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


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

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

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

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.


+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()

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)
          }
      }
 }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.