文档章节

iOS WebView使用Ajax与iOS的交互

H
 HeroHY
发布于 2017/05/09 15:30
字数 1275
阅读 41
收藏 0

iOS 使用Ajax实现与Javascript同步异步交互

实现原理:

1.Ajax可以实现同步与异步请求
2.UIWebView可以实现Ajax跨域请求
3.NSURLProtocol可以拦截Ajax请求
4.NSURLProtocol可以实现模拟响应结果

 

需要解决的问题:

1.实现NSURLProtocol拦截Ajax请求

2.实现Ajax跨域,解决Ajax预检请求问题

3.实现NSURLProtocol返回响应

 

对于上述问题,我们定义自己的NSURLProtocol

#import <Foundation/Foundation.h>

@interface MyURLProtocol : NSURLProtocol


@end

代码实现

我们这里指定schema为 oschina://

对于其中可能遇到预检请求问题,请参阅(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注册一下才行,注意:多次注册也是可以的,不会造成多次拦截。

[NSURLProtocol registerClass:[UyURLProtocol class]];

 

通过这种方式,我们可以实现iOS端数据处理,在Javascript端我们需要实现2类调用,同步和异步

//异步请求
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调用

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方式来实现,具体过程可能使用到信号量的概念,这里不再赘述,又需要可以留言。

本文转载自:https://my.oschina.net/ososchina/blog/735693

共有 人打赏支持
H
粉丝 3
博文 152
码字总数 77343
作品 0
海淀
私信 提问
iOS与JS交互之WKWebView-WKUIDelegate协议

级别:★★☆☆☆ 标签:「iOS与JS交互」「WKWebView与JS交互」「WKUIDelegate」 作者: Xs·H 审校: QiShare团队 先解释下标题:“iOS与JS交互”。iOS指原生代码(文章只有示例),JS指前端...

QiShare
09/03
0
0
iOS中UIWebView的使用详解

iOS中UIWebView的使用详解 一、初始化与三种加载方式 UIWebView继承与UIView,因此,其初始化方法和一般的view一样,通过alloc和init进行初始化,其加载数据的方式有三种: 第一种: - (voi...

珲少
2015/06/23
0
1
iOS与JS交互之UIWebView-JSExport协议

级别:★★☆☆☆ 标签:「iOS与JS交互」「UIWebView与JS交互」「JSExport」 作者: Xs·H 审校: QiShare团队 先解释下标题:“iOS与JS交互”。iOS指原生代码(文章只有示例),JS指前端(不...

QiShare
08/31
0
0
IOS下WEBVIEW 的javascript数组与json定义 及交互

最近在折腾IOS新闻浏览客户端,当中需要用到webview传递JSON数据到IOS上,然后在IOS上解析。刚入门IOS不久,看了不少的书,但都是囫囵吞枣。在开发过程中,遇到不少问题。 开发环境 mac mini...

GIFCOOL
2013/08/20
0
0
iOS与JS交互之WKWebView-WKScriptMessageHandler协议

级别:★★☆☆☆ 标签:「iOS与JS交互」「WKWebView与JS交互」「WKJSMessageHandler」 作者: Xs·H 审校: QiShare团队 先解释下标题:“iOS与JS交互”。iOS指原生代码(文章只有示例),J...

QiShare
09/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

大数据学习之大数据技术笔记—spring入门

篇一 spring介绍 spring.io 官网 快速开始 Aop 面向切面编程,可以任何位置,并且可以细致到方法上 连接框架与框架 Spring 就是 IOC AOP 思想 有效的组织中间层对象一般都是切入 service 层 ...

董黎明
6分钟前
1
0
Linux如何查看进程、杀死进程、启动进程等常用命令

关键字: linux 查进程、杀进程、起进程 1.查进程 ps命令查找与进程相关的PID号: ps a 显示现行终端机下的所有程序,包括其他用户的程序。 ps -A 显示所有程序。 ps c 列出程序时,显示每个程...

临江仙卜算子
24分钟前
3
0
ASP.NET Core MVC 静态文件配置

在启动文件中添加以下配置 public class Startup{ public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddDirectoryBrowser(); ......

whltian
34分钟前
1
0
linux之自定义命令

本人使用的是ubuntu系统,不喜欢建各种桌面快捷链接,但是每次启动个软件,去查找又麻烦,所以自定义了命令,来快捷的启动应用: 1、修改/etc/bash.bashrc,在文件末尾,加上如下List-1中的内...

克虏伯
41分钟前
7
0
linux基础

系统安全 sudo su chmod setfacl 进程管理 w top ps kill pkill pstree killall 用户管理 id usermod useradd groupad userdel 文件系统 mount umount fsck df du 网络应用 curl telnet mail......

关元
43分钟前
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部