How to communicate with iframes inside WebView

Draw & Code
3 min readJan 20, 2017

--

One of the core functionalities at the heart of Onefill is its ability to auto-fill even the most complex forms on a website with a single tap and 100% precision. The technology we built allows us to do just that, but with one exception — iframes.

And while you might think that iframes are a thing of the past (who would use them these days, right?), the reality is they’re often powering the most critical piece of functionality of any online store — the payment form.

Before I go on, a quick shout-out. If you want to work with us on problems like this one, we’re hiring right now — native mobile development, Javascript, and other roles. Check out our jobs page and let’s have a chat! http://onefill.com/meettheteam/

Now back to iframes. Many payment processors (e.g. Stripe, or Braintree) offer their customers a way to accept payments in the simplest possible way — by just dropping a piece of javascript into their checkout page. This renders an iframe with a form provided by the payment processor that accepts card details — meaning the merchant never sees them at all, and everyone involved in the process feels safer.

The problem for us is that this, together with modern browser security mechanisms like CORS and same-origin policy, mean that we can’t automate the process of entering credit card data if we only inject Onefill’s JavaScript helpers into the main page, and the payment form is rendered inside an iframe.

After some experimentation, however, we’ve found an approach to support iframe — at least with iOS web view — and to communicate with it.

Conclusions first

  1. Use WKWebView instead UIWebView or WebView for 2 reasons: a) Apple recommends it; and b) There is an API to inject JS into iFrame
  2. Use built-in WKUserScript API to inject JS, set forMainFrameOnly as false to inject JS into every frames (including iframe)
  3. To communicate between iframe and app, use JS webkit API

And now for the code

It turns out the problem can be solved if we split it into two parts:

  1. how to inject our Javascript helpers into an iframe; and
  2. how to establish interactions between our code in an iframe, and the one in the main frame.

Let’s look at these one by one.

Just a note: To run the code in this blog post you’ll need:

  • Xcode: Version 8.2.1 (8C1002)
  • Swift: Apple Swift version 3.0.2 (swiftlang-800.0.63 clang-800.0.42.1)

1. Injecting JavaScript helpers into an iframe

It’s actually really easy to inject JavaScript helpers in an iframe these days. All you need to do is use WKUserScript API to inject JavaScript into all frames, and specifically a forMainFrameOnly parameter when creating an injectable user script object.

Example: Inject a script to make every h1 tag red.

As you can see, all we have to do is simply create WKUserScript, and configure it with forMainFrameOnly: false, which means that this script will be executed in all frames of WKWebView, and not just the main one.

After that, add it to an instance of WKUserContentController, create a WKWebView with that controller, and load your page with iframe — you should see your code being executed in all of them.

2. Communicating with iFrame

Using JavaScript webkit API is almost the same as injecting JavaScript helpers into an iframe, however we need to implement WKScriptMessageHandler protocol to receive message sent by JavaScript.

Once we’ve got our code running inside all iframes, we need to communicate with it to execute specific tasks. This can be done by implementing WKScriptMessageHandler protocol, and adding that resulting class to WKUserContentController used with the web view.

Once that’s done, the handler becomes available to JavaScript code running inside the frame as window.webkit.messageHandlers.<handlerName> object, exposina g postMessage method that accepts string data and sends it back to the main iOS application.

The application then becomes the communication medium, passing and orchestrating messages back and forth between various frames on the page, and allowing them to work together and execute cross-iframe tasks.

Here’s an example of the full configuration, with message handler being attached to the web view:

So that’s it. Of course, WKWebView is available only on iOS 8 — but thankfully old OS support is not that big of an issue on iOS platforms.

Now I’m off to implement the orchestration code, and find a similar solution for Android!

References

  1. wkwebview-and-javascript-in-ios-8-using-swift
  2. WKWebit
  3. 2014 WWDC about WKWebView
  4. Apple API Docs:

By Wayne Chu

--

--

Draw & Code

Draw & Code is where geekery manifests itself at fintech startup Maxwell Forest. Here you will find tips, tricks and tales from our engineers and designers.