Come salvare il contenuto in UIWebView per caricare più velocemente al prossimo avvio?

So che ci sono alcune classi di cache introdotte recentemente nell’sdk di iphone, e c’è anche un TTURLRequest dalla libreria di three20 che ti permette di memorizzare una richiesta in una URL. Tuttavia, poiché sto caricando la pagina Web in UIWebView chiamando loadRequest di UIWebView, tali tecniche non sono realmente applicabili.

Qualche idea su come posso salvare una pagina web in modo che al prossimo avvio dell’app, non devo recuperare di nuovo dal web per la pagina intera? La pagina stessa ha già un meccanismo ajax che aggiorna automaticamente le parti di se stesso.

Ci sono un sacco di articoli sul modo in cui funziona la cache di UIWebView e la sensazione globale è che anche se alcuni meccanismi sembrano funzionare correttamente con MacOS X, gli stessi approcci potrebbero avere un comportamento curioso sotto iPhone.


TUTTAVIA, lo sto facendo giocando con la cache globale a cui si accede da qualsiasi NSURLConnection , incluso UIWebView . E nel mio caso, funziona;).

Quello che devi capire è il stream globale:

  • TU -> loadRequest su UIWebView
  • Questo entra in NSURLCache per chiedere “c’è qualcosa nella cache per questa richiesta?”:
 - (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request 

Da qui, ecco cosa faccio per gestire la cache sul disco, al mio fianco, per accelerare il carico di un UIWebView:

  • Sottoclassi NSURLCache e sovrascrivi il controllo get sul selettore di -(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
  • Reimplementa questo selettore in modo tale che se non è stato scritto nulla sul FS per questa richiesta (senza cache), quindi fai la richiesta dalla tua parte e archivia il contenuto su FS. Altrimenti, restituisci ciò che è stato precedentemente memorizzato nella cache.
  • Crea un’istanza della sottoclass e impostala sul sistema in modo che venga utilizzata dall’applicazione

Ora il codice:

MyCache.h

 @interface MyCache : NSURLCache { } @end 

MyCache.m

 @implementation MyCache -(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSLog(@"CACHE REQUEST S%@", request); NSString *documentsDirectory = [paths objectAtIndex:0]; NSArray* tokens = [request.URL.relativePath componentsSeparatedByString:@"/"]; if (tokens==nil) { NSLog(@"ignoring cache for %@", request); return nil; } NSString* [email protected]""; for (int i=0; i<[tokens count]-1; i++) { pathWithoutRessourceName = [pathWithoutRessourceName stringByAppendingString:[NSString stringWithFormat:@"%@%@", [tokens objectAtIndex:i], @"/"]]; } NSString* absolutePath = [NSString stringWithFormat:@"%@%@", documentsDirectory, pathWithoutRessourceName]; NSString* absolutePathWithRessourceName = [NSString stringWithFormat:@"%@%@", documentsDirectory, request.URL.relativePath]; NSString* ressourceName = [absolutePathWithRessourceName stringByReplacingOccurrencesOfString:absolutePath withString:@""]; NSCachedURLResponse* cacheResponse = nil; //we're only caching .png, .js, .cgz, .jgz if ( [ressourceName rangeOfString:@".png"].location!=NSNotFound || [ressourceName rangeOfString:@".js"].location!=NSNotFound || [ressourceName rangeOfString:@".cgz"].location!=NSNotFound || [ressourceName rangeOfString:@".jgz"].location!=NSNotFound) { NSString* storagePath = [NSString stringWithFormat:@"%@/myCache%@", documentsDirectory, request.URL.relativePath]; //this ressource is candidate for cache. NSData* content; NSError* error = nil; //is it already cached ? if ([[NSFileManager defaultManager] fileExistsAtPath:storagePath]) { //NSLog(@"CACHE FOUND for %@", request.URL.relativePath); content = [[NSData dataWithContentsOfFile:storagePath] retain]; NSURLResponse* response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:@"" expectedContentLength:[content length] textEncodingName:nil]; cacheResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:content]; } else { //trick here : if no cache, populate it asynchronously and return nil [NSThread detachNewThreadSelector:@selector(populateCacheFor:) toTarget:self withObject:request]; } } else { NSLog(@"ignoring cache for %@", request); } return cacheResponse; } -(void)populateCacheFor:(NSURLRequest*)request { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); //NSLog(@"PATH S%@", paths); NSString *documentsDirectory = [paths objectAtIndex:0]; NSArray* tokens = [request.URL.relativePath componentsSeparatedByString:@"/"]; NSString* [email protected]""; for (int i=0; i<[tokens count]-1; i++) { pathWithoutRessourceName = [pathWithoutRessourceName stringByAppendingString:[NSString stringWithFormat:@"%@%@", [tokens objectAtIndex:i], @"/"]]; } NSString* absolutePath = [NSString stringWithFormat:@"%@/myCache%@", documentsDirectory, pathWithoutRessourceName]; //NSString* absolutePathWithRessourceName = [NSString stringWithFormat:@"%@%@", documentsDirectory, request.URL.relativePath]; //NSString* ressourceName = [absolutePathWithRessourceName stringByReplacingOccurrencesOfString:absolutePath withString:@""]; NSString* storagePath = [NSString stringWithFormat:@"%@/myCache%@", documentsDirectory, request.URL.relativePath]; NSData* content; NSError* error = nil; NSCachedURLResponse* cacheResponse = nil; NSLog(@"NO CACHE FOUND for %@", request.URL); //NSLog(@"retrieving content (timeout=%f) for %@ ...", [request timeoutInterval], request.URL); content = [NSData dataWithContentsOfURL:request.URL options:1 error:&error]; //NSLog(@"content retrieved for %@ / error:%@", request.URL, error); if (error!=nil) { NSLog(@"ERROR %@ info:%@", error, error.userInfo); NSLog(@"Cache not populated for %@", request.URL); } else { NSURLResponse* response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:@"" expectedContentLength:[content length] textEncodingName:nil]; cacheResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:content]; //the store is invoked automatically. [[NSFileManager defaultManager] createDirectoryAtPath:absolutePath withIntermediateDirectories:YES attributes:nil error:&error]; BOOL ok;// = [[NSFileManager defaultManager] createDirectoryAtPath:absolutePath withIntermediateDirectories:YES attributes:nil error:&error]; ok = [content writeToFile:storagePath atomically:YES]; NSLog(@"Caching %@ : %@", storagePath , [email protected]"OK":@"KO"); } [pool release]; } @end 

E l'uso di esso nella tua applicazione:

 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString* documentsDirectory = [paths objectAtIndex:0]; NSString* diskCachePath = [NSString stringWithFormat:@"%@/%@", documentsDirectory, @"myCache"]; NSError* error; [[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath withIntermediateDirectories:YES attributes:nil error:&error]; MyCache* cacheMngr = [[MyCache alloc] initWithMemoryCapacity:10000 diskCapacity:100000000 diskPath:diskCachePath]; [NSURLCache setSharedURLCache:cacheMngr]; 

Questo codice merita un bel po 'di pulizia ... ma le cose principali dovrebbero essere lì. Ho avuto un sacco di problemi per farlo funzionare, spero che questo aiuti.

Recentemente ho trovato questo progetto sotto github: http://github.com/rs/SDURLCache L’approccio è esattamente lo stesso della mia risposta precedente qui descritta Come salvare il contenuto in UIWebView per caricare più velocemente al prossimo avvio? , ma il codice sembra più shiny, quindi forse ha senso provarlo.

Se la pagina ha già AJAX, perché non memorizzare il JavaScript / HTML nel pacchetto di applicazioni per avviarlo piuttosto che scaricarlo al primo avvio? Quindi caricare la pagina con il codice Corey fornito di seguito e lasciare che l’handle AJAX colpisca la rete per le parti aggiornate della pagina.

È ansible salvare un HTML nella directory dei documenti e caricare la pagina direttamente dalla directory dei documenti all’avvio.

Per salvare il contenuto della visualizzazione Web: lettura del contenuto HTML da una UIWebView

Caricare:

  NSString* path = [[NSBundle mainBundle] pathForResource:@"about" ofType:@"html"]; NSURL* url = [NSURL fileURLWithPath:path]; NSURLRequest* request = [NSURLRequest requestWithURL:url]; [webView loadRequest:request];