Console.log () de JavaScript en una UIWebView de iOS

84

Al escribir una aplicación para iPhone / iPad con UIWebView, la consola no está visible. esta excelente respuesta muestra cómo atrapar errores, pero también me gustaría usar console.log ().

TinkerTank
fuente
1
Primero escríbalo en el navegador, encienda las herramientas de desarrollo y luego mire la salida de la consola.
Beatgammit

Respuestas:

183

Después de consultar con un estimado colega hoy, me alertó sobre Safari Developer Toolkit, y cómo esto se puede conectar a UIWebViews en el simulador de iOS para la salida de la consola (¡y la depuración!).

Pasos:

  1. Abra Preferencias de Safari -> pestaña "Avanzado" -> active la casilla de verificación "Mostrar menú Desarrollar en la barra de menú"
  2. Inicie la aplicación con UIWebView en iOS Simulator
  3. Safari -> Desarrollar -> i (Pad / Pod) Simulator -> [the name of your UIWebView file]

Ahora puede colocar Javascript complejo (en mi caso, flot ) y otras cosas en UIWebViews y depurar a voluntad.

EDITAR: Como lo señaló @Joshua J McKinnon, esta estrategia también funciona al depurar UIWebViews en un dispositivo. Simplemente habilite Web Inspector en la configuración de su dispositivo: Configuración-> Safari-> Avanzado-> Web Inspector (salud @Jeremy Wiebe)

ACTUALIZACIÓN: WKWebView también es compatible

NSTJ
fuente
13
Tenga en cuenta que esta estrategia también funciona al depurar en dispositivos iOS reales.
Joshua J. McKinnon
2
+100 si pudiera. ¡Esto es maravilloso, también funciona para aplicaciones de brecha de teléfono!
Andy Novocin
2
Intentando con un iPad, cuando voy al menú de desarrollo en Safari, no hay dispositivos para elegir. Cuando lo implemento en el simulador, funciona de maravilla.
Floydian
10
@Floydian tienes que habilitar Web Inspector en el dispositivo. Configuración-> Safari-> Avanzado-> Inspector web.
Jeremy Wiebe
2
mi aplicación no aparece en mi menú de desarrollo. He habilitado el inspector web. Aparece Safari, pero mi aplicación (que está actualizada; y muestra 2 UIWebviews) no se detecta ... ¿alguna idea?
narco
82

Tengo una solución para iniciar sesión, usando javascript, en la consola de depuración de aplicaciones. Es un poco tosco, pero funciona.

Primero, definimos la función console.log () en javascript, que abre y elimina inmediatamente 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;

Ahora tenemos que capturar esta URL en UIWebViewDelegate en la aplicación de iOS usando la función 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;
}
TinkerTank
fuente
1
vea la idea simple de NSTJ a continuación.
Ashwin S
en swift 4 tal vez? : D
Konstantinos Natsios
35

Aquí está la solución Swift: (es un truco para obtener el contexto)

  1. Creas el UIWebView.

  2. Obtenga el contexto interno y anule la función de 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")
    
Leslie Godwin
fuente
3
+100! me ahorró TONELADAS de tiempo, gran truco, requiere 0 cambios en el código JS. ¡¡Gracias!! Solo mis 2 centavos para futuros lectores: no olvide vincular el JavaScriptCoremarco a su proyecto y importen su archivo rápido de webview.
Mindbomb
Funciona para mí con Swift 4 ... tienes que lanzar "log" a NSString..context.objectForKeyedSubscript ("consola"). SetObject (unsafeBitCast (logFunction, to: AnyObject.self), forKeyedSubscript: "log" como NSString)
Serge
28

A partir de iOS7, puede utilizar el puente Javascript nativo. Algo tan simple como seguir

 #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
fuente
Por interés, ¿cuál es el lugar ideal para poner este código?
Leslie Godwin
OK, lo descubrí. Justo después de haber creado el UIWebview, puede configurar cualquier elemento JSContext.
Leslie Godwin
4
¿ JSContextSigue funcionando en iOS 8+ con WKWebView?
Nikolai Samteladze
¡Me lo puse - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationTypey funciona perfectamente!
Artur Bartczak
@NikolaiSamteladze: Lo intenté con WKWebViewiOS 11.4.1 y no puede encontrarlo documentViewy se bloquea. Vi esta respuesta y parece que no es posible de esta manera.
prueba el
10

NativeBridge es muy útil para comunicarse desde UIWebView a Objective-C. Puede usarlo para pasar registros de la consola y llamar a funciones de 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);
};

La ventaja de esta técnica es que se conservan elementos como las líneas nuevas en los mensajes de registro.

tangente405
fuente
+1. Nota para los usuarios de Apache Cordova : Cordova ya maneja console.log, ¡pero la window.onerrorfunción en esta respuesta es muy útil!
mpontillo
Para desarrolladores de Appcelerator / Titanium: esto también funciona para depurar su Ti.UI.WebView
Byters
0

Probé la solución de Leslie Godwin pero recibí este error:

'objectForKeyedSubscript' is unavailable: use subscripting

Para Swift 2.2, esto es lo que funcionó para mí:

Necesitará importar JavaScriptCore para que este código se compile:

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

Luego, en su código javascript, al llamar a console.log ("_ your_log_") se imprimirá en la consola de Xcode.

Mejor aún, agregue este código como una extensión 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")
        }
    }
}

Y luego llame a este método durante el paso de inicialización de UIWebView:

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

Rápido 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
fuente