2012-02-14 33 views
5

我想写一个应用程序,它在UIWebView中使用了很多Java脚本。当需要的时候,这个java脚本的一些动态加载(使用jquery)。在UIWebView中动态加载JavaScript文件与本地缓存不工作

index.html和jquery文件按预期加载,但不是code.js文件。该code.js文件是这样的请求(从index.html的剪断的Java脚本代码):

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"); 
} 

的start()函数被调用的的index.html的body标签的onload事件。该code.js文件只包含以下三行代码:

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

我从这个代码得到的输出看起来像这样(我有一个转发的console.log一些额外的js代码调用到Xcode的控制台,其我略):

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"); 

我尽快得到这个行为,我试图返回在覆盖NSURLCache类code.js的内容。这个想法最终是有一个应用程序,其中一部分运行在UIWebView中,而无需从外部Web服务器加载内容。

这是缓存类的短码(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]; 

(缓存代码是从这里:http://cocoawithlove.com/2010/09/substituting-local-data-for-remote.html

现在,在我的视图控制器(其中包含Web视图)我做到以下几点:

- (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"]]]; 
} 

现在有趣的事情是,当我用下面的代码替换代码在viewDidLoad方法:

- (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]]]; 
} 

现在一切正常,我得到我的两个警报的显示。所以我怀疑我在缓存中返回的内容与原始请求在被缓存捕获时返回的内容不一样。

以下是我已经检查了:

  • 不使用文件URL的工作情况。我的原始代码从Web服务器获取未缓存的代码。但是对于我使用文件URL的示例项目,这使得项目更简单。
  • 返回的内容类型。在两种情况下均为text/javascript
  • Specials标头设置。我已经检查了一个http代理,哪些头文件没有被缓存的请求被设置。没有特殊的标题设置。

我最大的问题是,jQuery错误回调不会返回任何有用的错误信息。我只是得到了textStatus参数的错误输出。

你可以从这里下载示例项目:http://dl.dropbox.com/u/5426092/LocalCacheJsInjectTest.zip

希望有人能帮助我在这里

回答

11

后一些更多的工作,我找到了解决我的问题。并在这里回答我发现的内容。

解决的办法是创建一个自定义的NSURLProtocol实现,并在应用程序中注册为http协议的处理程序。该代码看起来像这样(我省略了一些错误的代码处理):现在

#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 

此代码允许加载index.html文件被访问以下代码:

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

而且请不要忘记在您的应用程序代理中注册自定义协议:

[NSURLProtocol registerClass:[TOHttpURLProtocol class]];

[NSURLProtocol registerClass:[MyHttpURLProtocol class]]; 

这是多么简单的解决方案可以在你找到它后,令人惊讶。这里是链接到更新的示例项目:http://dl.dropbox.com/u/5426092/LocalCacheJsInjectTest-working.zip

而且为完整起见这里是链接到博客文章这让我找到了解决方案:http://www.infinite-loop.dk/blog/2011/09/using-nsurlprotocol-for-injecting-test-data/

+0

的NSHTTPURLResponse initWithURL:的StatusCode:方法是可行的,因为5.0尽管它没有出现在文档中。这很奇怪,我们可以使用它吗? – groumpf 2012-03-28 15:05:28

+0

我认为我们可以使用这个方法,因为它被添加并且在这个类的普通可见头文件中。 但到目前为止,我还没有在应用商店中使用此代码的应用程序,所以它可能是不同的。 – Chris 2012-03-29 08:22:30

+0

我已经成功提交我的应用程序使用提到的方法。没问题。 – Chris 2012-09-19 06:03:26