Es cierto que la UIWebViewDelegate no supervisar las solicitudes realizadas mediante el uso de un XMLHttpRequest? Si es así, ¿hay una manera de controlar este tipo de peticiones?

por ejemplo, UIWebViewDelegate no atrapar a este 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();
InformationsquelleAutor Thys | 2011-03-18

5 Comentarios

  1. 80

    Pregunta interesante.

    Hay dos partes para hacer este trabajo: un controlador de JavaScript y UIWebView los métodos de delegado. En JavaScript, se puede modificar el prototipo de los métodos para activar los eventos cuando una petición AJAX se crea. Con nuestro UIWebView delegado, podemos capturar estos eventos.

    Controlador De JavaScript

    Necesitamos para ser notificado cuando una petición AJAX se hace. He encontrado la solución aquí.

    En nuestro caso, para hacer que el código de trabajo, me pongo el siguiente código JavaScript en un recurso llamado ajax_handler.js que se incluye con la 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 va a hacer es cambiar la ubicación del navegador para algunos compone esquema de URL (en este caso, mpAjaxHandle) con información sobre la petición formulada. No te preocupes, nuestro delegado, con la captura de este y la ubicación no va a cambiar.

    UIWebView Delegado

    Primero, necesitamos leer nuestro archivo JavaScript. Me sugieren hacer almacenar en una variable estática. Estoy en 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 inyectar este JavaScript antes de que una 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.

    Desde el Esquema de URL se compone, no queremos realmente siguen. Volvemos 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;
    }

    He creado un proyecto de ejemplo con la solución, pero no tienen lugar para el anfitrión. Usted puede enviarme un mensaje si puedes alojarlo y voy a editar este post en consecuencia.

    • aunque este no permite la edición de la solicitud, que no era mi pregunta. Así que gracias! 🙂
    • Tenga cuidado de que XCode no agrega automáticamente los archivos con las extensiones .js a la agrupación. Usted necesita agregar el archivo de forma manual.
    • Hola Mike – Cualquier Posibilidad de que usted podría compartir el proyecto de ejemplo con mí? Lo agradecería muchísimo
    • Usted me ha ayudado mucho, este fue un gran problema para mí. Ahora todo funciona perfectamente 🙂 🙂 gracias También por tu super detallada explicación, tú eres el hombre! 😉
    • Esta pregunta está relacionada con: stackoverflow.com/questions/12563311/…
    • Si usted tiene problemas para conseguir este trabajo puede ser debido a Xcode está tratando de compilar el código JS. Más info sobre esto en stackoverflow.com/a/3629479/196420
    • El javascript deja de funcionar después de regresar de otro sitio (en mi caso, para la autenticación de terceros) e incluso después de la re-inyección de la js. Funciona cuando se inyectan en webViewDidFinishLoad.
    • Esta pregunta es viejo, pero si usted todavía tiene el código que se debe publicar en GitHub
    • Nota sólo me golpeó el JS de la muestra directamente en un NSString para probar y no funciona en un primer momento. He tenido que añadir una ; después de la final } de XMLHttpRequest.el prototipo.enviar/abrir asignación.
    • Nota también he añadido un if(!s_ajaxListener) alrededor de todo el lote se continuó el trabajo, incluso si el JS se ejecuta en/evaluados dos veces.
    • No estoy seguro de por qué controlador se inyecta en webViewDidStartLoad, no debe ser en webViewDidFinishLoad, después de que se carga la página?
    • Así que tengo una petición que viene por ahí el asunto y el cuerpo se establecen así: xmlhttp.send({ subject:"This is the subject", body:JSON.stringify(jsonString)}); Esta asignado aquí: if(s_ajaxListener.method.toLowerCase() == 'post')s_ajaxListener.data = a; Pero me parece que no puede averiguar cómo acceder a este data de la propiedad en el request pasado. Alguna idea?
    • Necesito ampliar esta en 2 formas: 1) Agregar un encabezado personalizado con un valor dinámico (que Swift sabe, pero la estática JS no) para cada solicitud y 2) Modificar la URL de la ruta de acceso para agregar un prefijo static para cada solicitud antes de la emisión. ¿Alguien sabe cómo hacerlo?
    • La solución que me dio algunos problemas con la animación. Parece que la ventana.cambio de ubicación de los causados animaciones de stop. Afortunadamente, usted puede hacer un truco muy similar a esto: tener un iframe hacer la carga. Se activará el delegado de los métodos, y no se detiene la página principal de la animación. También, creo que este proyecto implementado con un iframe, podría ser útil para algunos. github.com/tcoulter/jockeyjs

  2. 20

    Puede utilizar un NSURLProtocol. Por ejemplo, si usted llama XMLHttpRequest con http://localhost/path se puede manejar con los siguientes:

    @interface YourProtocol: NSURLProtocol

    Luego de la aplicació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
    {
    }

    Usted necesita para registrar el protocolo de la siguiente manera:

        [NSURLProtocol registerClass:[YourProtocol class]];
    • El problema aquí es que se pierde todo y cualquier referencia a su UIWebView a menos que usted mantenga una lista, e incluso entonces, que no es 100% confiable
    • en realidad esto es mucho healtier sollution comparado con el otro (que es muy hackish). Usted puede hacer una referencia de la clase en algún lugar de su UIWebView ejemplo, si usted necesita… Este método también es mucho más capaz (si usted quería más que de supervisión)
    • Vale la pena mencionar que sería necesario llamar a [auto.cliente URLProtocolDidFinishLoading:auto]; en startLoading para la solicitud completa. (De lo contrario se acaba el tiempo de espera en el javascript final.)
    • Más correctamente sería necesario llamar a algo como [auto.cliente URLProtocol:auto didReceiveResponse:[NSURLResponse nuevo] cacheStoragePolicy:NSURLCacheStorageNotAllowed]; then [auto.cliente URLProtocolDidFinishLoading:auto]; en startLoading para la solicitud completa.
  3. 3

    Por la aplicación y el registro de una subclase de NSURLProtocol puede capturar todas la solicitud de su UIWebView. Puede ser una exageración, en algunos casos, pero si usted no es capaz de modificar el código javascript en realidad se va a ejecutar es su mejor apuesta.

    En mi caso necesito para la captura de todas las solicitudes y de inserción específica de un encabezado HTTP para cada uno de ellos.
    He hecho esto mediante la implementación de NSURLProtocol, registrarse utilizando registerClass y respondiendo que SÍ, en mi subclase de a + (BOOL)canInitWithRequest:(NSURLRequest *)solicitud si la solicitud corresponde a la Url de la que estoy interesado.
    Entonces usted tiene que aplicar los otros métodos del protocolo de esto se puede hacer mediante el uso de un NSURLConnection, la configuración de la clase de protocolo como el delegado y la reorientación de los métodos de delegado de NSURLConnection a NSURLProtocolClient

    • Específicamente, si lo que quieres es sólo para el monitor de la solicitud, (sin modificarlo). Esto es muy fácil: 1) crear una subclase de NSURLProtocol 2) implementar + (BOOL)canInitWithRequest:(NSURLRequest *)solicitud de { return NO;} 3) registrar su protocolo de uso de [NSURLProtocol registerClass:lShareSwymURLProtocol]; en la implementación en el paso 2) usted puede agregar cualquier código que desea después de la inspección de la solicitud. sólo asegúrese de NO volver si desea que el estándar de carga mecanismo para continuar
  4. 1

    Parece ser cierto. No hay forma de controlar lo que un UIWebView está haciendo más allá de lo que UIWebViewDelegate proporciona, a menos que tal vez usted puede encontrar una manera de utilizar stringByEvaluatingJavaScriptFromString: inyectar un poco de Javascript para hacer lo que usted necesita.

  5. 0

    Como otros pueblos mencionados, UIWebViewDelegate observa los cambios de la ventana.ubicación y iframe.src sólo.

    En caso de que se quiera usar la url personalizada esquema de iOS solo,
    usted puede tomar ventaja de <iframe> como de esta manera.

    Por ejemplo, si desea llamar a un Esquema de URL como esta

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

    en su 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});

Dejar respuesta

Please enter your comment!
Please enter your name here