UIWebViewDelegate no monitorea XMLHttpRequest?

¿Es cierto que UIWebViewDelegate no supervisa las solicitudes realizadas mediante el uso de XMLHttpRequest? Si es así, ¿hay alguna manera de controlar este tipo de solicitudes?

por ejemplo, UIWebViewDelegate no detecta esto en -(BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSMutableURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType ;

 var xhr = new XMLHttpRequest(); xhr.open("GET", "http://www.google.com", true); xhr.onreadystatechange=function() { if (xhr.readyState==4) { alert(xhr.responseText); } } xhr.send(); 

Interesante pregunta.

Hay dos partes para que esto funcione: un controlador de JavaScript y métodos de delegado de UIWebView. En JavaScript, podemos modificar los métodos prototipo para desencadenar eventos cuando se crea una solicitud AJAX. Con nuestro delegado de UIWebView, podemos capturar estos eventos.

Controlador de JavaScript

Necesitamos ser notificados cuando se realiza una solicitud de AJAX. Encontré la solución aquí .

En nuestro caso, para hacer que el código funcione, puse el siguiente JavaScript en un recurso llamado ajax_handler.js que se incluye con mi aplicación.

 var s_ajaxListener = new Object(); s_ajaxListener.tempOpen = XMLHttpRequest.prototype.open; s_ajaxListener.tempSend = XMLHttpRequest.prototype.send; s_ajaxListener.callback = function () { window.location='mpAjaxHandler://' + this.url; }; XMLHttpRequest.prototype.open = function(a,b) { if (!a) var a=''; if (!b) var b=''; s_ajaxListener.tempOpen.apply(this, arguments); s_ajaxListener.method = a; s_ajaxListener.url = b; if (a.toLowerCase() == 'get') { s_ajaxListener.data = b.split('?'); s_ajaxListener.data = s_ajaxListener.data[1]; } } XMLHttpRequest.prototype.send = function(a,b) { if (!a) var a=''; if (!b) var b=''; s_ajaxListener.tempSend.apply(this, arguments); if(s_ajaxListener.method.toLowerCase() == 'post')s_ajaxListener.data = a; s_ajaxListener.callback(); } 

Lo que esto realmente hará es cambiar la ubicación del navegador a un esquema de URL creado (en este caso, mpAjaxHandle ) con información sobre la solicitud realizada. No se preocupe, nuestro delegado captará esto y la ubicación no cambiará.

Delegado de UIWebView

Primero, necesitamos leer nuestro archivo JavaScript. Sugiero almacenarlo en una variable estática. Tengo el hábito de usar + inicializar.

 static NSString *JSHandler; + (void)initialize { JSHandler = [[NSString stringWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"ajax_handler" withExtension:@"js"] encoding:NSUTF8StringEncoding error:nil] retain]; } 

A continuación, queremos insertar este JavaScript antes de que la página se termine de cargar para que podamos recibir todos los eventos.

 - (void)webViewDidStartLoad:(UIWebView *)webView { [webView stringByEvaluatingJavaScriptFromString:JSHandler]; } 

Finalmente, queremos capturar el evento.

Como el esquema de URL está compuesto, no queremos seguirlo realmente. Devolvemos NO y todo está bien.

 #define CocoaJSHandler @"mpAjaxHandler" - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { if ([[[request URL] scheme] isEqual:CocoaJSHandler]) { NSString *requestedURLString = [[[request URL] absoluteString] substringFromIndex:[CocoaJSHandler length] + 3]; NSLog(@"ajax request: %@", requestedURLString); return NO; } return YES; } 

Creé un proyecto de muestra con la solución pero no tengo dónde alojarlo. Puede enviarme un mensaje si puede alojarlo y editaré esta publicación en consecuencia.

Puede usar un NSURLProtocol. Por ejemplo, si llama a XMLHttpRequest con http://localhost/path , puede manejarlo con lo siguiente:

 @interface YourProtocol: NSURLProtocol 

Luego para la implementación:

 + (BOOL)canInitWithRequest:(NSURLRequest *)request { return [request.URL.host isEqualToString:@"localhost"]; } + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request { return request; } - (void) startLoading { // Here you handle self.request } - (void)stopLoading { } 

Debe registrar el protocolo de la siguiente manera:

  [NSURLProtocol registerClass:[YourProtocol class]]; 

Al implementar y registrar una subclase de NSURLProtocol, puede capturar todas las solicitudes desde su UIWebView. Puede ser excesivo en algunos casos, pero si no puede modificar el javascript que se está ejecutando en realidad, es su mejor opción.

En mi caso, necesito capturar todas las solicitudes e insertar un encabezado HTTP específico para cada una de ellas. Lo he hecho implementando NSURLProtocol, registrándolo usando registerClass y respondiendo SÍ en mi subclase a + (BOOL) canInitWithRequest: (NSURLRequest *) solicito si la solicitud corresponde a las URL que me interesan. Luego debe implementar los otros métodos. del protocolo, esto se puede hacer utilizando una NSURLConnection, estableciendo la clase de protocolo como delegado y redireccionando los métodos de delegado de NSURLConnection a NSURLProtocolClient

Parece ser cierto. No hay forma de controlar qué hace UIWebView más allá de lo que proporciona UIWebViewDelegate, a menos que tal vez pueda encontrar una manera de usar stringByEvaluatingJavaScriptFromString: para inyectar Javascript para hacer lo que necesita.

Como otros pueblos mencionaron, UIWebViewDelegate observa los cambios de window.location y iframe.src solamente.

En caso de que solo desee utilizar el esquema de url personalizado de iOS solamente, puede aprovechar esta manera.

Por ejemplo, si desea llamar a un esquema de URL como este

 objc://my_action?arg1=1&arg2=2 

en tu archivo js:

 /** * @param msg : path of your query * @param data : arguments list of your query */ var call_url = function(msg, data){ const scheme = "objc"; var url = scheme + '://' + msg; if(data){ var pstr = []; for(var k in data) if(typeof data[k] != 'function') pstr.push(encodeURIComponent(k)+"="+encodeURIComponent(data[k])); url += '?'+pstr.join('&'); } var i = document.createElement("iframe"); i.src = url; i.style.opacity=0; document.body.appendChild(i); setTimeout(function(){i.parentNode.removeChild(i)},200); } //when you call this custom url scheme call_url ("my_action", {arg1:1,arg2:2});