Using the JSContext
from a UIWebView
I have created a javascript function that is implemented as an Objective C block:
JSContext *js = ... //get contect from web view
js[@"aFunc"] = ^(JSValue *aString, JSValue *callback) {
NSString *realString = [aString toString];
MyOperation *op = [[MyOperation alloc] initWithString:realString andCallback:callback];
//Do some heavy lifting in background
[self.myQueue addOperation:op];
}
This function takes a callback as an argument and performs some work in an NSOperationQueue
before calling the callback like:
- (void)main {
JSValue *arg = [self theHeavyWork];
//Now we have finished the heavy work, switch back to main thread to run callback (if any).
if ([self.callback isObject] != NO) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.callback callWithArguments:@[arg]];
});
}
}
This works fine, unless the callback contains a call to alert()
:
//This javascript is part of the page in the UIWebView
window.aFunc("important information", function(arg) { alert("Got " + arg); });
In this case the alert shows and the UI becomes completely unresponsive. I am assuming that the event the touch event to close the alert is being blocked by the alert being there.
If I call the callback without the dispatch (in other words on which ever thread the MyOperation
is running on) it works just fine, but I was under the impression that any code that could have UI implications (in other words any JS callbacks) should always be run on the main thread. Am I missing something, or do is it really impossible to safely use alert()
when using the JavaScriptCore
framework?
After a couple days looking at stack traces of threads waiting for each other, the solution was so simple I'm not surprised I overlooked it in favor of trying more complicated stuff.
If you want to call back into a UIWebView
's javascript asynchronously, use window.setTimeout
and let the JSVirtualMachine
take care of queuing the callback.
Just replace
dispatch_async(dispatch_get_main_queue(), ^{
[self.callback callWithArguments:@[arg]];
});
with
dispatch_async(dispatch_get_main_queue(), ^{
[self.callback.context[@"setTimeout"] callWithArguments:@[self.callback, @0, arg]];
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With