Как мой код iPhone Objective-C может получать уведомления об ошибках Javascript в UIWebView? - PullRequest
46 голосов
/ 11 октября 2008

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

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

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

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

Ответы [ 10 ]

28 голосов
/ 11 октября 2008

Теперь я нашел один способ, используя перехватчики сценариев в 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, и будьте готовы к каким-то хакерским попыткам заставить его работать.

13 голосов
/ 16 июля 2012

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

Вы можете скачать его здесь:

UIWebView + Debug

Это должно упростить отладку UIWebView:)

11 голосов
/ 05 ноября 2010

Я использовал замечательное решение, предложенное Робертом Сандерсом: Как мой код 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», потому что интерфейс класса скрыт. Я игнорирую их, и это прекрасно работает.

8 голосов
/ 18 марта 2013

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

  1. В Safari: включить меню «Разработка» («Установки»> «Дополнительно»> «Показать меню разработки» в строке меню)
  2. Подключите телефон к компьютеру через кабель.
  3. Элемент списка
  4. Загрузите приложение (через xcode или просто запустите его) и перейдите на экран, который вы хотите отладить.
  5. Вернувшись в Safari, откройте меню «Разработка», найдите имя вашего устройства в этом меню (мое называется iPhone 5), должно быть прямо под User Agent.
  6. Выберите его, и вы увидите выпадающий список веб-просмотров, видимых в настоящее время в вашем приложении.
  7. Если у вас есть несколько веб-просмотров на экране, вы можете попытаться различить их, пролистав название приложения в меню разработки. Соответствующий UIWebView станет синим.
  8. Выберите название приложения, откроется окно разработки и вы сможете проверить консоль. Вы даже можете выдавать команды JS через него.
8 голосов
/ 08 октября 2011

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

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

4 голосов
/ 22 апреля 2009

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

  1. Сообщение об ошибке
  2. Имя файла, в котором произошла ошибка
  3. Номер строки, в которой происходит ошибка
  4. JavaScript callstack, включая переданные параметры

Он является частью платформы QuickConnectiPhone, доступной с проекта sourceForge

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

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

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

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

Блог о разработке здесь здесь и содержит ссылки на вики, sourceForge, группу google и твиттер. Может быть, это поможет вам.

0 голосов
/ 13 апреля 2017

Первая настройка 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);
        }
    }
}];
0 голосов
/ 23 февраля 2015

См. Обработку исключений в iOS7: http://www.bignerdranch.com/blog/javascriptcore-example/

[context setExceptionHandler:^(JSContext *context, JSValue *value) {
    NSLog(@"%@", value);
}];
0 голосов
/ 16 ноября 2012

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

0 голосов
/ 11 октября 2008

Я сделал это в прошивке 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];
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...