Sharing Web Data with iOS Using WKWebView

I recently helped develop a native iOS app for a client that sells software to many different educational organizations. We wrote the app in Swift, and it interacts with our client’s pre-existing web API.

One challenge we faced was that many of our client’s customers require single-account, multiple-login (SAML) support through their own web portals. To support SAML, we needed an easy way to pass a user’s API credentials from a web page to our iOS application. In this post, I’ll show how this can be accomplished using WKWebView.

The Problem

The high-level overview of the issue we faced:

  • Our client provides software to many organizations, and each has users that must log in.
  • To log in, a user must identify his or her organization.
  • After identifying the organization, the user can log in using our client’s API or, in some cases, an external web site hosted by the organization.

With SAML, users are sent to the organization’s web page to log in, then redirected back to a landing page that our client controlls. Once that redirect occurs, we have the login credentials (for instance, a user token required by our client’s API). The question is: How do we pass that token to the native iOS app?


In iOS 8, Apple provided WKWebView as part of WebKit, a newer way of embedding web views in a native app. (Among other features, WKWebView boasts better performance than the older UIWebView.) In the following code samples, I will show how a developer can pass data in the form of a JavaScript object from a web page to the native iOS app.

First, create a simple UIViewController in Interface Builder and give it a custom class. I chose to name mine SamlLoginViewController. In the view controller’s loadView method, we create the WKWebView and set up the communication channel between the web and our app as follows:

import UIKit
import WebKit

class SamlLoginViewController: WKScriptMessageHandler {
    var webView: WKWebView?
    let userContentController = WKUserContentController()

    override func loadView() {

        let config = WKWebViewConfiguration()
        config.userContentController = userContentController

        self.webView = WKWebView(frame: self.view.bounds, configuration: config)
        userContentController.addScriptMessageHandler(self, name: "userLogin")

        self.view = self.webView

Before diving into loadView, note that there are two instance variables declared at the top: webView and userContentController. Further, we are going to add the WKScriptMessageHandler protocol to our class. Our class will handle messages passed from JavaScript on the web page.

To create a WKWebView, we first construct a WKWebViewConfiguration object and set its userContentController value to the WKUserContentController on our class. The WKWebView can then be constructed like this:

let config = WKWebViewConfiguration()
config.userContentController = userContentController

self.webView = WKWebView(frame: self.view.bounds, configuration: config)

After creating the WKWebView, we set up the message handler:

userContentController.addScriptMessageHandler(self, name: "userLogin")

The side-effect of addScriptMessageHandler is that it will create an object that will be added to window.webkit.messageHandlers for any webpage loaded in the WKWebView. In this case, we name the handler “userLogin” so our JavaScript code will be able to access window.webkit.messageHandlers.userLogin.

Next, we need to create the delegate method to receive messages in our view controller:

func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {
    let dict = message.body as! [String:AnyObject]
    let username    = dict["username"] as! String
    let secretToken = dict["secretToken" as! String 
    // now use the name and token as you see fit!

The JavaScript Object

The last piece of the puzzle is the JavaScript needed to send the message. The following code must be run on the page loaded into the WKWebView:

This code creates a simple HTML button and adds an even listener to call the messageHandler that is automatically set up by WKWebView. You can verify that window.webkit.messageHandlers.userLogin only exists in the context of your iOS app’s view. Just visit the URL in a browser and it will be undefined. When postMessage is called, the iOS app receives the sent JavaScript object. It’s that easy!