open - ¿Cómo se puede notificar a mi iPhone código Objective-C de los errores de Javascript en un UIWebView?
web view swift 4 (10)
Ahora he encontrado una forma de usar los ganchos del depurador de scripts en WebView (nota, NO UIWebView). Primero tuve que subclasificar UIWebView y agregar un método como este:
- (void)webView:(id)webView windowScriptObjectAvailable:(id)newWindowScriptObject {
// save these goodies
windowScriptObject = newWindowScriptObject;
privateWebView = webView;
if (scriptDebuggingEnabled) {
[webView setScriptDebugDelegate:[[YourScriptDebugDelegate alloc] init]];
}
}
A continuación, debe crear una clase YourScriptDebugDelegate que contenga métodos como estos:
// 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]);
}
Probablemente exista un gran impacto en el tiempo de ejecución, ya que el delegado de depuración también puede proporcionar los métodos que se deben invocar para ingresar y salir de un marco de pila, y para ejecutar cada línea de código.
Consulte http://www.koders.com/noncode/fid7DE7ECEB052C3531743728D41A233A951C79E0AE.aspx para la definición Objective-C ++ de WebScriptDebugDelegate.
Esos otros métodos:
// 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;
Tenga en cuenta que todo esto está oculto en un marco privado, así que no intente poner esto en el código que envía a la tienda de aplicaciones, y prepárese para algunos hackers para que funcione.
Necesito que mi código de iPhone Objective-C detecte errores de Javascript en un UIWebView. Eso incluye excepciones no detectadas, errores de sintaxis al cargar archivos, referencias de variables indefinidas, etc.
Esto es para un entorno de desarrollo, por lo que no necesita ser SDK-kosher. De hecho, solo necesita funcionar en el simulador.
Ya he descubierto que usé algunos de los trucos ocultos de WebKit para, por ejemplo, exponer objetos Obj-C a JS e interceptar ventanas emergentes de alerta, pero este todavía me está eludiendo.
[NOTA: después de publicar esto encontré una forma de usar un delegado de depuración. ¿Hay alguna manera de reducir la sobrecarga usando la consola de error / inspector web?]
He hecho esto en el firmware 1.x pero no en el 2.x. Este es el código que utilicé en 1.x, al menos debería ayudarte en tu camino.
// 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];
}
Creé un reportero de errores kosher de SDK que incluye:
- El mensaje de error
- El nombre del archivo en el que ocurre el error
- El número de línea en el que ocurre el error
- La pila de llamadas de JavaScript, incluidos los parámetros pasados
Es parte del marco QuickConnectiPhone disponible del proyecto sourceForge
Incluso hay una aplicación de ejemplo que muestra cómo enviar un mensaje de error a la terminal de Xcode.
Todo lo que necesita hacer es rodear su código JavaScript, incluidas las definiciones de funciones, etc. con try catch. Debe tener un aspecto como este.
try{
//put your code here
}
catch(err){
logError(err);
}
No funciona muy bien con los errores de compilación, pero funciona con todos los demás. Incluso funciones anónimas.
El blog de desarrollo está aquí y contiene enlaces a la wiki, sourceForge, el grupo de google y Twitter. Tal vez esto te ayude.
Straight Forward Way: coloque este código en la parte superior de su controlador / vista que está usando 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
Y luego instanciarlo así:
#ifdef DEBUG
myWebview = [[DebugWebView alloc] initWithFrame:frame];
#else
myWebview = [[UIWebView alloc] initWithFrame:frame];
#endif
Usar #ifdef DEBUG garantiza que no se incluya en la versión de lanzamiento, pero también recomendaría comentarlo cuando no lo estés usando, ya que tiene un impacto en el rendimiento. El crédito es para Robert Sanders y Prcela por el código original
Además, si usa ARC, puede que necesite agregar "-fno-objc-arc" para evitar algunos errores de compilación.
Creé una pequeña y agradable categoría que puedes agregar a tu proyecto ... Se basa en la solución de Robert Sanders. Prestigio.
Puedes descargarlo aquí:
Esto debería hacer mucho más fácil depurarlo UIWebView :)
Una solución más simple para algunos casos podría ser simplemente agregar Firebug Lite a la página web.
Una forma que funciona durante el desarrollo si tienes Safari v 6+ (no estoy seguro de qué versión de iOS necesitas) es usar las herramientas de desarrollo de Safari y enganchar en UIWebView a través de él.
- En Safari: habilite el menú Desarrollar (Preferencias> Avanzado> Mostrar el menú Desarrollo en la barra de menús)
- Conecte su teléfono a la computadora a través del cable.
- Artículo de lista
- Cargue la aplicación (ya sea a través de xcode o simplemente ábrala) y vaya a la pantalla que desea depurar.
- De vuelta en Safari, abra el menú Desarrollo, busque el nombre de su dispositivo en ese menú (el mío se llama iPhone 5), debe estar justo debajo de User Agent.
- Selecciónelo y verá un menú desplegable de las vistas web actualmente visibles en su aplicación.
- Si tiene más de una vista web en la pantalla, puede tratar de diferenciarlas al pasar el nombre de la aplicación en el menú de desarrollo. El UIWebView correspondiente se pondrá azul.
- Seleccione el nombre de la aplicación, se abre la ventana de desarrollo y puede inspeccionar la consola. Incluso puedes emitir comandos JS a través de él.
Primero configure WebViewJavascriptBridge , luego anule la función console.error.
En javascript
window.originConsoleError = console.error;
console.error = (msg) => {
window.originConsoleError(msg);
bridge.callHandler("sendConsoleLogToNative", {
action:action,
message:message
}, null)
};
En 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);
}
}
}];
Utilicé la gran solución propuesta por Robert Sanders: ¿cómo puede mi código iPhone Objective-C ser notificado de los errores de Javascript en un UIWebView?
Ese gancho para webkit funciona bien también en iPhone . En lugar de UIWebView estándar, asigné MyUIWebView derivado. También necesitaba definir clases ocultas dentro de MyWebScriptObjectDelegate.h:
@class WebView;
@class WebFrame;
@class WebScriptCallFrame;
Dentro del ios sdk 4.1 la función:
- (void)webView:(id)webView windowScriptObjectAvailable:(id)newWindowScriptObject
está en desuso y en lugar de usar la función:
- (void)webView:(id)sender didClearWindowObject:(id)windowObject forFrame:(WebFrame*)frame
Además, recibo algunas advertencias molestas como "NSObject puede no responder -WindowScriptObject" porque la interfaz de clase está oculta. Los ignoro y funciona bien.
Consulte el manejo de excepciones en iOS7: http://www.bignerdranch.com/blog/javascriptcore-example/
[context setExceptionHandler:^(JSContext *context, JSValue *value) {
NSLog(@"%@", value);
}];