2012-02-14 8 views
5

Estoy tratando de escribir una aplicación que utiliza una gran cantidad de script java dentro de un UIWebView. Y parte de este script java se carga dinámicamente (con jquery) cuando es necesario.cargando archivos javascript dinámicamente en UIWebView con el caché local no funciona

Los archivos index.html y jquery se cargan como se esperaba pero no en el archivo code.js. El archivo es code.js solicitud como esta (cortado con tijeras del código java script de index.html):

function require(moduleName,filePath) { 
    var uri = filePath; 
    jQuery.ajax({ 
     type: "GET", 
     url: uri,   
     async: false, 
     dataType: "script", 
     success: function(content) { 
      console.log("Receive content of "+moduleName); 
     }, 
     error: function(XMLHttpRequest, textStatus, errorThrown) {  
      console.log("Error content of "+moduleName); 
      console.log("Status: " + textStatus); 
      console.log("Thrown error: " + errorThrown); 
      console.log("h" + XMLHttpRequest.getAllResponseHeaders()); 
      console.log(XMLHttpRequest.responseText); 
     } 
    }); 
} 

function start() { 
    require("test", "code.js"); 
} 

la función start() se llama en el proceso de carga de la etiqueta del cuerpo de la index.html. El archivo de code.js sólo contiene los siguientes tres líneas de código:

alert("Loaded file syncronosly"); 
// HEllo 
alert("second"); 

La salida que recibo de este código es el siguiente (he código adicional alguna js que reenvía el console.log llamadas a la consola, que Xcode omití):

UIWebView console: Error content of test 
UIWebView console: Status: error 
UIWebView console: Thrown error: 
UIWebView console: h 
UIWebView console: HTTP Error (0 error). 
UIWebView console: alert("Loaded file syncronosly"); 
// HEllo 
alert("second"); 

me sale este comportamiento tan pronto como tratar de devolver el contenido de code.js en la clase NSURLCache anulado. La idea al final es tener una aplicación cuya parte se ejecuta en un UIWebView sin la necesidad de cargar el contenido desde un servidor web externo.

El código abreviada de la clase caché (LocalSubstitutionCache):

NSString *pathString = [[request URL] absoluteString]; 
NSLog(@"request => %@", pathString); 

NSString* fileToLoad = nil; 
if ([pathString isEqualToString:@"http://example.com/index.html"]) { 
    fileToLoad = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"]; 
} 
else if ([pathString hasPrefix:@"http://example.com/code.js"]) { 
    fileToLoad = [[NSBundle mainBundle] pathForResource:@"code" ofType:@"js"]; 
} 
else if ([pathString isEqualToString:@"http://example.com/jquery-1.6.2.min.js"]) { 
    fileToLoad = [[NSBundle mainBundle] pathForResource:@"jquery-1.6.2.min" ofType:@"js"]; 
} 

if (!fileToLoad) { 
    // No local data needed for this request let super handle it: 
    return [super cachedResponseForRequest:request]; 
} 

NSData *data = [NSData dataWithContentsOfFile:fileToLoad]; 

NSURLResponse *response = 
    [[NSURLResponse alloc] 
     initWithURL:[request URL] 
     MIMEType:[self mimeTypeForPath:pathString] 
     expectedContentLength:[data length] 
     textEncodingName:nil]; 
cachedResponse = 
    [[NSCachedURLResponse alloc] initWithResponse:response data:data]; 

(El código de caché es de aquí: http://cocoawithlove.com/2010/09/substituting-local-data-for-remote.html)

Ahora en mi controlador de vista (que contiene una vista web) hago lo siguiente:

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    // This line loads the content with the cache enabled (and does not work): 
    [_webview loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://example.com/index.html"]]]; 
} 

Ahora lo interesante es que cuando reemplace el código en el método viewDidLoad con el siguiente código:

- (void) viewDidLoad { 
    [super viewDidLoad]; 

    // This two line load the content without the cache: 
    NSString* fileToLoad = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"]; 
    [_webview loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:fileToLoad]]]; 
} 

Ahora todo funciona bien y aparece mi alerta. Entonces, sospecho que lo que estoy devolviendo en el caché no es lo mismo que lo que devuelve la solicitud original cuando el caché lo atrapa.

Esto es lo que han dado de alta para:

  • No utilizar direcciones URL de archivos para el caso de trabajo. Mi código original obtiene el código no almacenado en caché de un servidor web. Pero para el proyecto de ejemplo, estoy usando URL de archivos ya que esto simplifica el proyecto.
  • Tipo de contenido devuelto. Es en ambos casos texto/javascript
  • Conjunto de encabezados especiales. He consultado con un proxy http los encabezados que se configuran en la solicitud que no está en la memoria caché. No hay conjuntos de encabezados especiales.

Mi mayor problema es que la devolución de llamada por error jquery no devuelve ninguna información de error útil. Acabo de obtener un resultado de error para el argumento textStatus.

Puede descargar el proyecto de ejemplo desde aquí: http://dl.dropbox.com/u/5426092/LocalCacheJsInjectTest.zip

espero que alguien me puede ayudar aquí

Respuesta

11

Después de algo más de trabajo más he encontrado la solución a mi problema.Y responde aquí con lo que encontré.

La solución consiste en crear una implementación de protocolo NSURL personalizada y registrarla en la aplicación como controlador para el protocolo http. El código es el siguiente (he omitido algunas manipulación en el código de error):

#import "MyHttpURLProtocol.h" 

@implementation MyHttpURLProtocol 

+ (BOOL)canInitWithRequest:(NSURLRequest *)request { 
    return [[[request URL] scheme] isEqualToString:@"http"]; // we handle http requests 
} 

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { 
    return request; 
} 

- (void) startLoading { 
    id<NSURLProtocolClient> client = [self client]; 
    NSURLRequest* request = [self request]; 
    NSString *pathString = [[request URL] absoluteString]; 

    NSString* fileToLoad = nil; 
    if ([pathString isEqualToString:@"http://example.com/index.html"]) { 
     fileToLoad = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"]; 
    } 
    else if ([pathString hasPrefix:@"http://example.com/code.js"]) { 
     fileToLoad = [[NSBundle mainBundle] pathForResource:@"code" ofType:@"js"]; 
    } 
    else if ([pathString isEqualToString:@"http://example.com/jquery-1.6.2.min.js"]) { 
     fileToLoad = [[NSBundle mainBundle] pathForResource:@"jquery-1.6.2.min" ofType:@"js"]; 
    } 

    NSData* data = [NSData dataWithContentsOfFile:fileToLoad]; 

    NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] initWithURL:[request URL] statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:[NSDictionary dictionary]]; 

    [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; 
    [client URLProtocol:self didLoadData:data]; 
    [client URLProtocolDidFinishLoading:self]; 
} 

- (void) stopLoading {} 

@end 

Este código permite ahora a cargar el archivo index.html estar accediendo al siguiente código:

[_webview loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://example.com/index.html"]]]; 

Y no olvide registrar el protocolo personalizado en su delegado de aplicación:

[NSURLProtocol registerClass: [TOHttpURLProtocol class]];

[NSURLProtocol registerClass:[MyHttpURLProtocol class]]; 

Es sorprendente lo simple que puede ser la solución después de haberla encontrado. Aquí está el enlace al ejemplo de proyecto actualizado: http://dl.dropbox.com/u/5426092/LocalCacheJsInjectTest-working.zip

Y por el amor completo aquí está el enlace a la entrada del blog que me ayudó a encontrar la solución: http://www.infinite-loop.dk/blog/2011/09/using-nsurlprotocol-for-injecting-test-data/

+0

El NSHTTPURLResponse initWithURL: statusCode: método está disponible desde 5.0 aunque no aparece en la documentación. Esto es raro, ¿podemos usarlo? – groumpf

+0

Supuse que podemos usar este método tal como fue agregado y está en el archivo de encabezado visible normal para esta clase. Pero hasta ahora no tengo ninguna aplicación con este código en la tienda de aplicaciones por lo que podría ser diferente. – Chris

+0

He enviado mi aplicación con éxito utilizando el método mencionado. No hay problema con eso – Chris

Cuestiones relacionadas