Call Swift functions from JS

I have a VIewController with WKWebView to display HTML content to my users. What I need is to get some information from my project side(swift codes) such as app version, data reports to javascrtript side. On the side of js, I'll be able to generate html codes or display elements based on swift function calls.

Here are some codes in my ViewController:

    class AboutViewController: UIViewController
    {
         @IBOutlet weak var webView: WKWebView!
          {
            didSet {
               setJS()
            }
         }
         private let jsCtx = JSContext()
         private func setJS() {
           let obj = MyVersionClass()
           jsCtx.ctx.setObject(
            obj,
            forKeyedSubscript: "versionObj")
      }
     override func viewDidLoad() {
        super.viewDidLoad()
        // build url
       webView.loadFileURL(
            url,
            allowingReadAccessTo: url)
        let request = URLRequest(url: url)
        webView.load(request)
    }

MyVersionClass is defined as:

     import JavaScriptCore
    @objc protocol JSAppVersionProtocol: JSExport
    {
        func getAppVersion() -> String
        static func createObj() -> MyVersionClass
    }
   
    class MyVersionClass: NSObject, JSAppVersionProtocol
    {
      static func createObj() -> MyVersionClass {
        let obj = MyVersionClass()
        return obj
    }

    func getAppVersion() -> String {
       ...
    }
}

The class AboutViewController will load an html file with js defined in script section

    <head>
    <script type="text/javascript" src="../version.js"></script>
    </head>
   <body onload="updateVersion()">
     ...
    <span id="appVersion">To be updated with my app version
     </span>
    ....

JS code:

    function updateVersion()
    {
      let e = document.getElementById("appVersion");
      var ver =versionObj.getAppVersion();
      // another try, see following codes
     // var ver =versionObj.createObj().getAppVersion();
      e.innerHTML = ver;
    }

I tested the js function call in my AbountViewController class right after I setObject like this:

         let result = jsCtx.evaluateScript("versionObj")
         print("\(result)"

I got the result in console like this:

        <MyApp.JSAppVersion: 0x282aa0af0>

I also tried to setObject like this:

      jsCtx.ctx.setObject(
            MyVersionClass.self,
            forKeyedSubscript: "versionType")
      // My test of script function
         let result = jsCtx.evaluateScript("versionType")
         print("\(result)" // result is <MyApp.MyVersionClass>

However, it seems that my js does not know what my swift function code is. It fails to get the app version.

Not sure what is missing or wrong? How can I set up mu swift function available in js side?

Answered by DTS Engineer in 723034022

JavaScriptCore is not the droid you’re looking for here. There’s no supported way to share a JavaScriptCore context between your app and a WKWebView [1].

The standard approach for this with WKWebView is to use the WKUserContentController class to install a script message handler.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] That’s because the WKWebView runs all of its JavaScript is a separate process for security reasons. For general background on this, watch WWDC 2018 Session 207 Strategies for Securing Web Content.

Accepted Answer

JavaScriptCore is not the droid you’re looking for here. There’s no supported way to share a JavaScriptCore context between your app and a WKWebView [1].

The standard approach for this with WKWebView is to use the WKUserContentController class to install a script message handler.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] That’s because the WKWebView runs all of its JavaScript is a separate process for security reasons. For general background on this, watch WWDC 2018 Session 207 Strategies for Securing Web Content.

I got the issue resolved by using WKScriptMessageHandler. I wrote a blog about this: Building a Bridge Between Swift and JS:

https://davidchuprogramming.blogspot.com/2022/08/build-bridge-between-swift-and-js.html

Call Swift functions from JS
 
 
Q