UIWebView and JavaScript Woes

JavaScript Core is unfortunately not a public framework on iOS, leaving us at the mercy of the UIWebView class if we want to execute JavaScript in our applications. I have used JavaScript a lot recently and want to share four gotchas that I have had.

Functional code!

Turns out that JavaScript is functional, not as in working but as in a functional programming language. So when calling -[UIWebView stringByEvaluatingJavaScriptFromString:] you do not pass in for example:

But instead:

Yes even without the trailing ; character! The result returned from -[UIWebView stringByEvaluatingJavaScriptFromString:] will be the value of the last statement, if you add a trailing ; then the last statement will be an empty statement and you will get an empty string as a result. Took quite some time before I figured that one out.

UIWebView must be on screen to execute script

The UIWebView class will not render any content, nor execute JavaScript if it is not potentially on screen. So you must add the UIWebView to the view hierarchy even if you just have it around for executing some JavaScript in the background.

It is perfectly ok to let the web view be 0×0 pixels, or full transparency, but hidden does not seem to work either.

All access must be on the main thread

Not news since the documentation clearly says you should only work with UIKit classes on the main thread (with a few added thread safe additions in IOS 4). But this is a problem if you want to do background tasks with JavaScript, since you must call -[UIWebView stringByEvaluatingJavaScriptFromString:] from the main thread. Any long running JavaScript will block all user input and updates of the screen.

No fear though, you can make your JavaScript asynchronous. But then -[UIWebView stringByEvaluatingJavaScriptFromString:] will return before your script has finished and UIWebView do not have any obvious other way to communicate any results back to you. Obvious way that is. What you instead have to do to get asynchronious results back into the Objective-C world is by fake URL requests. Use a function like this to return results from the JavaScript world:

And then let the delegate for your UIWebView get the result as such:

Only String results

Turns out that -[UIWebView stringByEvaluatingJavaScriptFromString:] only returns strings as result. No problem for simple types such as said strings and numbers, but a big problem if you try to return a complex value such as an array:

This will not work, and will return a NSString of zero length. You must explicitly convert arrays to strings before returning them to the Objective-C world, easiest way would be something like this:

Conclusion

UIWebView is a blunt tool. Please do use http://bugreport.apple.com and file a request to make JavaScript Core a public framework. The more duplicates, the higher Apple will prioritize this issue on their todo list.

In the meantime UIWebView can be used to your bidding if treated with care.

8 Responses to “UIWebView and JavaScript Woes”

  1. David Fauthoux says:

    You saved hours of my life! Thank you!

  2. [...] other interesting stuff about javascript and UIWebView can be found here and [...]

  3. Daniel Nord says:

    Hi,

    Timothy: I think you’ve found what to do but for others looking for the solution here it is:
    function drawChartAsynchronous(data,start)
    {
    var delay = function() { drawChart(data,start); };
    setTimeout(delay, 100);
    }

    function drawChart(data,start)
    {
    //Function called asynchronously
    }

    I use this in my app Footsteps to plot a chart.

    Best
    Daniel

  4. lmnbeyond says:

    Hi,
    Great post! Thanks:)

  5. JFA says:

    Hi, thanks for the post,
    I’m very beginner to Javascript but I wold like to use some code I’ve found and return results of the javascript code in objective C.

    Can this stringByEvaluatingJavaScriptFromString function read
    vars inside javascript script ?
    or only the returned value of a function ?

    can you mail me some basic xcode project with a stupid javascript script in the bundle like hello world
    just to better understand the whole setup ?

    thanks in advance

    I’m trying to build an app that takes javascript from a server and then saves it inside the app, then loads it and starts it with UIWebView
    but I’m blocked at this point with this method:
    stringByEvaluatingJavaScriptFromString
    Can you please mail me some

  6. Tyler Young says:

    Hi Fredrick,

    Any suggestions on how to resize the UIWebView to 0×0 pixels? I can’t seem to get the darn thing out of full screen mode!

  7. Timothy Lee says:

    Thanks for the post! I’m running into a problem where a Javascript method takes a long time to run and blocks the UI thread (it’s not a network request). You mentioned making the Javascript code run asynchronously…how do you do that?

    Thanks!

  8. M Penades says:

    Hi!

    Great post! For sure I will make my request so I ve been searching stuff about how to use javascript from my native iphone application…

    I ve found another post with same topic saying than better than change window.location on your js to get results in objectiveC, is creating a fake iframe. More info in this post:

    http://blog.techno-barje.fr/post/2010/10/06/UIWebView-secrets-part3-How-to-properly-call-ObjectiveC-from-Javascript

Leave a Reply