iOS Swift如何监听WKWebView中网页发送的window.postMessage消息?
没问题,这事儿我熟!电商APP里用WKWebView处理下单支付后监听网页的window.postMessage回调是非常常见的需求,我给你一套完整的Swift实现方案,直接就能落地:
完整实现步骤
1. 配置WKWebView并注入拦截脚本
首先,我们需要给WKWebView配置消息处理器,同时注入一段JS脚本——因为WKWebView默认监听的是webkit.messageHandlers的消息,而网页用的是全局的window.postMessage,所以得把网页的postMessage消息转发到我们的处理器上。
import WebKit class OrderPayViewController: UIViewController, WKScriptMessageHandler { private var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() setupWebView() loadPayPage() } private func setupWebView() { // 初始化WKWebView配置 let config = WKWebViewConfiguration() let userContentController = WKUserContentController() // 注册消息处理器,名字"orderCallback"要和后面JS里的对应 userContentController.add(self, name: "orderCallback") // 注入JS脚本:拦截window.postMessage并转发给APP let interceptScript = """ // 保存原生postMessage方法,避免覆盖后影响网页内部逻辑 const originalPostMessage = window.postMessage; window.postMessage = function(message, targetOrigin, transfer) { // 把消息转发给APP的WKWebView处理器 window.webkit.messageHandlers.orderCallback.postMessage(message); // 执行原生postMessage逻辑(如果网页本身有其他监听,保留这个很重要) originalPostMessage(message, targetOrigin, transfer); }; """ let userScript = WKUserScript(source: interceptScript, injectionTime: .atDocumentStart, forMainFrameOnly: true) userContentController.addUserScript(userScript) config.userContentController = userContentController // 创建并添加WKWebView到页面 webView = WKWebView(frame: view.bounds, configuration: config) view.addSubview(webView) // 适配安全区域(可选,根据你的布局调整) webView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), webView.leadingAnchor.constraint(equalTo: view.leadingAnchor), webView.trailingAnchor.constraint(equalTo: view.trailingAnchor), webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) ]) } private func loadPayPage() { // 替换成你的下单支付网页URL guard let payUrl = URL(string: "https://your-shop.com/pay") else { return } webView.load(URLRequest(url: payUrl)) } }
2. 实现消息接收与处理逻辑
接下来实现WKScriptMessageHandler协议的方法,接收网页发来的消息,并执行APP内的后续操作:
// MARK: - WKScriptMessageHandler extension OrderPayViewController { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { // 只处理我们注册的"orderCallback"消息 guard message.name == "orderCallback" else { return } // 解析消息内容(根据网页实际发送的格式调整,这里假设是字符串) switch message.body { case let successMsg as String where successMsg == "order_paid": // 订单支付成功:比如跳转订单详情、弹出成功提示 print("订单已支付成功!") showSuccessAlert() navigateToOrderDetail() case let cancelMsg as String where cancelMsg == "order_canceled": // 订单取消:返回上一页或关闭当前页面 print("用户取消了订单") navigationController?.popViewController(animated: true) default: print("收到未知回调消息:\(message.body)") } } // 示例:弹出成功提示 private func showSuccessAlert() { let alert = UIAlertController(title: "支付成功", message: "你的订单已完成支付", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "确定", style: .default)) present(alert, animated: true) } // 示例:跳转到订单详情页 private func navigateToOrderDetail() { let detailVC = OrderDetailViewController() navigationController?.pushViewController(detailVC, animated: true) } }
3. 避免内存泄漏的关键操作
WKWebView对WKScriptMessageHandler的引用是强引用,如果直接用ViewController作为处理器,可能会导致内存泄漏。可以用一个弱引用代理类来解决:
// 弱引用代理类,避免WKWebView强引用ViewController class WeakScriptMessageProxy: NSObject, WKScriptMessageHandler { weak var delegate: WKScriptMessageHandler? init(delegate: WKScriptMessageHandler) { self.delegate = delegate super.init() } func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { delegate?.userContentController(userContentController, didReceive: message) } }
然后修改注册处理器的代码:
// 原来的userContentController.add(self, name: "orderCallback") 替换成: userContentController.add(WeakScriptMessageProxy(delegate: self), name: "orderCallback")
同时,记得在ViewController销毁时移除处理器:
deinit { webView.configuration.userContentController.removeScriptMessageHandler(forName: "orderCallback") print("OrderPayViewController已销毁") }
测试小技巧
你可以在网页里加个测试按钮,快速验证回调是否生效:
<button onclick="window.postMessage('order_paid', '*')">模拟支付成功</button> <button onclick="window.postMessage('order_canceled', '*')">模拟取消订单</button>
点击这些按钮,就能看到APP里对应的逻辑是否执行啦。
内容的提问来源于stack exchange,提问作者KP26
相关产品推荐
相关产品推荐

