本文介绍了在本地缓存不起作用的 UIWebView 中动态加载 javascript 文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一个在 UIWebView 中使用大量 java 脚本的应用程序.并且在需要时会动态加载其中的一些 java 脚本(使用 jquery).

I'm trying to write an application which uses a lot of java script inside a UIWebView. And some of this java script is loaded dynamically (with jquery) when it is needed.

index.html 和 jquery 文件按预期加载,但 code.js 文件没有加载.code.js 文件是这样的请求(从 index.html 中截取 java 脚本代码):

The index.html and the jquery files are loaded as expected but not the code.js file. The code.js file is request like this (snipped of the java script code from index.html):

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

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

The start() function is called in the onload event of the body tag of the index.html. The code.js file only contains the following three lines of code:

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

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

The output I get from this code looks like this (I have some additional js code which forwards the console.log calls to the Xcode console which I omitted):

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 服务器加载内容.

I get this behavior as soon as I try to return the content of code.js in the overridden NSURLCache class. The idea in the end is to have a application which of which a part runs in a UIWebView without the need to load the content from an external web server.

这是缓存类(LocalSubstitutionCache)的缩写代码:

This is the shortened code of the cache class (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)

(The cache code is from here: http://cocoawithlove.com/2010/09/substituting-local-data-for-remote.html)

现在在我的视图控制器(其中包含一个 Web 视图)中,我执行以下操作:

Now in my view controller (which contains a web view) I do the following:

- (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 方法中的代码替换为以下代码时:

Now the interesting thing is that when I replace the code in the viewDidLoad method with the following code:

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

现在一切正常,并显示了我的两个警报.所以我怀疑我在缓存中返回的内容与缓存捕获时返回的原始请求不同.

Now all is working fine and I get my two alert's displayed. So I suspect that what I'am returning in the cache is not the same as what the original request is returning when it is catches by the cache.

这是我已经检查过的内容:

Here is what I already checked for:

  • 在工作案例中不使用文件网址.我的原始代码从 Web 服务器获取未缓存的代码.但是对于示例项目,我使用的是文件 url,因为这使项目更简单.
  • 返回的内容类型.在这两种情况下都是 text/javascript
  • 特价标头集.我已经使用 http 代理检查了未缓存的请求上设置了哪些标头.没有设置特殊的标头.

我最大的问题是 jquery 错误回调没有返回任何有用的错误信息.我只是得到 textStatus 参数的错误输出.

My biggest issue is that the jquery error callback does not return any useful error information. I just get an output of error for the textStatus argument.

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

You can download the example project from here: http://dl.dropbox.com/u/5426092/LocalCacheJsInjectTest.zip

希望有人可以帮助我

推荐答案

经过更多的工作,我找到了解决问题的方法.并在这里回答我的发现.

After some more more work I found the solution to my problem. And answer here with what I found.

解决方案是创建一个自定义的 NSURLProtocol 实现并将其注册到应用程序中作为 http 协议的处理程序.代码如下(我在代码中省略了一些错误处理):

The solution is to to create a custom NSURLProtocol implementation and register this in the application as handler for the http protocol. The code looks like this (I've omitted some error handling in the code):

#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 文件并访问以下代码:

This code allows now to load the index.html file be accessing the following code:

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

别忘了在你的应用委托中注册自定义协议:

And don't forget to register the custom protocol in your app delegate:

[NSURLProtocol registerClass:[MyHttpURLProtocol class]];

在您找到解决方案后,它竟然如此简单,真是令人惊讶.这是更新示例项目的链接:http://dl.dropbox.com/u/5426092/LocalCacheJsInjectTest-working.zip

It's amazing how simple the solution can be after your found it. Here is the link to the updated example project: http://dl.dropbox.com/u/5426092/LocalCacheJsInjectTest-working.zip

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

And for completeness sake here is the link to the blog post which helped me in finding the solution: http://www.infinite-loop.dk/blog/2011/09/using-nsurlprotocol-for-injecting-test-data/

这篇关于在本地缓存不起作用的 UIWebView 中动态加载 javascript 文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-26 21:21