Creating links between WKWebView and Native code

The past month I’ve been working for a customer that had some pretty specific requirements for their product; they wanted an application that used a particular map component, the same one they use in their web app. That meant two things: javascript 🙈 and trying to find out how to communicate between the native code and the webview that would house the map.

The Problem

Let’s take some more time to sketch out the problem. We’re dealing with two major components here: a WKWebView to house the map and the native user interface. We want to send messages both ways between these components. When the user interacts with certain objects on the map, we need to trigger events in our native code to perform actions, like show an interface element, or trigger a segue to a new view. We also need to send messages to the webview from our native code, so we can delegate all the heavy lifting to the native application to minimize the amount of calculations javascript has to make.

The Solution

Lucky for us, WKWebView offers everything we need. With a bit of tinkering, we can configure it to do exactly what we need! Setting up a webview is trivial and well-documented, so I’m not going to explain that here. You can either instantiate it through code or simply drag one in your view in Interface Builder and create an outlet for it. I chose to do the latter.

Let’s make our ViewController conform to WKScripMessageHandler and WKNavigationDelegate:

Don’t forget to set the webview’s navigationDelegate to self in viewDidLoad!

Alright, now we’ve got part of our webview setup. We’ll come back to this later, but first, we will dive into some javascript.

Javascript 🙈

A quick disclaimer: I am not a web developer. If you are a seasoned javascript professional, please shield your eyes from the code that follows.

All we need to do in javascript is create a few functions for handling incoming responses, and sending responses back to the webview. I expect you’re comfortable with setting up an index.html file, so I won’t show you how to do that.

Now, we probably want to do something else than just logging the values we receive, but for the sake of explaining the concept, this will do. Say we’re keeping a list of people in the webview, the addPerson function will be called from the native context to add it to the webview. The sendNameToNative function can be called to send a message back to the native code. Let’s add the last building blocks to get everything working!

The last few steps

We need to do some things in our loadView function, so we will override that. In here we’ll set some properties for our webview to listen to the callback from javascript when the sendNameToNative function is called.

You might need to fiddle with this a bit depending on how you set up your views. One thing you might to do is change the frame to be equal to view.frame, and another thing that might fix problems here is adding view = webview after the last line. This might create some other problems.

There’s only one thing left to do here, which is actually sending a message to the webview.

This is the final piece to our puzzle; actually sending the message to the webview. Calling this function allows us to execute javascript in the webview. As you can see in the example, when we want to send a String, we need to add ‘ ’ around it. We can now call functions like this in the webView(_:didFinish:) function to set up the webview on load, and call it after interaction with the native user interface to send more messages.

Keep in mind that when you’re handling properties entered by the user, you might want to validate it so you don’t execute malicious javascript in the webview.

What’s next?

Now that you’ve got the structure laid out, you can go wild:

  • Actually handle the response to trigger certain actions in native code, like animations or segues.
  • Add more handlers to handle different responses from the webview, creating a tightly-knit connection between the two

Congratulations! If you’re reading this you’ve created a connection between a WKWebView and the native context of your application. This opens a lot of possibilities, and you can take this as far as you want. If you created something awesome with this, please share it with me!

If you liked this article, check out the other articles I wrote! If you’d like to get in touch, you can follow me on Twitter or shoot me a message on LinkedIn. Also, if you need any help with anything iOS Programming related, you can join this Discord server created for exactly that purpose :)