2012-02-14 142 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