Javascript如何傳遞大數據傳給Objective-C

Javascript與UIWebView交互,通常的做法是雙方約定一個協議,如:protocol://function/params,在Javascript中用window.location.href = "protocol://function/params";來請求,然後UIWebview的delegate方法:- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType會攔截到這個請求,分析出所要進行的操作。但是通過window.location.href這種方式實際上是GET的方式(請不要計較說GET和POST是http://纔有的請求方式- -),傳輸的數據量是有一定限制的。當數據量很大時,假如達到了5M,是不是可以用POST方式呢,我們知道POST是沒有限制的。


在Foundation的URL加載系統中有一個NSURLProtocol類,它是一個抽象類,可以通過子類化來定義新的或已存在的url加載行爲。當它的子類註冊到url加載系統中時,便可攔截所有url請求。是你想要的請求你可以對其處理,不是的話就不管,交給系統中其他的NSURLProtocol子類處理。那麼js發出的這個post請求,自然可以被你攔截到啦,你要傳的大數據就在request的body裏啦,哈哈~。注意!以post的方式請求,url的scheme必須是http://,可不能是那個什麼protocol://啦。


那麼問題的關鍵就是這個NSURLProtocol的子類究竟怎麼實現了,創建一個AppProtocolHandler類繼承NSURLProtocol,具體的實現如下:

AppProtocolHandler.m

#import "AppProtocolHandler.h"
static NSString * const MyURLProtocolHandledKey = @"MyURLProtocolHandledKey";
@interface AppProtocolHandler()
@property (nonatomic, strong) NSURLConnection *connection;
@property (nonatomic, strong) NSMutableData *mutableData;
@property (nonatomic, strong) NSURLResponse *response;
@end
@implementation AppProtocolHandler

//註冊自己的urlprotocl到url加載系統中
+ (void)registerSpecialProtocol {
    static BOOL inited = NO;
    
    if (!inited) {
        inited = YES;
        [NSURLProtocol registerClass:[AppProtocolHandler class]];
    }
}

- (void)handelRequest
{
    id<NSURLProtocolClient> client = [self client];
    NSURLRequest *request = [self request];
    NSURL *url = [request URL];
    //我們想要的數據
    NSData *bodyData = [request HTTPBody];
    NSString* bodyStr = [[NSString alloc] initWithData:bodyData encoding:NSUTF8StringEncoding];
    NSLog(@"[request HTTPBody]: %@", bodyStr);
    
    //響應一個空的data回去
    NSData *data = [NSData data];
    NSURLCacheStoragePolicy caching = NSURLCacheStorageNotAllowed;
    NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url MIMEType:@"text/plain" expectedContentLength:[data length] textEncodingName:@"utf-8"];
    [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:caching];
    [client URLProtocol:self didLoadData:data];
    [client URLProtocolDidFinishLoading:self];
}

#pragma mark - Override NSURLProtocol Methods

+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b
{
    return [super requestIsCacheEquivalent:a toRequest:b];
}

+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
    if ([NSURLProtocol propertyForKey:MyURLProtocolHandledKey inRequest:request]) {
        return NO;
    }
    return YES;
}

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
    return request;
}

- (void)startLoading
{
    id<NSURLProtocolClient> client = [self client];
    NSURLRequest *request = [self request];
    NSURL *url = [request URL];
    
    //是js給傳值的請求
    if ([[url absoluteString]hasPrefix:@"http://test"]) {
        [self handelRequest];
        return;
    }
    
    //是本地文件
    if ([[url absoluteString]hasPrefix:@"file://"]) {
        //直接讀取本地文件內容,作爲response的內容
        NSData *data = [[NSData alloc]initWithContentsOfFile:[url path]];
        NSURLCacheStoragePolicy caching = NSURLCacheStorageAllowedInMemoryOnly;
        
        NSURLResponse *response = [[NSURLResponse alloc] initWithURL:url MIMEType:[[url path]pathExtension] expectedContentLength:[data length] textEncodingName:@"utf-8"];
        [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:caching];
        [client URLProtocol:self didLoadData:data];
        [client URLProtocolDidFinishLoading:self];
    }
    //服務器上的文件
    else{
        //重新創建一個request
        NSMutableURLRequest *newRequest = [self.request mutableCopy];
        //標記是否創建過了該請求,不加標識會陷入一個死循環中
        [NSURLProtocol setProperty:@YES forKey:MyURLProtocolHandledKey inRequest:newRequest];
        //用nsurlconnection進行網絡請求
        self.connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
    }

}

- (void) stopLoading {
    [self.connection cancel];
    self.mutableData = nil;
}

#pragma mark - NSURLConnectionDelegate

- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
    
    self.response = response;
    self.mutableData = [[NSMutableData alloc] init];
}

- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.client URLProtocol:self didLoadData:data];
    
    [self.mutableData appendData:data];
}

- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
    [self.client URLProtocolDidFinishLoading:self];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    [self.client URLProtocol:self didFailWithError:error];
}

@end

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions中添加

[AppProtocolHandlerregisterSpecialProtocol];   這樣所有的請求都會先讓AppProtocolHandler過濾一遍的。


好了,關鍵的代碼完成了,找一個viewcontroller添加一個uiwebview上去吧,然後加載測試的html:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    NSURL *url = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"html"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [self.webview loadRequest:request];
}

怕小夥伴們oc寫久了,忘記了ajax怎麼post請求,再附加一段可測試的html代碼吧(也還是別人幫我寫的偷笑):

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
            <title>Document</title>
            <script type="text/javascript" src="http://upcdn.b0.upaiyun.com/libs/jquery/jquery-1.9.1.min.js"></script>
            <script type="text/javascript">
                $(function(){
                  alert(1);
                  $('#btn').click(function(){
                                  
                                  alert('Pre Run Post Request');
                                  
                                  $.post("http://test/setTitle", {
                                         "key":"value"
                                         }, function(response){
                                         alert('Return Http Status OK');
                                         alert('Here the response: ' + response);
                                         });
                                  
                                  })
                  });
                </script>
    </head>
    <body>
        <button type="button" id="btn">Click Me</button>
    </body>
</html>

注:js裏不會報錯的,因爲這個post根本沒走網絡,我就給了它一個長度爲0的data作爲響應。


參考鏈接:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章