0

I'm running a script in a UIWebView which sends data back to the host application (in Objective-C). If I put a link on the page pointing to myprotocol://some_data, I receive the information on the host side.

How can I achieve the same behaviour in pure Javascript, without user interaction? Something like a AJAX call, but not over HTTP?

I'm using AngularJS, but any solution in Javascript is welcome.

Samuel Bolduc
  • 18,163
  • 7
  • 34
  • 55

2 Answers2

2

I found that there was no way to do this without "cheating" in some way. I decided to create a <a> element in my page and dynamically change its href attribute, then triggering the click event on it with Javascript. Here is what I did :

CSS

a#sendToiOS {
  display:block;
  position: fixed;
  bottom: -1px;
  right: -1px;
  z-index: -1;
  opacity: 0;
  width: 1px;
  height: 1px;
}

Javascript

  var sendToiOS = function(protocol, action, params) {
    var e = document.createElement('a');
    e.id = 'sendToiOS';
    var strParams = "";
    if(typeof params !== 'undefined') {
      Object.keys(params).forEach(function(key) {
        strParams += strParams != "" ? '&' : '';
        strParams += key+'='+encodeURI(params[key]);
      });
    }
    e.href = strParams.length > 0 ? protocol+'://'+action+'?'+strParams : protocol+'://'+action;
    document.getElementsByTagName('body')[0].appendChild(e);
    e.click();
    e.parentNode.removeChild(e);
  }

Calling sendToiOS('mycustomprotocol', 'some_action', {foo: 'bar', foo2: 1337}); would then trigger a call to mycustomprotocol://some_action?foo=bar&foo2=1337.

Samuel Bolduc
  • 18,163
  • 7
  • 34
  • 55
  • Huh - I didn't suggest manually triggering click() as I assumed this wouldn't work - nice one :) – Graham Nov 27 '13 at 20:20
1

OK, I dug around inside the guts of Cordova and it seems like they use an iframe bridge for this. Essentially they dynamically create an iframe (so the iframe domain is the same as the calling page), then update the iframe src to a custom protocol URL.

Interestingly the iframe bridge is a fallback mode for iOS4; they do use an XMLHttpRequest, but I'm not yet certain how they work around the same-domain policy.

EDIT: To do this they register a custom NSURLProtocol, and then issue a HEAD request along with the desired data.

If you want to take a closer look, search for the exec.js file inside the cordova-js.zip file which comes with Cordova. You can find the custom protocol as CDVURLProtocol.m inside cordova-ios.zip.

Graham
  • 6,484
  • 2
  • 35
  • 39
  • Thank you. I finally decided to use a simpler workaround, but looking at the source code there is what pointed me in the right direction. I went with an easier implementation but still helped me! Thanks – Samuel Bolduc Nov 27 '13 at 16:27