web-dev-qa-db-fra.com

Comment mon code iPhone Objective-C peut-il être averti des erreurs Javascript dans une UIWebView?

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

Il s'agit d'un environnement de développement, il n'a donc pas besoin d'être casher SDK. En fait, il n'a vraiment besoin que de travailler sur le simulateur.

J'ai déjà trouvé utilisé certaines des astuces WebKit cachées, par exemple exposer les objets Obj-C à JS et intercepter les popups d'alerte, mais celui-ci m'échappe toujours.

[REMARQUE: après avoir posté cela, 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 en utilisant la console d'erreur/l'inspecteur Web?]

46
Robert Sanders

J'ai maintenant trouvé un moyen d'utiliser les crochets de débogage de script dans WebView (notez, PAS UIWebView). J'ai d'abord dû sous-classer UIWebView et ajouter 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 comme celles-ci:

// in YourScriptDebugDelegate

- (void)webView:(WebView *)webView       didParseSource:(NSString *)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:(NSString *)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 quitter une trame de pile et pour exécuter chaque ligne de code.

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

Ces autres méthodes:

// just entered a stack frame (i.e. 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 (i.e. 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 cadre privé, alors n'essayez pas de mettre cela dans le code que vous soumettez à l'App Store, et soyez prêt pour un certain piratage pour le faire fonctionner.

28
Robert Sanders

J'ai créé une jolie petite catégorie drop-in que vous pouvez ajouter à votre projet ... Elle est basée sur la solution Robert Sanders. Gloire.

Vous pouvez le télécharger ici:

IWebView + Debug

Cela devrait faciliter le débogage de votre UIWebView :)

13
Pablo

J'ai utilisé l'excellente solution proposée par Robert Sanders: Comment mon code iPhone Objective-C peut-il être averti des erreurs Javascript dans une UIWebView?

Ce crochet pour webkit fonctionne bien également sur iPhone . Au lieu d'UIWebView standard, j'ai alloué MyUIWebView dérivé. J'avais également besoin de définir des classes cachées dans MyWebScriptObjectDelegate.h:

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

Dans l'ios sdk 4.1, la fonction:

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

est obsolète et à la place j'ai utilisé la fonction:

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

De plus, 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.

11
Prcela

Une façon qui fonctionne pendant le développement si vous avez Safari v 6+ (je ne suis pas sûr de la version iOS dont vous avez besoin) est d'utiliser les outils de développement Safari et de vous connecter à UIWebView à travers lui.

  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 liste
  4. Chargez l'application (via xcode ou lancez-la simplement) 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 être juste sous l'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 plusieurs vues Web à l'écran, vous pouvez essayer de les différencier en survolant le nom de l'application dans le menu de développement. L'UIWebView correspondante deviendra bleue.
  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.
9
bobc

Straight Forward Way: Mettez ce code au-dessus de 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 puis instanciez-le 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 inclus dans la version, mais je recommanderais également de le commenter lorsque vous ne l'utilisez pas car cela a un impact sur les performances. Nous remercions Robert Sanders et Prcela pour le code original

De plus, si vous utilisez ARC, vous devrez peut-être ajouter "-fno-objc-arc" pour éviter certaines erreurs de génération.

8
psy

J'ai créé un rapporteur d'erreurs 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 pile d'appels JavaScript, y compris les paramètres passés

Il fait partie du framework QuickConnectiPhone disponible depuis le 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 fonction, 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 développement le blog est ici est ici et inclut des liens vers le wiki, sourceForge, le groupe google et Twitter. Peut-être que cela vous aiderait.

4
Lee Barney

Première configuration 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)
    };

Dans Objective-C

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

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: (NSString*) 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];
}
0
kdbdallas

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

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