Мне нужно, чтобы код iPhone Objective-C улавливал ошибки Javascript в UIWebView. Сюда входят неперехваченные исключения, синтаксические ошибки при загрузке файлов, ссылки на неопределенные переменные и т. д.
Это для среды разработки, поэтому SDK не обязательно должен быть кошерным. Фактически, это действительно нужно только для работы на симуляторе.
Я уже обнаружил, что использовал некоторые скрытые трюки WebKit, например, открывать объекты Obj-C для JS и перехватывать всплывающие окна с предупреждениями, но этот все еще ускользает от меня.
[ПРИМЕЧАНИЕ: после публикации я нашел способ использовать делегат отладки. Есть ли способ снизить накладные расходы с помощью консоли ошибок / веб-инспектора?]





Я сделал это в прошивке 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];
}
Теперь я нашел один способ использования перехватчиков отладчика скриптов в 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 для определения WebScriptDebugDelegate в Objective-C++.
Эти другие методы:
// 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, и будьте готовы к некоторым взломам, чтобы заставить его работать.
Я попытался создать подкласс UIWebView и добавить метод webView: windowScriptObjectAvailable, как вы описали, но он так и не был вызван. Это с iphone 3.0 SDK. Это больше не работает или я что-то не так делаю?
Я пробовал это на iphone, и он отлично работает. Но похоже, что возникают только обработанные исключения. Это может оказаться бесполезным, если необработанные исключения исчезнут.
Я использую исключение было поднято в делегате и перебираю вызывающий фрейм. но все они имеют функцию Name, которая возвращает значение null. Любая идея?
Я создал репортер кошерных ошибок SDK, который включает:
Это часть платформы QuickConnectiPhone, доступной по адресу проект sourceForge
Существует даже пример приложения, которое показывает, как отправить сообщение об ошибке на терминал Xcode.
Все, что вам нужно сделать, это окружить ваш код JavaScript, включая определения функций и т. д., С помощью try catch. Должно получиться вот так.
try{
//put your code here
}
catch(err){
logError(err);
}
Он не очень хорошо работает с ошибками компиляции, но работает со всеми остальными. Даже анонимные функции.
Разработка блог здесь находится здесь и включает ссылки на вики, sourceForge, группу Google и Twitter. Может быть, это поможет вам.
Я использовал отличное решение, предложенное Робертом Сандерсом: Как мой код 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», потому что интерфейс класса скрыт. Я игнорирую их, и это прекрасно работает.
Прямой путь: поместите этот код поверх вашего контроллера / представления, использующего 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», чтобы предотвратить некоторые ошибки сборки.
Вы также можете просто создать категорию для UIWebView с помощью метода webView: didClearWindowObject: forFrame: вместо того, чтобы создавать экземпляры разных типов классов.
Я получил сообщение об ошибке типа приемника WebScriptCallFrame, например, сообщение является предварительным объявлением. на ios6
Я создал небольшую симпатичную подкатегорию, которую вы можете добавить в свой проект ... Он основан на решении Роберта Сандерса. Престижность.
Вы можете скачать его здесь:
Это должно упростить отладку UIWebView :)
Спасибо. Это было чрезвычайно полезно, так как я пришел из MonoTouch и еще не очень хорошо разбирался в Objective C.
Это как я использую ваше решение в MonoTouch.
Многообещающе. Однако мне не удалось открыть URL-адрес отладки в моем браузере.
Мне нравится это решение. Я просто хочу, чтобы было проще получить трассировку стека исключений. По крайней мере, с этим вы получите функцию и номер строки. (хотя номер строки кажется смещенным на единицу - с нуля?)
В некоторых случаях более простым решением может быть просто добавить Firebug Lite на веб-страницу.
Один из способов, который работает во время разработки, если у вас есть Safari v 6+ (я не уверен, какая версия iOS вам нужна), - это использовать инструменты разработки Safari и подключиться к UIWebView через него.
Это должен быть принятый ответ, это намного лучшее решение, чем другие :)
Я хотел бы добавить, что это также работает в вашем симуляторе iOS. Он будет отображаться как пользовательский агент iOS Simulator.
См. Обработку исключений в iOS7: http://www.bignerdranch.com/blog/javascriptcore-example/
[context setExceptionHandler:^(JSContext *context, JSValue *value) {
NSLog(@"%@", value);
}];
Привет, Йоав, добро пожаловать в Stack Overflow. Вы можете найти это полезным: stackoverflow.com/help/how-to-answer. Пожалуйста, подумайте о том, чтобы сделать ваш ответ более полезным для читателей: «Ссылки на внешние ресурсы приветствуются, но, пожалуйста, добавьте контекст вокруг ссылки, чтобы ваши коллеги-пользователи имели некоторое представление о том, что это такое и почему она существует. Всегда цитируйте наиболее релевантную часть важная ссылка на случай, если целевой сайт недоступен или постоянно отключен ». «Краткость приемлема, но более полные объяснения лучше».
Первая настройка 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);
}
}
}];
Я где-то нашел это, но, похоже, это не работает для меня в 2.1. Предполагая, что это методы, добавленные в подкласс UIWebView.