Hello everyone!
Let’s start with a short background story: the task was to develop an open source library for presenting rich (HTML-based content) notifications inside iOS applications. Please feel free to take a look at current fork for the full story on GitHub.
Challenging part this time was implementing communication with JavaScript embedded in a notification (banner). The actual idea is to implement two-way communication, meaning that iOS must be able to parse functions from JS, and JS must be able to ask for some info from iOS app. Fortunately – not very well documented, yet pretty cool and neat class JSContext, available since iOS 7.0, came to rescue.
To not hold you any longer, let’s dig into the code!
The next moment after creating a UIWebView and loading HTML, fetch its JS context in order to provide info which JavaScript may require from the app:
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
Using the example from Notable library, JavaScript wants to know about banner’s width and height:
<script> document.getElementById('message').innerText = 'This view is ' + width() + ' pixels wide by ' + height() + ' pixels high.' </script>
iOS has the answers prepared like following:
[context evaluateScript:[NSString stringWithFormat:@"function width(){return %f;}", self.frame.size.width]]; [context evaluateScript:[NSString stringWithFormat:@"function height(){return %f;}", self.frame.size.height]];
Pretty simple, isn’t it? Now to the other side of story, where JS wants iOS app to perform some task(s). Fetching JS context is done using the same method like above, but it’s important to fetch the context from – (void)webViewDidFinishLoad:(UIWebView *)webView delegate method. The reason is that context may change while loading, so this is the perfect place for correct evaluation.
Evaluation is done by setting “listeners” for JS functions which in general look like this:
context[@"functionName"] = ^(NSString *parameter1, NSString *parameter2) { // Handle function with parameters };
JavaScript for the listener in example above could be something like:
<button onclick="functionName('parameter1', 'parameter2');">DoSomething</button>
Note: functionName has to be a custom function name. Otherwise, if you try to use a function which already exists in JavaScript, it is not going to be evaluated.
In case you want to see it in action, a full working implementation of examples and code snippets above can be found here.
Thanks for reading this and hopefully it saved you some time on Google! 🙂