Inject JavaScript into WKWebView

In Swift, it is possible to inject a JavaScript code into a WKWebView and execute it from your app. In this tutorial, I will show you how to do it.

Step 1: Load an HTML File from a Local File

To load an HTML file from a local file in a WKWebView, you need to first obtain the URL for the HTML file located within your project’s bundle. Then, you can use the loadFileURL method of WKWebView to load the HTML content.

Here’s how you can implement this in your ViewController:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    
    // Obtain the URL for the HTML file
    guard let myProjectBundle = Bundle.main,
          let myUrl = myProjectBundle.url(forResource: "index", withExtension: "html") else {
        return
    }
    
    // Load the HTML file into the WKWebView
    myWebView.loadFileURL(myUrl, allowingReadAccessTo: myUrl)
}

Also, you can use a web URL to load this HTML. If you want a complete tutorial about loading HTML files from an App Bundle, read this tutorial.

Step 2: Load JavaScript Code from a Local File

Next, you’ll need to read the contents of a JavaScript file from your project’s bundle. You can use the Bundle class to locate the file and read its contents as a string.

Here’s the function that reads the JavaScript file:

func getMyJavaScript() -> String {
    if let filepath = Bundle.main.path(forResource: "script", ofType: "js"),
       let jsString = try? String(contentsOfFile: filepath) {
        return jsString
    } else {
        return ""
    }
}

Step 3: Create a WKUserScript Object

Once you have the JavaScript code as a string, you can create a WKUserScript object. This object allows you to inject custom JavaScript code into your webpage loaded in the WKWebView.

Here’s how to create a WKUserScript object:

let js = getMyJavaScript()
let script = WKUserScript(source: js, injectionTime: .atDocumentEnd, forMainFrameOnly: false)

Step 4: Inject JavaScript into an HTML Document

After creating the WKUserScript object, you can add it to the WKWebView‘s configuration using the userContentController. This controller manages the scripts that are injected into the webpage.

Here’s how to inject the JavaScript into the HTML document:

config.userContentController.addUserScript(script)

Communication Between Swift and Javascript

The userContentController(_:didReceive:) function is part of the WKScriptMessageHandler protocol, which enables communication between JavaScript running in a WKWebView and your native Swift code. When JavaScript posts a message using window.webkit.messageHandlers.<handlerName>.postMessage(), the userContentController(_:didReceive:) function receives the message on the Swift side.

Here’s how it works:

  • JavaScript Side: When you want to send a message from JavaScript to Swift, you use window.webkit.messageHandlers.<handlerName>.postMessage(). The <handlerName> corresponds to the name you used when adding a message handler in your Swift code.
  • Swift Side: To set up the receiver in Swift, you conform to the WKScriptMessageHandler protocol and implement the userContentController(_:didReceive:) function. Within this function, you check the message.name property to determine which message handler the message was intended for.

Here’s an example of how to set up a message handler in Swift:

let controller = WKUserContentController()
controller.add(self, name: "messageHandler")

And here’s the corresponding userContentController(_:didReceive:) function:

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    if message.name == "messageHandler" {
        // Handle the message from JavaScript
        print(message.body)
    }
}

In the JavaScript code, you would send a message like this:

window.webkit.messageHandlers.messageHandler.postMessage('Change Background button tapped! (Message from JavaScript)');

When this line of JavaScript executes, the userContentController(_:didReceive:) function in your Swift code will be called, and it will print the message sent from JavaScript. This setup allows you to execute JavaScript functions from Swift and vice versa, enabling a two-way communication channel between the web content and your native app code like below.

Inject JavaScript into an HTML Document

Complete Code Example

Putting it all together, here’s a complete example of a ViewController that loads an HTML file, injects JavaScript, and handles messages from JavaScript:

import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler {
    
    var myWebView: WKWebView!
    
     override func viewDidLoad() {
        super.viewDidLoad()
        
        let config = WKWebViewConfiguration()
        let js = getMyJavaScript()
        let script = WKUserScript(source: js, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
        
        config.userContentController.addUserScript(script)
        config.userContentController.add(self, name: "messageHandler")
        
        myWebView = WKWebView(frame: view.bounds, configuration: config)
        myWebView.uiDelegate = self
        myWebView.navigationDelegate = self
        view.addSubview(myWebView!)
 
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        let myProjectBundle:Bundle = Bundle.main
        
        let myUrl = myProjectBundle.url(forResource: "index", withExtension: "html")!
        myWebView.loadFileURL(myUrl,allowingReadAccessTo: myUrl)
        
    }
    
    func getMyJavaScript() -> String {
        if let filepath = Bundle.main.path(forResource: "script", ofType: "js") {
            do {
                return try String(contentsOfFile: filepath)
            } catch {
                return ""
            }
        } else {
           return ""
        }
    }
    
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        print(message.body)
    }
   
}

Javascript Code:

document.getElementById('colorButton').addEventListener('click', function() {
    document.body.style.backgroundColor = '#808080';
    document.body.style.fontSize = '18px';
    document.querySelector('h1').style.color = '#FF0000';
    this.textContent = 'Background Color Changed';
    window.webkit.messageHandlers.messageHandler.postMessage('Change Background button tapped! (Message from JavaScript)');
});

HTML Code:

<!DOCTYPE html>
<html>
<head>
    <title>Appsdeveloperblog</title>
    <style>
        body { background-color: #FFFFF; font-family: Arial, sans-serif; }
        h1 { color: #22222A; }
    </style>
</head>
<body>
    <h1>Welcome to the Appsdeveloperblog</h1>
    <p>This is a simple demonstration of loading HTML content in a WKWebView.</p>
    <!-- Added button -->
    <button id="colorButton">Change Background Color</button>
</body>
</html>

Inject JavaScript into an HTML Document

Conclusion

If you are interested in video lessons on how to write Unit tests and UI tests to test your Swift mobile app, check out this page: Unit Testing Swift Mobile App

I hope this tutorial was helpful to you. You now know how to Inject JavaScript into WKWebView.

To learn more about Swift and to find other code examples, check the following page: Swift Code Examples.

Keep coding, and happy learning!

Leave a Reply

Your email address will not be published. Required fields are marked *