iOS WKWebView Communication Using Javascript and Swift

Mark Waters
John Lewis Partnership Software Engineering
5 min readJun 17, 2019
Photo by Youssef Sarhan on Unsplash

It’s common for native iOS apps to include web components, particularly to display web content. However, communicating with a web view is not always straightforward as it requires a “bridge” between Swift and Javascript.

Recently I worked on a problem where a user journey started in a web view but, due to a hardware integration, most of the logic and processing had to be performed in the native iOS app. Having finished the processing, a callback to the web-view was required to pass a success or failure result and the corresponding values that the web content required to continue.

In iOS there are two types of web view commonly used, a Safari View Controller and a WKWebView. Fortunately, I already had access to a WKWebView (from WebKit), this component natively provides the “bridge” required to communicate between Swift and Javascript . A WKWebView allows you to load a URL into a web view but also gives the user many configuration options and methods to interact with native iOS code. This article will cover the implementation and behaviour of this “bridge” between the two technologies.

Prerequisites:

  • Xcode 10, Swift 5

Key Components:

  • WKWebView — allows web content to be loaded via a url
  • WKScriptMessage — object created when a postMessage() is received
  • WKUserContentController — manages javascript posts and injection
  • WKScriptMessageHandler — protocol to access WKScriptMessage delegate methods
  • WKWebViewConfiguration — config passed to WKWebView

Overview:

The following high level diagram shows the journey to and from the web-view — I’ve used the generic term “doStuff” for the method calls, please use your imagination to replace this with whatever behaviour you like :)

Trigger a postMessage call:

In the following example the javascript will call postMessage on the doStuffMessageHandler, later in our app code we will add a string that matches the handler name. You can call it whatever you like, just remember you are looking for the name that comes before the postMessage call and it will be referred to as the message “name” when the app recognises the post (see next section).

A postMessage call can be stubbed out in Xcode by adding a blank file to your project, for convenience name it index.html, Add the following code to the file:

This code can be executed by triggering a web view request:

When executed the postMessage() will be triggered. In this example two parameters are passed but you can add as many as you like. The javascript can be embedded in any web page that is loaded into your web view. Alternatively WKUserScripts can be injected into the webpage using the addUserScript() method on the userContentController. Scripts added with addUserScript() can be instructed to trigger when the page starts or finishes loading.

Listen for javascript postMessage calls in the iOS app

To add a javascript message handler the handler name needs adding to the configuration object passed into the WKWebView at initialisation time. This is done by calling the add() method of the userContentContoller property on the WKWebViewConfiguration object and passing the name of the handler. The class receiving the message also needs to be passed into the add() method as a parameter, as I’m referring to the calling class in the example “self” will be passed in here:

The class passed into the add() method (i.e. the one which is going to listen to the postMessage trigger) must then conform to the WKScriptMessageHandler Protocol. This allows our “target” class to access delegate methods which register that javascript has been called for the specific handler name. In this example I’ve extended the class WebViewContoller to conform to the protocol and added the didReceiveMessage delegate method:

When a postMessage trigger is called this delegate method notifies the app with a WKScriptMessage object. The WKScriptMessage contains a “name” which equates to the handler name and a “body” which will include any text or parameters passed with the post. We can switch on the message name to ensure the correct message has been passed to the app then parse the body to get the parameters. Remember, if switching on a string you will need to handle any default cases gracefully.

Communicating back to the web-view

Once the app has consumed the web view data and needs to hand back control to the web view, the app can invoke the WKWebView’s evaluateJavascript method. This method takes a String which should include the javascript function plus any arguments, therefore any arguments passed with the function have to be converted back to a String. The second parameter is a completion block which executes when the evaluation of the script has finished, it can also pass back an error for validation purposes.

In the above example I have passed JSON formatted data as a string. You can convert any Encodable or Codable type to JSON format then convert it back to a String. Depending on how you wish to receive the data in the web view you may need to pass the parameters in a string block — to do this (as in the example above) you can use a combination of double and single quotes.

The script you are “evaluating” must live in the webpage loaded in the web-view at the time this script is triggered. If it does not you can look at injecting it with the addUserScript() method on the userContentController. This is the final stage of communication with the web view. The web code can then parse the payload of the method appropriately.

--

--