Javascript console.log () in un iOS UIWebView


84

Quando si scrive un'app per iPhone / iPad con UIWebView, la console non è visibile. questa eccellente risposta mostra come intercettare gli errori, ma vorrei usare anche console.log ().


1
Scrivilo prima nel browser, attiva gli Strumenti per sviluppatori, quindi guarda l'output della console.
beatgammit

Risposte:


183

Dopo essersi consultato con uno stimato collega oggi, mi ha avvisato del Safari Developer Toolkit e di come questo può essere collegato a UIWebViews nel simulatore iOS per l'output della console (e il debug!).

Passaggi:

  1. Apri Preferenze Safari -> scheda "Avanzate" -> attiva la casella di controllo "Mostra menu Sviluppo nella barra dei menu"
  2. Avvia l'app con UIWebView in iOS Simulator
  3. Safari -> Sviluppo -> i (Pad / Pod) Simulator -> [the name of your UIWebView file]

Ora puoi rilasciare Javascript complessi (nel mio caso, flot ) e altre cose in UIWebViews ed eseguire il debug a piacimento.

EDIT: come sottolineato da @Joshua J McKinnon, questa strategia funziona anche durante il debug di UIWebViews su un dispositivo. Abilita semplicemente Web Inspector nelle impostazioni del tuo dispositivo: Impostazioni-> Safari-> Avanzate-> Web Inspector (cheers @Jeremy Wiebe)

AGGIORNAMENTO: anche WKWebView è supportato


13
Nota, questa strategia funziona anche durante il debug su dispositivi iOS reali.
Joshua J. McKinnon

2
+100 se potessi. È meraviglioso, funziona anche per le app per smartphone!
Andy Novocin

2
Provando con un iPad, quando vado al menu di sviluppo su Safari, non ci sono dispositivi da scegliere. Quando eseguo il deployment sul simulatore, funziona come un incantesimo.
Floydian

10
@Floydian devi abilitare Web Inspector sul dispositivo. Impostazioni-> Safari-> Avanzate-> Web Inspector.
Jeremy Wiebe

2
la mia app non viene visualizzata nel menu di sviluppo. Ho abilitato il web inspector. Safari si presenta, ma la mia app (che è attuale; y mostra 2 UIWebviews) non viene rilevata .. qualche idea?
narco

82

Ho una soluzione per accedere, utilizzando javascript, alla console di debug delle app. È un po 'rozzo, ma funziona.

Innanzitutto, definiamo la funzione console.log () in javascript, che apre e rimuove immediatamente un iframe con un 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;

Ora dobbiamo catturare questo URL in UIWebViewDelegate nell'app iOS usando la funzione shouldStartLoadWithRequest.

- (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
vedere la semplice idea di NSTJ di seguito.
Ashwin S

in swift 4 forse? : D
Konstantinos Natsios

35

Ecco la soluzione Swift: (È un po 'un trucco per ottenere il contesto)

  1. Crei il UIWebView.

  2. Ottieni il contesto interno e sovrascrivi la funzione javascript 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! mi ha fatto risparmiare TONNELLATE di tempo, ottimo hack, richiede 0 modifiche nel codice JS. Grazie!! Solo i miei 2 centesimi per i futuri lettori: non dimenticare di collegare il JavaScriptCoreframework al tuo progetto e di importinserirlo nel tuo file swift webview.
mindbomb

Per me funziona con Swift 4 ... devi trasmettere "log" a NSString..context.objectForKeyedSubscript ("console"). SetObject (unsafeBitCast (logFunction, to: AnyObject.self), forKeyedSubscript: "log" come NSString)
Serge

28

A partire da iOS7, puoi utilizzare il bridge Javascript nativo. Qualcosa di semplice come seguire

 #import <JavaScriptCore/JavaScriptCore.h>

JSContext *ctx = [webview valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
ctx[@"console"][@"log"] = ^(JSValue * msg) {
NSLog(@"JavaScript %@ log message: %@", [JSContext currentContext], msg);
    };

Per interesse, dov'è il posto ideale per mettere questo codice?
Leslie Godwin

OK, capito. Subito dopo aver creato il UIWebview, puoi configurare qualsiasi JSContextcosa.
Leslie Godwin

4
Funziona JSContextancora in iOS 8+ con WKWebView?
Nikolai Samteladze

L'ho inserito - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationTypee funziona perfettamente!
Artur Bartczak

@NikolaiSamteladze: ho provato con WKWebViewe iOS 11.4.1 e non riesce a trovare documentViewe si blocca. Ho visto questa risposta e sembra che non sia possibile in questo modo.
test il

10

NativeBridge è molto utile per comunicare da un UIWebView a Objective-C. Puoi usarlo per passare i registri della console e chiamare le funzioni 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);
};

Il vantaggio di questa tecnica è che cose come le nuove righe nei messaggi di log vengono conservate.


+1. Nota per gli utenti di Apache Cordova : Cordova lo gestisce già console.log, ma la window.onerrorfunzione in questa risposta è molto utile!
mpontillo

Per sviluppatori Appcelerator / Titanium: anche questo funziona per eseguire il debug del tuo Ti.UI.WebView
Byters

0

Ho provato la correzione di Leslie Godwin ma ho ricevuto questo errore:

'objectForKeyedSubscript' is unavailable: use subscripting

Per Swift 2.2, ecco cosa ha funzionato per me:

Dovrai importare JavaScriptCore per compilare questo codice:

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

Quindi nel tuo codice javascript, la chiamata a console.log ("_ your_log_") verrà stampata nella console Xcode.

Meglio ancora, aggiungi questo codice come estensione a 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")
        }
    }
}

E quindi chiama questo metodo durante il passaggio di inizializzazione di UIWebView:

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)
          }
      }
 }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.