Javascript console.log () dans une interface utilisateur iOS

84

Lors de l'écriture d'une application iPhone / iPad avec une UIWebView, la console n'est pas visible. cette excellente réponse montre comment piéger les erreurs, mais j'aimerais aussi utiliser console.log ().

TinkerTank
la source
1
Écrivez-le d'abord dans le navigateur, activez les outils de développement, puis regardez la sortie de la console.
beatgammit

Réponses:

183

Après avoir consulté un collègue estimé aujourd'hui, il m'a alerté sur Safari Developer Toolkit, et comment cela peut être connecté à UIWebViews dans le simulateur iOS pour la sortie de la console (et le débogage!).

Pas:

  1. Ouvrez les Préférences Safari -> onglet "Avancé" -> cochez la case "Afficher le menu Développement dans la barre de menus"
  2. Démarrer l'application avec UIWebView dans le simulateur iOS
  3. Safari -> Développer -> Simulateur i (Pad / Pod) -> [the name of your UIWebView file]

Vous pouvez maintenant déposer du Javascript complexe (dans mon cas, flot ) et d'autres éléments dans UIWebViews et déboguer à volonté.

EDIT: Comme l'a souligné @Joshua J McKinnon, cette stratégie fonctionne également lors du débogage d'UIWebViews sur un appareil. Activez simplement l'inspecteur Web sur les paramètres de votre appareil: Paramètres-> Safari-> Avancé-> Inspecteur Web (bravo @Jeremy Wiebe)

MISE À JOUR: WKWebView est également pris en charge

NSTJ
la source
13
Notez que cette stratégie fonctionne également lors du débogage sur de vrais appareils iOS.
Joshua J. McKinnon
2
+100 si je pouvais. C'est merveilleux, cela fonctionne aussi pour les applications de téléphone mobile!
Andy Novocin
2
En essayant avec un iPad, lorsque je vais dans le menu de développement sur Safari, il n'y a pas d'appareils à choisir. Lorsque je déploie sur le simulateur, cela fonctionne comme un charme.
Floydian
10
@Floydian vous devez activer Web Inspector sur l'appareil. Paramètres-> Safari-> Avancé-> Inspecteur Web.
Jeremy Wiebe
2
mon application n'apparaît pas dans mon menu de développement. J'ai activé l'inspecteur Web. Safari apparaît, mais mon application (qui est actuelle; y affichant 2 UIWebviews) n'est pas détectée .. des idées?
narco
82

J'ai une solution pour me connecter, en utilisant javascript, à la console de débogage des applications. C'est un peu grossier, mais ça marche.

Tout d'abord, nous définissons la fonction console.log () en javascript, qui ouvre et supprime immédiatement un iframe avec 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;

Nous devons maintenant attraper cette URL dans UIWebViewDelegate dans l'application iOS à l'aide de la fonction 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
la source
1
voir l'idée simple de NSTJ ci-dessous.
Ashwin S
dans Swift 4 peut-être? : D
Konstantinos Natsios
35

Voici la solution Swift: (C'est un peu un hack pour obtenir le contexte)

  1. Vous créez l'UIWebView.

  2. Obtenez le contexte interne et remplacez la fonction 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
la source
3
+100! m'a sauvé des tonnes de temps, super hack, nécessite 0 changement dans le code JS. Merci!! Juste mes 2 cents pour les futurs lecteurs: n'oubliez pas de lier le JavaScriptCoreframework à votre projet et importcelui - ci dans votre fichier webview swift.
mindbomb
Fonctionne pour moi avec Swift 4 ... vous devez convertir "log" en NSString..context.objectForKeyedSubscript ("console"). SetObject (unsafeBitCast (logFunction, to: AnyObject.self), forKeyedSubscript: "log" en tant que NSString)
Serge
28

À partir d'iOS7, vous pouvez utiliser le pont Javascript natif. Quelque chose d'aussi simple que de suivre

 #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
la source
Par intérêt, où est l'endroit idéal pour mettre ce code?
Leslie Godwin
OK, je l'ai compris. Juste après avoir créé le, UIWebviewvous pouvez configurer n'importe quel élément JSContext.
Leslie Godwin
4
Fonctionne JSContexttoujours sous iOS 8+ avec WKWebView?
Nikolai Samteladze
Je l'ai mis dedans - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationTypeet ça marche parfaitement!
Artur Bartczak
@NikolaiSamteladze: J'ai essayé avec WKWebViewet iOS 11.4.1 et il ne trouve pas documentViewet plante. J'ai vu cette réponse et il semble que ce n'est pas possible de cette façon.
test
10

NativeBridge est très utile pour communiquer depuis une UIWebView vers Objective-C. Vous pouvez l'utiliser pour transmettre les journaux de la console et appeler les fonctions 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);
};

L'avantage de cette technique est que des éléments tels que les retours à la ligne dans les messages de journal sont conservés.

tangente405
la source
+1. Note aux utilisateurs d' Apache Cordova - Cordova gère déjà console.log, mais la window.onerrorfonction dans cette réponse est très utile!
mpontillo le
Pour les développeurs Appcelerator / Titanium: cela fonctionne également pour déboguer votre Ti.UI.WebView
Byters
0

J'ai essayé le correctif de Leslie Godwin mais obtenait cette erreur:

'objectForKeyedSubscript' is unavailable: use subscripting

Pour Swift 2.2, voici ce qui a fonctionné pour moi:

Vous devrez importer JavaScriptCore pour que ce code soit compilé:

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

Ensuite, dans votre code javascript, l'appel de console.log ("_ votre_log_") s'imprimera dans la console Xcode.

Mieux encore, ajoutez ce code en tant qu'extension à 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")
        }
    }
}

Et puis appelez cette méthode lors de votre étape d'initialisation UIWebView:

let webView = UIWebView(frame: CGRectZero)
webView.hijackConsoleLog()
parag
la source
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
la source