8

I am trying to use one of iOS7 new features, the JavaScriptCore Framework. I can successfully output a helloWorld string from Javascript, but what I'm interested in, is doing HTTP POSTs in Javascript and then pass the response to Objective-C. Unfortunately, when I'm creating an XMLHttpRequest object in Javascript, I get EXC_BAD_ACCESS (code=1, address=....).

Here is the Javascript code (hello.js):

var sendSamplePost = function () {
    // when the following line is commented, everything works,
    // if not, I get EXC_BAD_ACCESS (code=1, address=....)
    var xmlHttp = new XMLHttpRequest();
};

var sayHello = function (name) {
    return "Hello " + name + " from Javascript";
};

Here is the Objective-C code inside my ViewController:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    JSContext *context = [[JSContext alloc] initWithVirtualMachine:[[JSVirtualMachine alloc] init]];

    NSString *scriptPath = [[NSBundle mainBundle] pathForResource:@"hello" ofType:@"js"];
    NSLog(@"scriptPath: %@", scriptPath);
    NSString *script = [NSString stringWithContentsOfFile:scriptPath encoding:NSUTF8StringEncoding error:nil];
    NSLog(@"script: %@", script);

    [context evaluateScript:script];

    JSValue *sayHelloFunction = context[@"sayHello"];
    JSValue *returnedValue = [sayHelloFunction callWithArguments:@[@"iOS"]];

    // this works!
    self.label.text = [returnedValue toString];


    JSValue *sendSamplePostFunction = context[@"sendSamplePost"];

    // this doesn't work :(
    [sendSamplePostFunction callWithArguments:@[]];
}

Could it be that HTTP Requests functionality is not provided in JavaScriptCore Framework? If yes, could I overcome this by using UIWebView's -stringByEvaluatingJavaScriptFromString:? What if I compiled and included in my project another Javascript Engine (e.g. V8)?

Damodaran
  • 10,882
  • 10
  • 60
  • 81
Kostis
  • 1,593
  • 15
  • 16

3 Answers3

23

XMLHttpRequest is, as stated before, not part of JavaScript, but you still can wrap the iOS URLRequest so it's available in your JS.

in JSUtils.h

   @protocol UtilsExport;

   @interface JSUtils : NSObject <UtilsExport>
   @end

   @protocol UtilsExport <JSExport>
   - (void)get:(NSString *)url then:(JSValue *)jsCallback;
   @end

in JSUtils.m

#import "JSUtils.h"

- (void)get:(NSString *)url then:(JSValue *)callback {
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]
                                                           cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
                                                       timeoutInterval:10];
    [request setHTTPMethod:@"GET"];
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        if ([data length] > 0 && error == nil) {
            [callback callWithArguments:@[[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding], @YES]];
        } 
    }];
}

Next, bind the instance to the JSContext somewhere in your code

JSContext *context = [[JSContext alloc] init];
context[@"utils"] = [[JSUtils alloc] init];

from your JS file, you can now call

utils.getThen('http://localhost/api/dashboard', function(resultString){
  console.log(resultString)
}

You could also use a block and bind it straight to the JSContext to get the same result.

Manuel Spuhler
  • 900
  • 12
  • 14
5

My guess would be that HTTP Requests are not part of JavaScript Core, as it's really part of the browser, not the JavaScript Language.
I would assume that JavaScript core only includes what's in the ECMAScript definition.

If you want AJAX, then the WebView is the way to go.

Ben Clayton
  • 80,996
  • 26
  • 120
  • 129
  • Thanks Ben for the help! I appreciate it. I was hoping that this new iOS7 feature would be trully powerful, but apparently, it isn't :( XMLHttpRequest seems to exist in [some draft version](http://www.w3.org/TR/XMLHttpRequest/). And if I'm not sure that UIWebView is **the way to go**, since I'll have to resort to [nasty hacks](http://stackoverflow.com/questions/5353278/uiwebviewdelegate-not-monitoring-xmlhttprequest) and have the UIWebView overhead, when I only need to execute Javascript. But anyway, since I don't see any other solution to this... Thanks Ben :) – Kostis Nov 19 '13 at 10:05
  • No probs. I recommend just biting the bullet and using AFNetworking for your POST request instead. – Ben Clayton Nov 19 '13 at 12:05
  • isn't `fetch()` a thing yet? – vicentedealencar Sep 24 '15 at 06:26
0
JSContext *context = [[JSContext alloc] init];

context[@"request"] = ^(NSString *url) {
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]
                                                       cachePolicy:NSURLRequestReloadIgnoringCacheData
                                                   timeoutInterval:10];

    NSURLResponse *response = nil;

    [request setHTTPMethod:@"GET"];
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
    NSString *body = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    return body;
};

In JS:

var body = request(url);
freestyler
  • 5,224
  • 2
  • 32
  • 39