Intercepting spawned URL requests from UIWebView and caching the response

  • Hi all,

    I'm trying to design a custom caching mechanism for a UIWebView. When the first URL request is loaded, it spawns further URL requests for loading graphics. As the user clicks links etc, it loads more URL requests.

    I can use a subclass of NSURLCache to intercept requests for cached data:

    @interface BFWURLCache : NSURLCache

    @end

    @implementation BFWURLCache

    - (NSCachedURLResponse*)cachedResponseForRequest:(NSURLRequest*)request
    {
    NSCachedURLResponse* cachedResponse = nil;
    NSString* cachePath = [self cachePathForRequest:request];
    if ([[NSFileManager defaultManager] fileExistsAtPath:cachePath])
    {
      DLog(@"using cached: %@", cachePath);
      NSURL* cacheURL = [NSURL fileURLWithPath:cachePath];
      NSURLResponse* urlResponse = [[NSURLResponse alloc] initWithURL:cacheURL MIMEType:nil expectedContentLength:1 textEncodingName:nil];
      cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:urlResponse data:[[NSFileManager defaultManager] contentsAtPath:cachePath]];
      [urlResponse release];
      [cachedResponse autorelease];
    }
    else
    {
      DLog(@"caching: %@", request.URL);
      [self queueRequest:request];
    }
    return cachedResponse;
    }

    @end

    Then in my AppDelegate, I tell the app to use my custom cache:

    [NSURLCache setSharedURLCache:[[[BFWURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil]] autorelease];

    That works fine for intercepting URL requests in any web view. If the corresponding file exists at cachePath, it is given as a response to the request.

    However, if the cachePath file does not already exist (ie has not been cached yet), my queueRequest method fires off a new NSURLConnection with the request, downloads the file into the cachePath. Since this takes time (in another thread), I don't have any cachedResponse ready to return from the cachedResponseForRequest call, so it just returns nil. The UIWebView sees the nil return (ie nothing cached) and so proceeds to load the URL itself. So now I have two processes loading the same URL: The web view and the custom caching mechanism.

    How can I intercept URL requests from UIWebViews, including spawned requests and customise the responses? Is overriding NSURLCache (as above) the only way? If so, how can I get at the data of the response when it is first loaded (so I don't have to download it separately)?

    Thanks,
    Tom
  • On Cocoa, You can intercept the WebView requests using a custom NSURLProtocol. There is a fairly complete example here:

    http://stackoverflow.com/questions/3155359/in-webkit-how-do-i-get-the-conte
    nt-of-a-resource


    On Jun 20, 2012, at 1:08 AM, BareFeetWare <list.developer...> wrote:

    > Hi all,
    >
    > I'm trying to design a custom caching mechanism for a UIWebView. When the first URL request is loaded, it spawns further URL requests for loading graphics. As the user clicks links etc, it loads more URL requests.
    >
    > I can use a subclass of NSURLCache to intercept requests for cached data:
    >
    > @interface BFWURLCache : NSURLCache
    >
    > @end
    >
    > @implementation BFWURLCache
    >
    > - (NSCachedURLResponse*)cachedResponseForRequest:(NSURLRequest*)request
    > {
    > NSCachedURLResponse* cachedResponse = nil;
    > NSString* cachePath = [self cachePathForRequest:request];
    > if ([[NSFileManager defaultManager] fileExistsAtPath:cachePath])
    > {
    > DLog(@"using cached: %@", cachePath);
    > NSURL* cacheURL = [NSURL fileURLWithPath:cachePath];
    > NSURLResponse* urlResponse = [[NSURLResponse alloc] initWithURL:cacheURL MIMEType:nil expectedContentLength:1 textEncodingName:nil];
    > cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:urlResponse data:[[NSFileManager defaultManager] contentsAtPath:cachePath]];
    > [urlResponse release];
    > [cachedResponse autorelease];
    > }
    > else
    > {
    > DLog(@"caching: %@", request.URL);
    > [self queueRequest:request];
    > }
    > return cachedResponse;
    > }
    >
    > @end
    >
    > Then in my AppDelegate, I tell the app to use my custom cache:
    >
    > [NSURLCache setSharedURLCache:[[[BFWURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil]] autorelease];
    >
    > That works fine for intercepting URL requests in any web view. If the corresponding file exists at cachePath, it is given as a response to the request.
    >
    > However, if the cachePath file does not already exist (ie has not been cached yet), my queueRequest method fires off a new NSURLConnection with the request, downloads the file into the cachePath. Since this takes time (in another thread), I don't have any cachedResponse ready to return from the cachedResponseForRequest call, so it just returns nil. The UIWebView sees the nil return (ie nothing cached) and so proceeds to load the URL itself. So now I have two processes loading the same URL: The web view and the custom caching mechanism.
    >
    > How can I intercept URL requests from UIWebViews, including spawned requests and customise the responses? Is overriding NSURLCache (as above) the only way? If so, how can I get at the data of the response when it is first loaded (so I don't have to download it separately)?
    >
    > Thanks,
    > Tom
  • > On Cocoa, You can intercept the WebView requests using a custom NSURLProtocol. There is a fairly complete example here:
    >
    > http://stackoverflow.com/questions/3155359/in-webkit-how-do-i-get-the-conte
    nt-of-a-resource


    Hi Jason,

    Thanks for your answer. Unfortunately, as you probably know, this doesn't help for my UIWebView on CocoaTouch (iOS).

    Thanks,
    Tom
  • On 20/06/2012, at 5:42 PM, 尹佳冀 <yinjiaji110...> wrote:

    >> The UIWebView sees the nil return (ie nothing cached) and so proceeds to load the URL itself. So now I have two processes loading the same URL: The web view and the custom caching mechanism

    > In this case you return a fake data, like <pre>fakeData = [[NSData dataWithBytes:" " length:1] retain];</pre>, webview will no to load this url, does this solution can help you?

    Thanks for your answer.

    True, if I feed empty data (ie not a nil) back to the web view from the cache request, the web view won't continue to load the URL. However, unless I can then ask the web view to instead load the cached version (after it downloads), I just get a blank web page. Since the URL also spawns other URL requests (eg images, javascript, CSS, redirects), they won't be loaded at all unless the web view loads the parent resource.

    Any other ideas?

    Thanks,
    Tom
  • Hi, BareFeetWare

        NSURLProtocol is also can be used in iOS cocoatouch, you can check
    this example
    http://code.google.com/p/tkawebview/downloads/detail?name=tkaurlprotocol.zi
    p&can=2&q

    this is a open source url protocol, but still have some bug,
    1.  however I think you can use + requestIsCacheEquivalent:toRequest: to
    check which resource to get from cache and which to start a new request
    2. if you want get the resource requested by UIWebView, you can override
    the UIWebView method  like this link
    http://stackoverflow.com/questions/3155359/in-webkit-how-do-i-get-the-conte
    nt-of-a-resourcemetioned

    - (NSURLRequest*) webView:(WebView*)sender
                    resource:(id)identifier

              willSendRequest:(NSURLRequest*)request

            redirectResponse:(NSURLResponse*)redirectResponse

              fromDataSource:(WebDataSource*)dataSource
    and if you want make all thing more safe, you can check this link
    http://stackoverflow.com/questions/1637604/method-swizzle-on-iphone-device

    2012/6/21 BareFeetWare <list.developer...>

    >> On Cocoa, You can intercept the WebView requests using a custom
    > NSURLProtocol. There is a fairly complete example here:
    >>
    >>
    > http://stackoverflow.com/questions/3155359/in-webkit-how-do-i-get-the-conte
    nt-of-a-resource

    >
    > Hi Jason,
    >
    > Thanks for your answer. Unfortunately, as you probably know, this doesn't
    > help for my UIWebView on CocoaTouch (iOS).
    >
    > Thanks,
    > Tom
    >

    --
    Fykec Yin
previous month june 2012 next month
MTWTFSS
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30  
Go to today