I have a hybrid app that uses a UIWebView to load an ajax/multi-step form from a server. Each step sends an ajax request to the server and receives an appropriate response from the server based on the previous steps input. On the last step, I'd like to be able to process the response and use that information in my app for various things. Using Swift, what is the best way to go about achieving this? I obviously can get the initial response when the page loads using NSURLCache, but I can't seem to figure out how to grab the responses that are being received between ajax steps. Any ideas?
1 Answers
As you can inject your own Javascript into a UIWebView web page, you can introduce a shim layer for Ajax calls once the initial page has loaded.
In this shim you load a made up URL which you can detect in your UIWebView delegate based on the Ajax call. When the delegate sees the fake URL, you record the Ajax call, but return that the load should not happen. You shim then calls the original Ajax code, so everything works.
So although you cannot detect Ajax calls you can detect URL requests.
The following answer seems to have all you need: UIWebViewDelegate not monitoring XMLHttpRequest?
** Update **
I took the code from the link above and extended it to also detect the responses. This updated code requests a load of a fake URL containing the readyState
and http status after the '//'. It does this on every ready state change.
So when an Ajax request is made you will see the following requests in your UIWebView delegate:
1) A request to load a URL of the form mpAjaxHandler://URL
2) On each state change following the Ajax send(), a request to load a URL of the form mpAjaxHandlerDone://readyState:httpstatus/URL
I guess you are looking for when the readyState gets to 4 with HTTP status of 200 for success.
To activate this, load the javascript below when your main page has finished loading in the UIWebView and see if your delegate fires for request and response.
var s_ajaxListener = new Object();
s_ajaxListener.tempOpen = XMLHttpRequest.prototype.open;
s_ajaxListener.tempSend = XMLHttpRequest.prototype.send;
s_ajaxListener.callback = function () {
console.log('mpAjaxHandler://' + this.url);
window.location='mpAjaxHandler://' + this.url;
};
s_ajaxListener.callbackDone = function (state,status) {
console.log('mpAjaxHandlerDone://' + state + ':' + status + '/' + this.url);
window.location='mpAjaxHandlerDone://' + state + ':' + status + '/' + this.url;
};
// Added this function to catch the readyState changes and request
// fake page loads.
function override_onreadystatechange(){
s_ajaxListener.callbackDone(this.readyState);
this.original_onreadystatechange();
}
XMLHttpRequest.prototype.open = function(a,b) {
if (!a) var a='';
if (!b) var b='';
s_ajaxListener.tempOpen.apply(this, arguments);
s_ajaxListener.method = a;
s_ajaxListener.url = b;
if (a.toLowerCase() == 'get') {
s_ajaxListener.data = b.split('?');
s_ajaxListener.data = s_ajaxListener.data[1];
}
}
XMLHttpRequest.prototype.send = function(a,b) {
if (!a) var a='';
if (!b) var b='';
s_ajaxListener.tempSend.apply(this, arguments);
if(s_ajaxListener.method.toLowerCase() == 'post')s_ajaxListener.data = a;
s_ajaxListener.callback();
// Added this to intercept Ajax responses for a given send().
this.original_onreadystatechange = this.onreadystatechange;
this.onreadystatechange = override_onreadystatechange;
}
I have tried this in a browser by pasting the code into the top of the loadDoc() method in this W3Schools demo and looking in the javascript console. http://www.w3schools.com/ajax/tryit.asp?filename=tryajax_first. If you try this comment out the two window.location=
lines first.
Hope this helps or gives you a start.

- 1
- 1

- 7,936
- 2
- 17
- 28
-
This seems like a viable option to intercept the request, but I don't see how this is going to help me with the response from the server. I don't see anywhere in that shim where I can get the response received from the server...I need to know that the response was successful so that I can store certain return data in my app. – kevindeleon Oct 06 '15 at 15:21
-
I would think in the shim, you could replace the `onreadystatechange` function of the calling XMLHttpRequest with a shim which does a similar trick. Have it check for `readyState` and load fake URLs as each state changes and call then orginal function. This would be per instance of the XMLHttpRequest and not on the prototype. – Rory McKinnel Oct 06 '15 at 20:11
-
@kevindeleon Updated answer with extended javascript which hopefully does what you need. – Rory McKinnel Oct 06 '15 at 22:07
-
@kevindeleon Did you try this as a solution? – Rory McKinnel Oct 15 '15 at 16:35
-
Yes I did...it's not exactly what I was looking for. I'm using a more generic solution (but still not through) using NSURLProtocol. – kevindeleon Oct 15 '15 at 19:03
-
1Makes sense. I use Apple's `CustomHTTPProtocol` NSURLProtocol subclass to handle http/https authentication which works well for me with UIWebView. – Rory McKinnel Oct 15 '15 at 22:28