IOS uses Ajax to synchronize asynchronous interactions with JavaScript
Implementation principle:
1.Ajax可以实现同步与异步请求2.UIWebView可以实现Ajax跨域请求3.NSURLProtocol可以拦截Ajax请求4.NSURLProtocol可以实现模拟响应结果
Issues that need to be addressed:
2.1.实现NSURLProtocol拦截Ajax请求2.实现Ajax跨域,解决Ajax预检请求问题3.实现NSURLProtocol返回响应
For the above questions, we define our own Nsurlprotocol
#import <Foundation/Foundation.h>@interface MyURLProtocol : NSURLProtocol@end
Code implementation
We specify schema as oschina://here
For issues where preflight requests may be encountered, see (preflighted requests in Ajax cross-domain (CROS) requests)
@interface Myurlprotocol () @property (nomatic,strong) nsmutabledictionary * reponseheader; @end @implementation myurlprotocol//replication Caninitwithrequest, decide whether to intercept the request + (BOOL) Caninitwithrequest: (Nsurlrequest *) request{//implemented here to oschina:// Synchttprequest and Oschina://asynchttprequest intercept 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; }}//If not intercepted, return no return no; Carbon canonicalrequestforrequest, processing request, here we can not process, directly using req+ (nsurlrequest*) Canonicalrequestforrequest: (Nsurlrequest * ) req{return req;} The replication startloading, and processes the preflight request-(void) startloading{//handles cross-domain operations, if it is an options operation. If cross-domain access sends an options request, a response permission is required to continue the head request//In addition, the data sent by Ajax cannot be received, and a custom request header X-javascript-header is required to javascript- >ios Passing Data if ([Self.request.HTTPMethod isequaltostring:@ "Options"]) {nsdictionary * Fields_resp = _reponsehe Ader Responding to Ajax preflight requests 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{//Implementation of an AJAX formal request for resolution and response [self dorequesttoresponse]; }}-(void) dorequesttoresponse{nsdictionary *dic = [self.request.allHTTPHeaderFields copy]; NSString *jsstr = dic[@ "X-javascript-header"]; Get response Header Data NSString * useragentinstorage = [[Nsuserdefaults standarduserdefaults] stringforkey:@ "useragent"]; NSString * useragent = dic[@ "User-agent"];//if necessary save 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]; Convert jsstr = [jsstr stringbyreplacingoccurrencesofstring:@ "\ n" withstring:@ "\\n"]; JSSTR = [Jsstr stringbyreplacingoccurrencesofstring:@ "\ r" withstring:@ "\\r"]; JSSTR = [Jsstr stringbyreplacingoccurrencesofstring:@ "\ T" withstring:@ "\\t"]; JSSTR = [Jsstr stringbyreplacingoccurrencesofstring:@ "+" withstring:@ "\\0"]; Nsmutabledictionary *jsdic = [Jsstr mutableobjectfromjsonstring]; if (jsdic==nil) {NSString * tempjsstr = [jsstr stringbyreplacingoccurrencesofstring:@ "\ \" withstring:@ "\\\\"]; Jsdic = [Tempjsstr mutableobjectfromjsonstring]; } if (Jsdic==nil) {[UMJS showtoast:@] parameter parsing failed! "]; Return } NSString *Servicename= jsdic[@ "service"]; NSString *methodname = jsdic[@ "method"]; ID params = jsdic["params"]; [------------------Processing response results------------------------]//1. Start processing, slightly//send the corresponding data to the Ajax side, assuming the result nsstring * response = [@{@ "Result": result,@ "MSG": @ "Hello World" @ "code": @1} jsonstring]; [Self sendresponsetoclient:response]; [------------------results of processing responses------------------------]} -(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]; }//Send Error request information-(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");} Processing jumps (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; }
After customizing the end, we need to register in appdetegate or Uiviewcontroller, note: Multiple registration is also possible, will not cause multiple interception.
[NSURLProtocol registerClass:[UyURLProtocol class]];
In this way, we can implement the iOS data processing, on the JavaScript side we need to implement 2 class calls, synchronous and asynchronous
Asynchronous request 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-javascript-header, note that using a third-party Base64 encode Xhr.setrequestheader ("X-javascript-header", Base64util.encode (Xjavascript)); Xhr.onload = function (e) {if (this.status = = =) {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) {}}}//sync request 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-javascript-header, note that using a third-party 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 = =) {return Xhr.execXhr.responseT Ext } else {return xhr.execXhr.responseText; }} return {};}
Then we call through 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);
In general Networkutil the method that can be called must be a class method
@implementation NetworkUtil+(NSString * )getNetworkInfo{ NSString * result = [略]; return result;}@end
We have implemented a synchronous asynchronous approach on the iOS platform, the Android platform has a system-provided Javascriptinterface interface, of course, we want to implement and this iOS similar way, we can use the serversocket way to achieve, The specific process may use the concept of semaphores, which are not mentioned here.
You can add my or qq:1499732272, I will provide a variety of iOS development related information!
iOS WebView using Ajax to interact with iOS