在本地caching不工作的情况下,在UIWebView中dynamic加载JavaScript文件

我正在尝试编写一个应用程序,它在UIWebView中使用了大量的Java脚本。 有些这个Java脚本是dynamic加载(与jQuery的),当它需要。

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

我从这个代码得到的输出如下所示(我有一些额外的js代码,将console.log调用转发到我省略的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服务器加载内容。

这是caching类(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]; 

(caching代码是从这里: http : //cocoawithlove.com/2010/09/substitution-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"]]]; } 

现在有趣的是,当我用下面的代码replaceviewDidLoad方法中的代码:

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

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

这是我已经检查过的:

  • 没有使用工作案例的文件url。 我的原始代码从Web服务器获取不caching的代码。 但是对于我使用文件URL的示例项目,这使得项目更简单。
  • 内容types返回。 在这两种情况下的文本/ JavaScript
  • 特价标题设置。 我已经检查了与一个HTTP代理哪些头被设置的请求没有被caching。 没有特别的标题设置。

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

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

希望有人能帮助我

经过一些更多的工作,我find了解决我的问题。 在这里回答我发现的内容。

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

 #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]]; 

find它之后解决scheme的简单程度真是太棒了。 以下是更新示例项目的链接: http : //dl.dropbox.com/u/5426092/LocalCacheJsInjectTest-working.zip

为了完整起见,这里是链接到博客post,帮助我find解决scheme: http : //www.infinite-loop.dk/blog/2011/09/using-nsurlprotocol-for-injecting-test-data/