Как мой код iPhone Objective-C получит уведомление об ошибках Javascript в UIWebView?

Мне нужно, чтобы мой iPhone Objective-C закодировал ошибки Javascript в UIWebView. Это включает в себя неперехваченные исключения, синтаксические ошибки при загрузке файлов, undefined ссылки на переменные и т.д.

Это для среды разработки, поэтому ей не нужно быть SDK-кошерным. На самом деле, это действительно нужно работать на симуляторе.

Я уже нашел некоторые скрытые трюки WebKit, например. выставлять объекты Obj-C в JS и перехватывать всплывающие окна, но это все еще ускользает от меня.

[ПРИМЕЧАНИЕ: после публикации этого вопроса я нашел один способ использования делегата отладки. Есть ли способ с более низкими накладными расходами, используя консоль ошибок/веб-инспектор?]

Ответ 1

Теперь я нашел один способ, используя крючки отладчика script в WebView (обратите внимание, НЕ UIWebView). Сначала мне пришлось подклассировать UIWebView и добавить такой метод:

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

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

Далее вы должны создать класс YourScriptDebugDelegate, содержащий такие методы:

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

Вероятно, для этого существует большое влияние времени выполнения, поскольку делегат отладки также может предоставлять методы, которые нужно вызывать для ввода и выхода из фрейма стека, и для выполнения каждой строки кода.

См. http://www.koders.com/noncode/fid7DE7ECEB052C3531743728D41A233A951C79E0AE.aspx для определения Objective-C ++ WebScriptDebugDelegate.

Те другие методы:

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

Обратите внимание, что все это скрыто в частной структуре, поэтому не пытайтесь помещать это в код, который вы отправляете в App Store, и будьте готовы к хакерству, чтобы заставить его работать.

Ответ 2

Я создал красивую небольшую категорию, которую вы можете добавить в свой проект... Он основан на решении Роберта Сандерса. Престижность.

Вы можете загрузить его здесь:

UIWebView + Отладка

Это облегчит вам отладку UIWebView:)

Ответ 3

Я использовал большое решение, предложенное Робертом Сандерсом: Как мой код iPhone Objective-C получит уведомление об ошибках Javascript в UIWebView?

Этот крючок для webkit отлично работает и на iPhone. Вместо стандартного UIWebView я выделил полученный MyUIWebView. Мне также нужно было определить скрытые классы внутри MyWebScriptObjectDelegate.h:

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

Внутри ios sdk 4.1 функция:

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

устарел, а вместо него я использовал функцию:

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

Кроме того, я получаю некоторые раздражающие предупреждения, такие как "NSObject может не отвечать -windowScriptObject", потому что интерфейс класса скрыт. Я игнорирую их, и это работает хорошо.

Ответ 4

Прямой путь: поместите этот код поверх своего контроллера/представления, использующего 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

И затем создайте его следующим образом:

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

Использование #ifdef DEBUG гарантирует, что оно не входит в сборку релиза, но я также рекомендовал бы комментировать его, когда вы его не используете, так как это влияет на производительность. Кредит принадлежит Роберту Сандерсу и Prcela за оригинальный код

Также, если вы используете ARC, вам может понадобиться добавить "-fno-objc-arc", чтобы предотвратить некоторые ошибки сборки.

Ответ 5

Один из способов, который работает во время разработки, если у вас есть Safari v 6+ (я не знаю, какая версия iOS вам нужна), следует использовать инструменты разработки Safari и подключаться к UIWebView через него.

  • В Safari: включить меню "Разработка" ( "Настройки" > "Дополнительно" > "Показать меню" в строке меню)
  • Подключите телефон к компьютеру через кабель.
  • Элемент списка
  • Загрузите приложение (либо через xcode, либо просто запустите его), и перейдите на экран, который вы хотите отлаживать.
  • Вернитесь в Safari, откройте меню "Разработка", ищите имя своего устройства в этом меню (мой называется iPhone 5), должен быть прав под User Agent.
  • Выберите его, и вы увидите раскрывающийся список веб-представлений, отображаемых в настоящее время в вашем приложении.
  • Если на экране есть несколько веб-представлений, вы можете попытаться рассказать их обо всем, переместив имя приложения в меню разработки. Соответствующий UIWebView станет синим.
  • Выберите имя приложения, откроется окно разработки и вы можете проверить консоль. Вы можете даже выдавать JS-команды через него.

Ответ 6

Я создал корректор ошибок кодера SDK, который включает в себя:

  • Сообщение об ошибке
  • Имя файла, в котором произошла ошибка
  • Номер строки, в которой происходит ошибка
  • Столбец JavaScript, включающий параметры, переданные

Это часть структуры QuickConnectiPhone, доступной из проекта sourceForge

Существует даже пример приложения, которое показывает, как отправить сообщение об ошибке на терминал Xcode.

Все, что вам нужно сделать, - это окружить ваш код JavaScript, включая определения функций и т.д. с помощью try catch. Это должно выглядеть так.

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

Это не очень хорошо работает с ошибками компиляции, но работает со всеми остальными. Даже анонимные функции.

Здесь находится блог здесь и включает ссылки на wiki, sourceForge, группу google и твиттер. Возможно, это поможет вам.

Ответ 7

Я сделал это в прошивке 1.x, но не 2.x. Вот код, который я использовал в 1.x, он должен по крайней мере помочь вам на вашем пути.

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

Ответ 9

Первая настройка WebViewJavascriptBridge, затем переопределите функцию console.error.

В javascript

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

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

Ответ 10

Более простым решением для некоторых случаев может быть просто добавить Firebug Lite на веб-страницу.