2008-10-10 12 views
45

Potrzebuję mieć mój iPhone Objective-C kod złapać błędy JavaScript w UIWebView. Obejmuje to nieprzechwycone wyjątki, błędy składni podczas ładowania plików, niezdefiniowane referencje zmiennych itp.W jaki sposób mój kod Objective-C iPhone'a może otrzymywać powiadomienia o błędach JavaScript w UIWebView?

Jest to środowisko programistyczne, więc nie musi to być program SDK-koszerowy. W rzeczywistości to naprawdę musi działać na symulatorze.

Znalazłem już używane niektóre ukryte sztuczki WebKit do np. wystawiać obiekty Obj-C na JS i przechwytywać wyskakujące okienka alertów, ale ten wciąż mnie omija.

[UWAGA: po opublikowaniu tego znalazłem jeden sposób korzystania z delegata debugowania. Czy istnieje sposób na niższy narzut, używając konsoli błędów/inspektora sieci?]

Odpowiedz

0

Zrobiłem to w oprogramowaniu 1.x, ale nie 2.x. Oto kod użyty w 1.x, powinien przynajmniej pomóc Ci w drodze.

// 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

znalazłem, że gdzieś, ale nie wydaje się, aby pracować dla mnie w 2.1. Zakładając, że są to metody dodane do podklasy UIWebView. –

28

Znalazłem jeden sposób, używając haseł debuggera skryptów w WebView (uwaga, NIE UIWebView). Po raz pierwszy mieliśmy do podklasy UIWebView i dodać metodę tak:

- (void)webView:(id)webView windowScriptObjectAvailable:(id)newWindowScriptObject { 
    // save these goodies 
    windowScriptObject = newWindowScriptObject; 
    privateWebView = webView; 

    if (scriptDebuggingEnabled) { 
     [webView setScriptDebugDelegate:[[YourScriptDebugDelegate alloc] init]]; 
    } 
} 

Następnie należy utworzyć YourScriptDebugDelegate klasę, która zawiera metody takie jak:

// 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]); 
} 

Prawdopodobnie duży wpływ wykonawcze do tego, jako delegat debugowania może również dostarczać metody, które należy wywoływać w celu wejścia i wyjścia z ramki stosu oraz wykonywania każdej linii kodu.

Zobacz definicję obiektu WebScriptDebugDelegate w języku Objective-C++ pod numerem http://www.koders.com/noncode/fid7DE7ECEB052C3531743728D41A233A951C79E0AE.aspx.

Te inne metody:

// 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; 

pamiętać, że to wszystko jest ukryte w prywatnym ram, więc nie starają się umieścić to w kodzie złożyć do App Store, a także być przygotowane przez jakiś hackery aby to działało.

+1

Próbowałem podklasy UIWebView i dodać metodę webView: windowScriptObjectAvailable zgodnie z opisem, ale nigdy nie jest wywoływana. Dzieje się tak z pakietem SDK iPhone 3.0. Czy to już nie działa lub czy robię coś nie tak? – pjb3

+0

Próbowałem tego na iPhone i działa świetnie. Wydaje się jednak, że zgłaszane są tylko obsługiwane wyjątki. Może nie być pomocne, jeśli zniknęły nieobsłużone wyjątki. – Prcela

+0

im using exceptionWasRaised w delegate i iteracyjne nad frame caller. ale wszystkie mają functionName zwraca null. Dowolny pomysł? – at0mzk

4

I stworzyli koszerne reporter błędzie SDK, który zawiera:

  1. Komunikat o błędzie
  2. Nazwa pliku błąd dzieje się w
  3. numer linii błędu dzieje na
  4. The Callstack JavaScript zawierający parametry przekazane

Jest to część struktury QuickConnectiPhone dostępnej w the sourceForge project

Istnieje nawet przykład aplikacji, która pokazuje, jak wysłać komunikat o błędzie do terminala Xcode.

Wszystko, co musisz zrobić, to otoczyć swój kod JavaScript, w tym definicje funkcji itp. Za pomocą funkcji catch catch. To powinno wyglądać tak.

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

To nie działa dobrze z błędami kompilacji, ale działa ze wszystkimi innymi. Nawet anonimowe funkcje.

Opracowanie blog is here jest tutaj i zawiera łącza do wiki, sourceForge, grupy google i Twittera. Może to ci pomoże.

11

Kiedyś wielki rozwiązanie zaproponowane Robert Sanders: How can my iPhone Objective-C code get notified of Javascript errors in a UIWebView?

To hak na WebKit działa dobrze również na iPhone. Zamiast standardowego interfejsu UIWebView przydzieliłem pochodną MyUIWebView. Musiałem także zdefiniowanie klas ukrytych wewnątrz MyWebScriptObjectDelegate.h:

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

ciągu iOS SDK 4.1 funkcję:

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

jest przestarzałe a zamiast niego Użyłem funkcji:

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

Dostaję również denerwujące ostrzeżenia, takie jak "NSObject może nie odpowiadać -windowScriptObject", ponieważ interfejs klasy jest ukryty. Ignoruję je i działa dobrze.

8

Straight Forward Way: Umieść ten kod na górze kontrolera/widoku, który używa 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 

A następnie oznacz ją tak:

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

Korzystanie #ifdef DEBUG zapewnia, że nie wchodzi w skład wydania, ale zaleciłbym również komentowanie go, gdy nie używasz go, ponieważ ma wpływ na wydajność. Podziękowania należą się Robertowi Sandersowi i Prcelie za oryginalny kod:

Również w przypadku używania ARC może być konieczne dodanie "-fno-objc-arc", aby zapobiec błędom kompilacji.

+2

Można również utworzyć kategorię dla interfejsu UIWebView za pomocą metody webView: didClearWindowObject: forFrame: zamiast tworzyć instancje różnych typów klas. –

+0

Wystąpił błąd dotyczący typu odbiornika WebScriptCallFrame, ponieważ wiadomość typu instance jest deklinacją do przodu. na ios6 – vagabond

13

Stworzyłem ładną małą kategorię, którą można dodać do swojego projektu ... Bazuje na rozwiązaniu Roberta Sandersa. Sława.

Można go dowload tutaj:

UIWebView+Debug

Powinno to dużo łatwiejsze do debugowania ty UIWebView :)

+0

Dzięki. To było niesamowicie pomocne, ponieważ pochodzę z MonoTouch i jeszcze nie poznałem się w Celu C. –

+0

To jest [jak używam twojego rozwiązania w MonoTouch] (http://stackoverflow.com/a/11529165/458193). –

+0

Obiecujący. Nie udało mi się jednak otworzyć adresu URL debugowania w przeglądarce. – neoneye

-3

Prostszym rozwiązaniem dla niektórych przypadkach może być po prostu dodać do Firebug Lite Strona internetowa.

7

Jeden ze sposobów, który działa podczas programowania, jeśli masz Safari v 6+ (nie jestem pewien, jakiej wersji potrzebujesz iOS), to użyj narzędzi programistycznych Safari i podłącz się do interfejsu UIWebView.

  1. W Safari: Włącz menu Develop (Preferencje> Zaawansowane> Pokaż menu w pasku menu Develop)
  2. Podłącz telefon do komputera za pomocą kabla.
  3. Pozycja na liście
  4. Załaduj aplikację (przez kod xcode lub po prostu uruchom ją) i przejdź do ekranu, który chcesz debugować.
  5. Wracając do Safari, otwórz menu Develop, znajdź nazwę swojego urządzenia w tym menu (mój nazywa się iPhone 5), powinien znajdować się pod User Agent.
  6. Wybierz to i powinieneś zobaczyć rozwijane widoki internetowe aktualnie widoczne w twojej aplikacji.
  7. Jeśli masz więcej niż jeden obraz na ekranie, możesz spróbować je rozdzielić, przewracając nazwę aplikacji w menu rozwijanym. Odpowiedni interfejs UIWebView zmieni kolor na niebieski.
  8. Wybierz nazwę aplikacji, rozwija się okno i możesz sprawdzić konsolę. Można nawet za jego pośrednictwem wydawać komendy JS.
+2

To powinna być zaakceptowana odpowiedź, jest to o wiele lepsze rozwiązanie niż inne :) – NSTJ

+0

Chciałbym dodać, że działa to również w symulatorze iOS. Zostanie wyświetlony jako agent użytkownika "Symulator iOS" – morksinaanab

0

See obsługa wyjątków w iOS7: http://www.bignerdranch.com/blog/javascriptcore-example/

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

Witaj, Yoav, witamy w Stack Overflow. Może się okazać, że jest to przydatne: http://stackoverflow.com/help/how-to-answer.Zastanów się nad tym, aby twoja odpowiedź była bardziej użyteczna dla czytelników: "Zachęca się linki do zasobów zewnętrznych, ale dodaj kontekst wokół linku, aby inni użytkownicy mieli pojęcie, co to jest i dlaczego tam jest. Zawsze cytuj najbardziej odpowiednią część ważne ogniwo, na wypadek, gdyby strona docelowa była nieosiągalna lub została trwale wyłączona. " "Spójność jest akceptowalna, ale pełniejsze wyjaśnienia są lepsze". –

0

Pierwsza konfiguracja WebViewJavascriptBridge, następnie zastąpić funkcję console.error.

w JavaScript

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

w 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); 
     } 
    } 
}]; 
Powiązane problemy