2012-02-14 32 views
5

Tôi đang cố gắng viết một ứng dụng sử dụng rất nhiều tập lệnh java bên trong một UIWebView. Và một số kịch bản java này được nạp động (với jquery) khi nó là cần thiết.Tự động tải các tệp javascript trong UIWebView với bộ đệm cục bộ không hoạt động

Tệp index.html và tệp jquery được tải như mong đợi nhưng không tải tệp code.js. Các tập tin code.js là yêu cầu như thế này (snipped của mã java script từ 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"); 
} 

Các hàm start() được gọi trong sự kiện onload của thẻ cơ thể của index.html. Các code.js nộp chỉ chứa ba dòng mã sau đây:

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

Kết quả tôi nhận được từ mã này trông như thế này (tôi có mã số bổ sung js đó chuyển tiếp cuộc gọi đến console.log Xcode console mà Tôi bỏ qua):

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

Tôi nhận được hành vi này ngay khi tôi cố trả lại nội dung của code.js trong lớp NSURLCache đã ghi đè. Ý tưởng cuối cùng là có một ứng dụng mà một phần chạy trong một UIWebView mà không cần tải nội dung từ một máy chủ web bên ngoài.

này được mã rút gọn của lớp bộ nhớ cache (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]; 

(Mã bộ nhớ cache là từ đây: http://cocoawithlove.com/2010/09/substituting-local-data-for-remote.html)

Bây giờ trong điều khiển điểm của tôi (trong đó có một cái nhìn web) Tôi làm như sau:

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

Điều thú vị là khi tôi thay thế mã trong phương thức viewDidLoad bằng mã sau:

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

Bây giờ tất cả đều hoạt động tốt và tôi nhận được hai cảnh báo của mình được hiển thị. Vì vậy, tôi nghi ngờ rằng những gì tôi đang trở lại trong bộ nhớ cache không giống như những gì yêu cầu ban đầu là trở lại khi nó được bắt bởi bộ nhớ cache.

Dưới đây là những gì tôi đã kiểm tra cho:

  • Không sử dụng url tập tin đối với trường hợp làm việc. Mã ban đầu của tôi lấy mã không được lưu trong bộ nhớ cache từ máy chủ web. Nhưng đối với dự án ví dụ tôi đang sử dụng các url của tệp vì điều này làm cho dự án đơn giản hơn.
  • Loại nội dung được trả lại. Có trên cả hai trường hợp văn bản/javascript
  • Các tiêu đề đặc biệt được đặt. Tôi đã kiểm tra với một proxy http những gì tiêu đề được thiết lập trên yêu cầu mà không được lưu trữ. Không có tiêu đề đặc biệt nào được đặt.

Vấn đề lớn nhất của tôi là cuộc gọi lại lỗi jquery không trả về bất kỳ thông tin lỗi hữu ích nào. Tôi chỉ nhận được một đầu ra lỗi cho đối số textStatus.

Bạn có thể tải dự án ví dụ từ đây: http://dl.dropbox.com/u/5426092/LocalCacheJsInjectTest.zip

Hope ai đó có thể giúp tôi đây

Trả lời

11

Sau khi một số công việc nhiều hơn tôi tìm thấy giải pháp cho vấn đề của tôi.Và trả lời ở đây với những gì tôi tìm thấy.

Giải pháp là tạo một triển khai NSURLProtocol tùy chỉnh và đăng ký điều này trong ứng dụng dưới dạng trình xử lý cho giao thức http. Mã này trông như thế này (tôi đã bỏ qua một số lỗi xử lý trong 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 

Mã này cho phép hiện nay để tải file index.html được truy cập vào đoạn mã sau:

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

Và đừng quên đăng ký giao thức tùy chỉnh trong ủy quyền ứng dụng của bạn:

[NSURLProtocol registerClass: [TOHttpURLProtocol class]];

[NSURLProtocol registerClass:[MyHttpURLProtocol class]]; 

Thật tuyệt vời giải pháp đơn giản như thế nào sau khi bạn tìm thấy nó. Dưới đây là liên kết với ví dụ dự án Cập nhật: http://dl.dropbox.com/u/5426092/LocalCacheJsInjectTest-working.zip

Và cho đầy đủ vì đây là liên kết đến bài viết trên blog mà đã giúp tôi trong việc tìm kiếm các giải pháp: http://www.infinite-loop.dk/blog/2011/09/using-nsurlprotocol-for-injecting-test-data/

+0

Các NSHTTPURLResponse initWithURL: statusCode: Phương pháp có sẵn từ 5.0 mặc dù nó không xuất hiện trong tài liệu. Điều này thật kỳ lạ, chúng ta có thể sử dụng nó không? – groumpf

+0

Tôi giả định rằng chúng ta có thể sử dụng phương thức này khi nó được thêm vào và nằm trong tệp tiêu đề có thể nhìn thấy thông thường cho lớp này. Nhưng cho đến nay tôi không có ứng dụng nào có mã này trong cửa hàng ứng dụng để nó có thể khác nhau. – Chris

+0

Tôi hiện đã gửi thành công ứng dụng của mình bằng phương pháp đã đề cập. Không có vấn đề với nó. – Chris

Các vấn đề liên quan