IOS uses Ajax for synchronous asynchronous interaction with Javascript

Implementation principle:

UIWebView can realize Ajax cross-domain request. 3.NSURLProtocol can intercept Ajax request. 4Copy the code

Problems to be solved:

1. Implement NSURLProtocol to intercept Ajax requests. 2. Implement NSURLProtocol to return the responseCopy the code

For the above problem, we define our own NSURLProtocol

#import <Foundation/Foundation.h>
@interface MyURLProtocol : NSURLProtocol
@end
Copy the code

Code implementation

Here we specify the schema as OSChina ://

@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; 
}

Copy the code

After the customization is complete, we need to register with AppDetegate or UIViewController. Note: multiple registrations are also allowed without multiple intercepts.

[NSURLProtocol registerClass:[UyURLProtocol class]];
Copy the code

In this way, we can implement data processing on the iOS side, and on the Javascript side we need to implement two types of calls, synchronous and asynchronous

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); // Send data to iOS via x-javascripts -Header, note, Encode xhr.setrequesTheader (" x-javasjavascript -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); 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); // Send data to iOS via x-javascripts -Header, note, Encode xhr.setrequesTheader (" x-javasjavascript -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 {}; }Copy the code

Then we call it through javascript

ar 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);Copy the code

In general, methods that NetworkUtil can call must be class methods

@implementation NetworkUtil +(NSString *)getNetworkInfo {NSString * result = [omitted]; return result; } @endCopy the code

We have implemented the synchronous asynchronous method on the iOS platform, and the Android platform has the javascriptInterface provided by the system. Of course, we want to implement the method similar to the one in the iOS, we can use the ServerSocket method, and the specific process may use the concept of semaphore. I won’t repeat it here

The original address: my.oschina.net/ososchina/b…