iOS: pasar NSURL personalizado a NSURLProtocol

Necesito pasar información adicional junto con UIWebView loadRequest: para que llegue a mi implementación de NSURLProtocol . La información no puede vincularse a NSURLRequest porque la información también debe conservarse con NSURLRequest mainDocumentURL . Así que NSURL y construí NSURLRequest . NSURLRequest con él. Ya sabía que la NSURLRequest que alcanza NSURLProtocol startLoading NO es la instancia que he alimentado a UIWebView loadRequest , por lo que implementé NSURL copyWithZone también, esperando ingenuamente que el sistema de carga de URL lo use.

Ahora, NSURLProtocol canInitWithRequest se llama no una vez como uno razonablemente espera, sino al less 4 veces antes de startLoading . Primero 2 veces de eso, la NSURLRequest entrante todavía contiene mi implementación NSURL personalizada. Luego, un desafortunado código interno llamado CFURLCopyAbsoluteURL solicita el absoluteURL de mi NSURL personalizado y el siguiente canInitWithRequest (y el startLoading siguienteLoading) ya obtiene una NSURLRequest completamente nueva con NSURL nuevo en él. copyWithZone nunca se llama y mi NSURL subclasificada se pierde.

Antes de abandonar e implementar una solución inferior y frágil con el agregado de material directamente a la cadena de URL, me gustaría preguntar a los magos de nivel superior, ya sea que vean cómo capturar ese parpadeo inicial en el radar NSURLProtocol o cómo engañar CFURLCopyAbsoluteURL en llevar mi instancia personalizada. He intentado hackear NSURL absoluteURL volviendo de nuevo una nueva instancia de mi class NSURL personalizada, pero no ayudó. He visto algunas promises en la NSURLProtocol setProperty , pero ahora parece bastante inútil. El sistema de carga de URL crea nuevas instancias de todo NSURLRequest y NSURLRequest llegó en NSURLProtocol parece ser el mismo que ingresó en UIWebView solo por crash.

ACTUALIZACIÓN: ok, quería mantener la publicación lo más corta posible, pero incluso la primera respuesta es pedir antecedentes técnicos, así que aquí vamos: tengo varios UIWebView s en la aplicación. Estas vistas pueden ejecutar requestes simultáneamente y pueden ejecutar requestes para la misma URL. Es como tabs en el browser de escritorio. Pero necesito distinguir qué UIWebView fue el origen de cada NSURLRequest particular que llega al NSURLProtocol . Necesito un context que se lleve con cada request de URL. No puedo simplemente asignar las URL a los datos, porque varias UIWebViews pueden cargar la misma URL en cualquier momento.

ACTUALIZACIÓN 2: Se prefiere adjuntar la información de context a NSURL y, según mi entendimiento, es el único utilizable. El problema es que las requestes de resources referencedos dentro de la página (imágenes, etc.) no pasan por UIWebViewDelegate en absoluto y terminan directamente en NSURLProtocol . No tengo la oportunidad de tocar, inspeccionar o modificar dichas requestes en cualquier lugar antes de NSURLProtocol . El único enlace contextual para tales requestes es su NSURLRequest mainDocumentURL .

Si hay alguna forma de utilizar su NSURL original como mainDocumentURL , sería ideal. Si no hay forma de evitar que se copie, pensé en el siguiente hack como alternativa:

Antes de la creación de cada UIWebView , establezca la cadena del agente de usuario en un valor único. Supuestamente, este cambio solo afecta los objects UIWebView que se crean posteriormente, por lo que cada vista terminará con su propia cadena de agente de usuario distintivo.

En la implementación NSURLProtocol , puede verificar la cadena del agente de usuario para identificar la UIWebView asociada y pasarla al controller de protocolo real utilizando la cadena del agente de usuario real (para que el server no vea nada diferente).

Todo esto depende de las vistas que realmente terminan con diferentes cadenas UA. ¡Déjame saber si logras que funcione!

NSURLRequest que no puede ponerlo en NSURLRequest , pero no estoy claro por qué de su discusión actualizada. Ese sería el lugar más natural para ponerlo.

  • Implementar webView:shouldLoadWithRequest:navigationType:
  • Adjunte una propiedad adicional a la request proporcionada utilizando objc_setAssociatedObject . Luego regrese YES . (Sería bueno utilizar setProperty:forKey:inRequest: aquí, pero UIWebView nos pasa una request no mutable, por lo que solo podemos adjuntar objects asociados. Otra forma en que UIWebView es una pálida sombra de WebView de OS X, que puede manejar esta).
  • En el NSProtocol , lea su propiedad adicional usando objc_getAssociatedObject . La request debe ser la misma que se presentó anteriormente. Sugieres que este no es el caso. ¿Estás diciendo que la request en webView:shouldLoadWithRequest:navigationType: es diferente de la request en initWithRequest:cachedResponse:client: 😕

¿Me estoy perdiendo otro requisito o peculiaridad?

Puede pasar opciones a través de encabezados de request personalizados, suponiendo que el website o el proveedor de services de destino no quitan de alguna manera a los que están en tránsito.

El reto que se presentaría sería un esquema de encoding que puede codificarse razonablemente en una cadena ASCII para el valor del campo de cabecera y luego decodificarse en el valor real que desee. Para esto, un NSValueTransformer personalizado parecería más apropiado.

Yo tuve el mismo problema. Finalmente me atuve a la solución sugerida por Matthew (usando la cadena del agente de usuario). Sin embargo, como la solución no está desarrollada, agrego una nueva respuesta con más detalles. Además, descubrí que no es necesario enviar una request para hacer que el agente de usuario se "pegue". Es suficiente para get a través de javascript como se sugiere aquí .

Los siguientes pasos me funcionaron:

(1) Obtenga el agente de usuario pnetworkingeterminado actual. Lo necesita más tarde para volver a includelo en la request en NSURLProtocol. Debe utilizar una nueva security de webview, ya que get el agente de usuario hará que se adhiera a la vista web, por lo que no podrá cambiarlo más adelante.

 UIWebView* myWebview = [[UIWebView alloc] init]; NSString* defaultUserAgent = [myWebview stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"]; [myWebview release]; // no needed with ARC, but to emphasize, that the webview instance is not needed anymore 

(2) Cambie el valor en el estándarUserDefaults (tomado de aquí ).

 NSDictionary* userAgentDict = [NSDictionary dictionaryWithObjectsAndKeys:@"yourUserAgent", @"UserAgent", nil]; [[NSUserDefaults standardUserDefaults] registerDefaults:userAgentDict]; 

(3) Haga que la nueva cadena de agente de usuario se adhiera a su webView obteniéndola a través de javascript como se hizo en (1), pero esta vez en la instancia de webview con la que realmente trabaja.

(4) Revertir el agente de usuario pnetworkingeterminado en standardUserDefaults como se hace aquí .