iOS WebView使用Ajax與iOS的交互

iOS 使用Ajax實現與Javascript同步異步交互javascript

實現原理:java

1.Ajax能夠實現同步與異步請求
2.UIWebView能夠實現Ajax跨域請求
3.NSURLProtocol能夠攔截Ajax請求
4.NSURLProtocol能夠實現模擬響應結果

 

須要解決的問題:ajax

1.實現NSURLProtocol攔截Ajax請求

2.實現Ajax跨域,解決Ajax預檢請求問題

3.實現NSURLProtocol返回響應

 

對於上述問題,咱們定義本身的NSURLProtocol跨域

#import <Foundation/Foundation.h>

@interface MyURLProtocol : NSURLProtocol


@end

代碼實現app

咱們這裏指定schema爲 oschina://dom

對於其中可能遇到預檢請求問題,請參閱(Ajax跨域(CROS)請求中的Preflighted Requests異步

@interface MyURLProtocol()
 @property(nomatic,strong) NSMutableDictionary * reponseHeader;
@end

@implementation MyURLProtocol

//複寫canInitWithRequest,決定是否攔截請求
+(BOOL)canInitWithRequest:(NSURLRequest *)request{
    
   //這裏實現對  oschina://syncHttpRequest和oschina://asyncHttpRequest攔截
    if(request.URL.scheme!=nil && [[request.URL.scheme lowercaseString] isEqualToString:@"oschina"])
    {
    
        if([request.URL.host isEqualToString:@"syncHttpRequest"] || [request.URL.host isEqualToString:@"asyncHttpRequest"])
        {
            if(_reponseHeader==nil)
             {
                 _reponseHeader = @{
                                  @"Access-Control-Allow-Credentials":@"true",
                                  @"Access-Control-Allow-Origin":@"*",
                                  @"Access-Control-Expose-Headers":@"jsStr",
                                  @"Access-Control-Allow-Methods":@"GET,POST,PUT,OPTIONS,HEAD",
                                  @"Access-Control-Allow-Headers":@"Origin,jsStr,Content-Type,X-Request-Width",
                                  @"Access-Control-Max-Age":@"10",
                                  @"Cache-Control":@"no-cache,private",
                                  @"Pragma":@"no-cache,no-store",
                                  @"Expires":@"0",
                                  @"Connection":@"Close"
                                  };
            }
            return YES;
        }
   }
    //若是不攔截,則返回NO
    return NO;
}

//複寫 canonicalRequestForRequest ,加工請求,這裏咱們能夠不加工,直接使用req
+ (NSURLRequest*) canonicalRequestForRequest:(NSURLRequest *)req
{
   
    return req;
}
//複寫startLoading,並處理預檢請求
- (void) startLoading{
    //處理跨域操做,若是是options操做。若是是跨域訪問會發送一個options請求,須要response一個權限纔會繼續走head請求
  //此外,ajax發送的數據沒法被接收,須要一個自定義請求頭X-Javascript-Header,用來javascript->iOS傳遞數據
  if ([self.request.HTTPMethod isEqualToString:@"OPTIONS"])
    {
        
        NSDictionary * fields_resp = _reponseHeader;
        //響應ajax預檢請求
        NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:[self.request URL] statusCode:200 HTTPVersion:@"1.1" headerFields:fields_resp];
        [[self client] URLProtocol: self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
        [[self client] URLProtocol:self didLoadData:[NSData data]];
        [[self client] URLProtocolDidFinishLoading:self];
    }else{
        //實現對ajax正式請求的解析與響應
        [self doRequestToResponse];
    }
    
}

-(void) doRequestToResponse
{

    NSDictionary *dic = [self.request.allHTTPHeaderFields copy];
    NSString *jsStr = dic[@"X-Javascript-Header"];  //獲取響應頭數據
    NSString * userAgentInStorage   = [[NSUserDefaults standardUserDefaults] stringForKey:@"UserAgent"];
    NSString * userAgent =  dic[@"User-Agent"];
    
    
//必要時保存user-Agent
    if([NSString isEmptyOrNil:userAgentInStorage] && ![NSString isEmptyOrNil:userAgent])
    {
        [[NSUserDefaults standardUserDefaults] setObject:userAgent forKey:@"UserAgent"];
        [[NSUserDefaults standardUserDefaults] synchronize];
    
    }
   if([NSString isEmptyOrNil:jsStr])
    {
        [self sendRequestErrorToClient];
        return;
    }

  if([jsStr hasPrefix:@"@"])
    {
        jsStr = [jsStr stringByReplacingOccurrencesOfString:@"@" withString:@""];
    }
   
    NSData *data = [GTMBase64 decodeString:jsStr];
    jsStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    
    // 轉換
    jsStr = [jsStr stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
    jsStr = [jsStr stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"];
    jsStr = [jsStr stringByReplacingOccurrencesOfString:@"\t" withString:@"\\t"];
    jsStr = [jsStr stringByReplacingOccurrencesOfString:@"\0" withString:@"\\0"];
    
    
    NSMutableDictionary *jsDic = [jsStr mutableObjectFromJSONString];
    
    if(jsDic==nil)
    {
        NSString * tempJsStr = [jsStr stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"];
        jsDic = [tempJsStr mutableObjectFromJSONString];
    }
    if(jsDic==nil)
    {
        [UMJS showToast:@"參數解析失敗!"];
        return;
    }
   
    NSString *serviceName= jsDic[@"service"];
    NSString *methodName = jsDic[@"method"];
    id params = jsDic["params"];

   [------------------處理響應的請結果------------------------]
     //1.開始處理,略
    //發送相應數據到Ajax端,假定結果爲result
   NSString * response = [@{@"result":result,@"msg":@"Hello World",@"code":@1} JSONString];
  [self sendResponseToClient:response];
   [------------------處理響應的請結果------------------------]

}

-(void) sendResponseToClient:(NSString *) str
{
     NSData *repData = [str dataUsingEncoding:NSUTF8StringEncoding];
  
    
    NSMutableDictionary *respHeader = [NSMutableDictionary dictionaryWithDictionary:fields_resp];
    respHeader[@"Content-Length"] = [NSString stringWithFormat:@"%ld",repData.length];
    
    NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:[self.request URL] statusCode:200 HTTPVersion:@"1.1" headerFields:respHeader];
    
    [[self client] URLProtocol: self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
    [[self client] URLProtocol:self didLoadData:repData];
    [[self client] URLProtocolDidFinishLoading:self];
    
 }
    


//發送錯誤請求信息
-(void) sendRequestErrorToClient
{

    NSData *data = [@"" dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary * fields_resp =_reponseHeader;
    NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:[self.request URL] statusCode:400 HTTPVersion:@"1.1" headerFields:fields_resp];
    [[self client] URLProtocol: self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
    [[self client] URLProtocol:self didLoadData:data];
    [[self client] URLProtocolDidFinishLoading:self];
    
}

- (void) stopLoading{
//    NSLog(@"stopLoading");
}
//處理跳轉
(NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response { 
if ([response isKindOfClass:[NSHTTPURLResponse class]]) 
{ 
NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse *)response;
 if ([HTTPResponse statusCode] == 301 || [HTTPResponse statusCode] == 302) 
{ 
NSMutableURLRequest *mutableRequest = [request mutableCopy]; 
[mutableRequest setURL:[NSURL URLWithString:[[HTTPResponse allHeaderFields] objectForKey:@」Location」]]];
 request = [mutableRequest copy];
 [[self client] URLProtocol:self wasRedirectedToRequest:request redirectResponse:response]; 
} 
} 
return request; 
}

自定義結束以後,咱們須要在AppDetegate或者UIViewController註冊一下才行,注意:屢次註冊也是能夠的,不會形成屢次攔截。async

[NSURLProtocol registerClass:[UyURLProtocol class]];

 

經過這種方式,咱們能夠實現iOS端數據處理,在Javascript端咱們須要實現2類調用,同步和異步this

//異步請求
function sendAsyncAjax(xJavascript, onload, onerror) {
     
     var xhr, results, url;
     url = 'oschina://asyncHttpRequest?rnd='+Math.random();
     xhr = new XMLHttpRequest();
   try{
    
     xhr.open('POST', url, true);
     xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
     xhr.setRequestHeader("Cache-Control", "no-cache,private");
     xhr.setRequestHeader("Pragma", "no-cache,no-store");
     xhr.setRequestHeader("User-Agent", navigator.userAgent);
    //經過X-Javascript-Header發送數據到iOS,注意,使用第三方Base64 encode
    xhr.setRequestHeader("X-Javascript-Header", Base64Util.encode(xJavascript));

     xhr.onload = function (e) {
         if (this.status === 200) {
             results = JSON.parse(xhr.responseText);
             onload(results);
         }else{
             onload({'e':e});
         }
     };
 
     xhr.onerror = function (e) {
         onerror({'e':e});
     };
    
     }catch(exception){
       console.error(exception);

   }finally{
    try{
    xhr.send(null);
    }catch(exception2){}
  }
}

//同步請求
function sendSyncAjax(xJavascript) {
     
     var xhr, results, url;
     url = 'oschina://syncHttpRequest?rnd='+Math.random();
     xhr = new XMLHttpRequest();
   try{
    
     xhr.open('POST', url, true);
     xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
     xhr.setRequestHeader("Cache-Control", "no-cache,private");
     xhr.setRequestHeader("Pragma", "no-cache,no-store");
     xhr.setRequestHeader("User-Agent", navigator.userAgent);
     //經過X-Javascript-Header發送數據到iOS,注意,使用第三方Base64 encode
    xhr.setRequestHeader("X-Javascript-Header", Base64Util.encode(xJavascript));

     }catch(exception){
       console.error(exception);

     }finally{
    try{
      xhr.send(null);
     
    }catch(exception2){}
  }
 if (xhr.readyState == 4) {
        
        if (xhr.status == 200) {
           return  xhr.execXhr.responseText;
        } else {
            return xhr.execXhr.responseText;
        }
    }

 return {};
}

而後咱們經過javascript調用url

var script = JSON.stringify({service:"NetworkUtil",method:"getNetworkInfo",params:{}};
sendAsyncAjax(script ,function(result){
   
}, function(error){

});
或者
var script = JSON.stringify({service:"NetworkUtil",method:"getNetworkInfo",params:{}};
var result = sendSyncAjax(script);

通常來講NetworkUtil能夠調用的方法必須是類方法

@implementation NetworkUtil

+(NSString * )getNetworkInfo

{

  NSString * result = [略];

  return result;

}

@end

 

咱們這裏實現了iOS平臺上同步異步的方法,Android平臺有系統提供的javascriptInterface接口,固然,咱們想要實現和本篇iOS相似的方式,咱們可使用ServerSocket方式來實現,具體過程可能使用到信號量的概念,這裏再也不贅述,又須要能夠留言。

相關文章
相關標籤/搜索