You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在SwiftUI中监听JavaScript回调?

在SwiftUI中实现WebView与JavaScript的消息交互

我是SwiftUI新手,已按如下方式创建了WebView:

struct WebView: UIViewRepresentable {
    @Binding var title: String
    var url: URL
    var loadStatusChanged: ((Bool, Error?) -> Void)? = nil

    func makeCoordinator() -> WebView.Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> WKWebView {
        let view = WKWebView()
        view.navigationDelegate = context.coordinator
        view.load(URLRequest(url: url))
        return view
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {
        // you can access environment via context.environment here
        // Note that this method will be called A LOT
    }

    func onLoadStatusChanged(perform: ((Bool, Error?) -> Void)?) -> some View {
        var copy = self
        copy.loadStatusChanged = perform
        return copy
    }

    class Coordinator: NSObject, WKNavigationDelegate,WKScriptMessageHandler {
        @State private var studentAppID = UserDefaults.standard.string(forKey: "studentAppID")
        let parent: WebView

        init(_ parent: WebView) {
            self.parent = parent
        }

        func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
            print(webView.url as Any)
            parent.loadStatusChanged?(true, nil)
        }

        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            print(webView as Any)
            print(webView.url as Any)
            parent.title = webView.title ?? ""
            webView.evaluateJavaScript("document.getElementsByName('applnID')[0].value='\(studentAppID!)'", completionHandler: nil)
            parent.loadStatusChanged?(false, nil)
        }

        func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
            parent.loadStatusChanged?(false, error)
        }
        
        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
//            guard let dict = message.body as? [String : AnyObject] else {
//                return
//            }
            
            print(message.body)
        }

    }
}

我想配置相关功能,以便在用户点击WebView中的按钮标签时接收来自WebView的响应。我找到了UIKit中的实现代码,但不清楚如何在SwiftUI中使用,代码如下:

class ViewController: UIViewController, WKScriptMessageHandler {
  let content = """
  <!DOCTYPE html><html><body>
  <button onclick="onClick()">Click me</button>
  <script>
  function onClick() {
    window.webkit.messageHandlers.backHomePage.postMessage("success");
  }
  </script>
  </body></html>
  """

  override func viewDidLoad() {
    super.viewDidLoad()

    let config = WKWebViewConfiguration()
    config.userContentController = WKUserContentController()
    config.userContentController.add(self, name: "backHomePage")

    let webView = WKWebView(frame: CGRect(x: 0, y: 0, width: 200, height: 200), configuration: config)

    view.addSubview(webView)

    webView.loadHTMLString(content, baseURL: nil)
  }

  func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    print(message.body)
  }
}

请问是否可以在SwiftUI中实现相同配置?如果可以,具体该怎么做?


可以实现,具体修改步骤如下:

1. 修改makeUIView方法,添加WKWebView配置

在创建WKWebView时,先初始化WKWebViewConfiguration,并将Coordinator注册为指定名称的消息处理器:

func makeUIView(context: Context) -> WKWebView {
    let config = WKWebViewConfiguration()
    // 注册消息处理器,名称要和JavaScript中调用的一致
    config.userContentController.add(context.coordinator, name: "backHomePage")
    
    let view = WKWebView(frame: .zero, configuration: config)
    view.navigationDelegate = context.coordinator
    view.load(URLRequest(url: url))
    return view
}

2. 完善Coordinator中的消息处理逻辑

给WebView添加消息回调属性,并在Coordinator中把收到的JS消息传递给SwiftUI视图:

struct WebView: UIViewRepresentable {
    @Binding var title: String
    var url: URL
    var loadStatusChanged: ((Bool, Error?) -> Void)? = nil
    // 添加JS消息回调属性
    var jsMessageReceived: ((Any) -> Void)? = nil
    
    // ... 原有代码不变
    
    // 添加链式调用方法,用于绑定回调
    func onJsMessageReceived(perform: ((Any) -> Void)?) -> some View {
        var copy = self
        copy.jsMessageReceived = perform
        return copy
    }
    
    class Coordinator: NSObject, WKNavigationDelegate,WKScriptMessageHandler {
        // ... 原有代码不变
        
        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            // 根据消息名称区分处理(多消息场景下可用)
            if message.name == "backHomePage" {
                // 将消息传递给SwiftUI视图的回调
                parent.jsMessageReceived?(message.body)
            }
        }
    }
}

3. 在SwiftUI视图中使用WebView并绑定回调

struct ContentView: View {
    @State private var webViewTitle = ""
    
    var body: some View {
        WebView(title: $webViewTitle, url: URL(string: "your-web-url")!)
            .onJsMessageReceived { message in
                // 处理收到的JavaScript消息
                print("SwiftUI收到消息:\(message)")
                // 可在此执行页面跳转、状态更新等操作
            }
    }
}

原理说明

通过WKWebViewConfiguration的userContentController注册消息处理器后,当WebView中的JavaScript调用window.webkit.messageHandlers.backHomePage.postMessage("内容")时,Coordinator的userContentController(_:didReceive:)方法会被触发,此时可以将消息传递给SwiftUI视图进行后续处理。


内容的提问来源于stack exchange,提问作者Muju

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 06:35:10