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.
J’ai créé un protractor d’erreur casher SDK qui comprend:
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.