Comment le code Objective-C de mon iPhone peut-il être informé des erreurs Javascript dans UIWebView?

J’ai besoin que mon code Objective-C pour iPhone détecte les erreurs Javascript dans UIWebView. Cela inclut les exceptions non interceptées, les erreurs de syntaxe lors du chargement de fichiers, les références de variable non définies, etc.

Ceci est pour un environnement de développement, il n’a donc pas besoin d’être SDK-Kasher. En fait, il suffit de travailler sur le simulateur.

J’ai déjà trouvé certains des trucs cachés de WebKit pour exposer des objects Obj-C à JS et intercepter des popups d’alerte, mais celui-ci m’échappe toujours.

[NOTE: après avoir posté ceci, j’ai trouvé un moyen d’utiliser un délégué de débogage. Existe-t-il un moyen de réduire les frais généraux à l’aide de la console d’erreur / de l’inspecteur Web?]

J’ai maintenant trouvé un moyen d’utiliser les hooks de débogage de script dans WebView (notez NOT UIWebView). J’ai d’abord dû sous-classer UIWebView et append une méthode comme celle-ci:

- (void)webView:(id)webView windowScriptObjectAvailable:(id)newWindowScriptObject { // save these goodies windowScriptObject = newWindowScriptObject; privateWebView = webView; if (scriptDebuggingEnabled) { [webView setScriptDebugDelegate:[[YourScriptDebugDelegate alloc] init]]; } } 

Ensuite, vous devez créer une classe YourScriptDebugDelegate qui contient des méthodes telles que celles-ci:

 // in YourScriptDebugDelegate - (void)webView:(WebView *)webView didParseSource:(NSSsortingng *)source baseLineNumber:(unsigned)lineNumber fromURL:(NSURL *)url sourceId:(int)sid forWebFrame:(WebFrame *)webFrame { NSLog(@"NSDD: called didParseSource: sid=%d, url=%@", sid, url); } // some source failed to parse - (void)webView:(WebView *)webView failedToParseSource:(NSSsortingng *)source baseLineNumber:(unsigned)lineNumber fromURL:(NSURL *)url withError:(NSError *)error forWebFrame:(WebFrame *)webFrame { NSLog(@"NSDD: called failedToParseSource: url=%@ line=%d error=%@\nsource=%@", url, lineNumber, error, source); } - (void)webView:(WebView *)webView exceptionWasRaised:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno forWebFrame:(WebFrame *)webFrame { NSLog(@"NSDD: exception: sid=%d line=%d function=%@, caller=%@, exception=%@", sid, lineno, [frame functionName], [frame caller], [frame exception]); } 

Cela a probablement un impact important sur l’exécution, car le délégué de débogage peut également fournir des méthodes à appeler pour entrer et sortir d’un cadre de stack et pour exécuter chaque ligne de code.

Voir http://www.koders.com/noncode/fid7DE7ECEB052C3531743728D41A233A951C79E0AE.aspx pour la définition d’Objective-C ++ de WebScriptDebugDelegate.

Ces autres méthodes:

 // just entered a stack frame (ie called a function, or started global scope) - (void)webView:(WebView *)webView didEnterCallFrame:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno forWebFrame:(WebFrame *)webFrame; // about to execute some code - (void)webView:(WebView *)webView willExecuteStatement:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno forWebFrame:(WebFrame *)webFrame; // about to leave a stack frame (ie return from a function) - (void)webView:(WebView *)webView willLeaveCallFrame:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno forWebFrame:(WebFrame *)webFrame; 

Notez que tout cela est caché dans un environnement privé, alors n’essayez pas de le mettre dans le code que vous soumettez à l’App Store, et préparez-vous à du piratage pour le faire fonctionner.

J’ai créé une jolie petite catégorie d’insertion que vous pouvez append à votre projet … Elle est basée sur la solution de Robert Sanders. Gloire.

Vous pouvez le télécharger ici:

UIWebView + Debug

Cela devrait rendre le débogage de UIWebView plus facile 🙂

J’ai utilisé la solution proposée par Robert Sanders: Comment le code Objective-C de mon iPhone peut-il être informé des erreurs Javascript dans UIWebView?

Ce crochet pour webkit fonctionne également très bien sur iPhone . Au lieu de UIWebView standard, j’ai atsortingbué MyUIWebView dérivé. Je devais aussi définir des classes cachées dans MyWebScriptObjectDelegate.h:

@class WebView;
@class WebFrame;
@class WebScriptCallFrame;

Dans le SDK ios 4.1, la fonction:

 - (void)webView:(id)webView windowScriptObjectAvailable:(id)newWindowScriptObject 

est obsolète et au lieu de cela j’ai utilisé la fonction:

 - (void)webView:(id)sender didClearWindowObject:(id)windowObject forFrame:(WebFrame*)frame 

En outre, je reçois des avertissements ennuyeux comme “NSObject peut ne pas répondre -windowScriptObject” car l’interface de classe est masquée. Je les ignore et ça marche bien.

Straight Forward Way: Placez ce code sur votre contrôleur / vue qui utilise UIWebView

 #ifdef DEBUG @interface DebugWebDelegate : NSObject @end @implementation DebugWebDelegate @class WebView; @class WebScriptCallFrame; @class WebFrame; - (void)webView:(WebView *)webView exceptionWasRaised:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno forWebFrame:(WebFrame *)webFrame { NSLog(@"NSDD: exception: sid=%d line=%d function=%@, caller=%@, exception=%@", sid, lineno, [frame functionName], [frame caller], [frame exception]); } @end @interface DebugWebView : UIWebView id windowScriptObject; id privateWebView; @end @implementation DebugWebView - (void)webView:(id)sender didClearWindowObject:(id)windowObject forFrame:(WebFrame*)frame { [sender setScriptDebugDelegate:[[DebugWebDelegate alloc] init]]; } @end #endif 

Et ensuite instancier comme ceci:

 #ifdef DEBUG myWebview = [[DebugWebView alloc] initWithFrame:frame]; #else myWebview = [[UIWebView alloc] initWithFrame:frame]; #endif 

L’utilisation de #ifdef DEBUG garantit qu’il ne sera pas intégré à la version de publication, mais je vous recommande également de le commenter lorsque vous ne l’utilisez pas, car cela a un impact sur les performances. Crédit à Robert Sanders et Prcela pour le code original

De même, si vous utilisez ARC, vous devrez peut-être append “-fno-objc-arc” pour éviter certaines erreurs de génération.

Une façon de travailler pendant le développement si vous avez Safari v 6+ (je ne suis pas certain de la version iOS dont vous avez besoin) consiste à utiliser les outils de développement Safari et à y accéder via UIWebView.

  1. Dans Safari: activez le menu Développement (Préférences> Avancé> Afficher le menu Développement dans la barre de menus)
  2. Branchez votre téléphone sur l’ordinateur via le câble.
  3. Élément de la liste
  4. Chargez l’application (via xcode ou lancez-la) et accédez à l’écran que vous souhaitez déboguer.
  5. De retour dans Safari, ouvrez le menu Développer, recherchez le nom de votre appareil dans ce menu (le mien s’appelle iPhone 5), devrait figurer sous Agent utilisateur.
  6. Sélectionnez-le et vous devriez voir une liste déroulante des vues Web actuellement visibles dans votre application.
  7. Si vous avez plus d’une vue Web à l’écran, vous pouvez essayer de les distinguer en survolant le nom de l’application dans le menu Développement. Le UIWebView correspondant deviendra bleu.
  8. Sélectionnez le nom de l’application, la fenêtre de développement s’ouvre et vous pouvez inspecter la console. Vous pouvez même émettre des commandes JS à travers elle.

J’ai créé un protractor d’erreur casher SDK qui comprend:

  1. Le message d’erreur
  2. Le nom du fichier dans lequel l’erreur se produit
  3. Le numéro de ligne sur lequel l’erreur se produit
  4. La stack d’appel JavaScript, y compris les parameters transmis

Il fait partie du framework QuickConnectiPhone disponible à partir du projet sourceForge

Il existe même un exemple d’application qui montre comment envoyer un message d’erreur au terminal Xcode.

Tout ce que vous devez faire est d’entourer votre code JavaScript, y compris les définitions de fonctions, etc. avec try catch. Ça devrait ressembler à ça.

 try{ //put your code here } catch(err){ logError(err); } 

Cela ne fonctionne pas très bien avec les erreurs de compilation, mais fonctionne avec tous les autres. Même des fonctions anonymes.

Le blog de développement est ici et comprend des liens vers le wiki, sourceForge, le groupe google et twitter. Peut-être que cela vous aiderait.

Je l’ai fait dans le firmware 1.x mais pas 2.x. Voici le code que j’ai utilisé dans 1.x, il devrait au moins vous aider sur votre chemin.

 // Dismiss Javascript alerts and telephone confirms /*- (void)alertSheet:(UIAlertSheet*)sheet buttonClicked:(int)button { if (button == 1) { [sheet setContext: nil]; } [sheet dismiss]; }*/ // Javascript errors and logs - (void) webView: (WebView*)webView addMessageToConsole: (NSDictionary*)dictionary { NSLog(@"Javascript log: %@", dictionary); } // Javascript alerts - (void) webView: (WebView*)webView runJavaScriptAlertPanelWithMessage: (NSSsortingng*) message initiatedByFrame: (WebFrame*) frame { NSLog(@"Javascript Alert: %@", message); UIAlertSheet *alertSheet = [[UIAlertSheet alloc] init]; [alertSheet setTitle: @"Javascript Alert"]; [alertSheet addButtonWithTitle: @"OK"]; [alertSheet setBodyText:message]; [alertSheet setDelegate: self]; [alertSheet setContext: self]; [alertSheet popupAlertAnimated:YES]; } 

Voir la gestion des exceptions dans iOS7: http://www.bignerdranch.com/blog/javascriptcore-example/

 [context setExceptionHandler:^(JSContext *context, JSValue *value) { NSLog(@"%@", value); }]; 

Configurez d’abord WebViewJavascriptBridge , puis remplacez la fonction console.error.

En javascript

  window.originConsoleError = console.error; console.error = (msg) => { window.originConsoleError(msg); bridge.callHandler("sendConsoleLogToNative", { action:action, message:message }, null) }; 

En Objective-C

 [self.bridge registerHandler:@"sendConsoleLogToNative" handler:^(id data, WVJBResponseCallback responseCallback) { NSSsortingng *action = data[@"action"]; NSSsortingng *msg = data[@"message"]; if (isSsortingngValid(action)){ if ([@"console.error" isEqualToSsortingng:action]){ NSLog(@"JS error :%@",msg); } } }]; 

Une solution plus simple dans certains cas pourrait consister simplement à append Firebug Lite à la page Web.