Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>There are a bunch of articles about the way the cache of the UIWebView works and the global feeling is that even if some mechanisms seems to work OK under MacOS X, the same approaches may have curious behavior under iPhone.</p> <hr> <p><strong>HOWEVER,</strong> I'm doing it by playing with the global cache that is accessed by any <code>NSURLConnection</code>, <code>UIWebView</code> included. <strong>And in my case, it works ;).</strong></p> <p>What you need to understand is the global flow:</p> <ul> <li>YOU -> <code>loadRequest</code> on a <code>UIWebView</code></li> <li>This goes into <code>NSURLCache</code> to ask "is there something cached for this request?":</li> </ul> <pre><code>- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request</code></pre> <p>From that, here's what I do to handle the cache on the disk, on my side, to speed up the load of a UIWebView:</p> <ul> <li>Subclass the <code>NSURLCache</code> and override the get control over the <code>-(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request</code> selector</li> <li>Reimplement this selector in such a way that if nothing has been written on the FS for this request (no cache), then do the request on your side and store the content on FS. Otherwise, return what has been previously cached.</li> <li>Create an instance of your subclass and set it to the system so that it is used by your application</li> </ul> <hr> <p>Now the code :</p> <h3>MyCache.h</h3> <pre><code>@interface MyCache : NSURLCache { } @end </code></pre> <h3>MyCache.m</h3> <pre><code>@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* pathWithoutRessourceName=@""; for (int i=0; i&lt;[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* pathWithoutRessourceName=@""; for (int i=0; i&lt;[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:&amp;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:&amp;error]; BOOL ok;// = [[NSFileManager defaultManager] createDirectoryAtPath:absolutePath withIntermediateDirectories:YES attributes:nil error:&amp;error]; ok = [content writeToFile:storagePath atomically:YES]; NSLog(@"Caching %@ : %@", storagePath , ok?@"OK":@"KO"); } [pool release]; } @end </code></pre> <p>And the use of it in your application:</p> <pre><code>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:&amp;error]; MyCache* cacheMngr = [[MyCache alloc] initWithMemoryCapacity:10000 diskCapacity:100000000 diskPath:diskCachePath]; [NSURLCache setSharedURLCache:cacheMngr]; </code></pre> <p>This code deserves a lot of cleanup.. but the main things should be in there. I had a lot of trouble to get this working, hope this helps.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload