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

如何从Swift调用JavaScript函数并获取返回结果(含闭包、无参/带参场景)

在Swift中调用JavaScript函数并获取返回结果(支持闭包、多参数场景)

在iOS开发中,使用WKWebView是实现Swift与JavaScript交互的标准方案,下面针对无参数、带参数、异步回调三种场景给出具体实现:

基础准备:初始化WKWebView

首先配置并初始化WKWebView,加载包含目标JS函数的HTML(可以是本地文件或字符串):

import WebKit

class JSBridgeHandler: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
    private var webView: WKWebView!
    // 自定义闭包,用于接收JS返回结果(可根据业务扩展)
    private var resultCallback: ((Any?, Error?) -> Void)?

    override init() {
        super.init()
        let config = WKWebViewConfiguration()
        // 注册消息处理器,用于JS异步回调
        config.userContentController.add(self, name: "swiftCallback")
        
        webView = WKWebView(frame: .zero, configuration: config)
        webView.navigationDelegate = self
        
        // 加载包含JS函数的HTML字符串
        let jsFunctions = """
        <script>
        // 无参数同步函数
        function getGreeting() {
            return "Hello from JavaScript";
        }
        
        // 带单个参数的同步函数
        function welcomeUser(username) {
            return `Welcome, ${username}!`;
        }
        
        // 带多个参数的同步函数
        function computeProduct(a, b) {
            return a * b;
        }
        
        // 异步函数,通过回调返回结果
        function loadRemoteData() {
            setTimeout(() => {
                const data = {code: 200, content: "Async data loaded successfully"};
                // 调用Swift注册的回调方法
                window.webkit.messageHandlers.swiftCallback.postMessage(data);
            }, 1500);
        }
        </script>
        """
        webView.loadHTMLString(jsFunctions, baseURL: nil)
    }
}

1. 调用无参数JS函数,通过闭包获取结果

页面加载完成后,使用evaluateJavaScript(_:completionHandler:)调用JS函数,在闭包中处理返回结果:

extension JSBridgeHandler {
    func callNoParamJSFunction(completion: @escaping (String?, Error?) -> Void) {
        guard webView.isLoading == false else {
            completion(nil, NSError(domain: "JSBridge", code: -1, userInfo: [NSLocalizedDescriptionKey: "WebView尚未加载完成"]))
            return
        }
        
        webView.evaluateJavaScript("getGreeting()") { result, error in
            completion(result as? String, error)
        }
    }
}

// 使用示例
let bridge = JSBridgeHandler()
bridge.callNoParamJSFunction { greeting, error in
    if let error = error {
        print("调用失败: \(error)")
        return
    }
    print("JS返回结果: \(greeting ?? "")")
}

2. 调用带参数的JS函数

针对单个或多个参数,推荐用JSON序列化传递参数,避免JS注入风险:

extension JSBridgeHandler {
    // 单个参数
    func callSingleParamJSFunction(username: String, completion: @escaping (String?, Error?) -> Void) {
        guard let jsonData = try? JSONSerialization.data(withJSONObject: username),
              let jsonString = String(data: jsonData, encoding: .utf8) else {
            completion(nil, NSError(domain: "JSBridge", code: -2, userInfo: [NSLocalizedDescriptionKey: "参数序列化失败"]))
            return
        }
        
        webView.evaluateJavaScript("welcomeUser(\(jsonString))") { result, error in
            completion(result as? String, error)
        }
    }
    
    // 多个参数
    func callMultiParamJSFunction(num1: Int, num2: Int, completion: @escaping (Int?, Error?) -> Void) {
        webView.evaluateJavaScript("computeProduct(\(num1), \(num2))") { result, error in
            completion(result as? Int, error)
        }
    }
}

// 使用示例
bridge.callSingleParamJSFunction(username: "Sajjad") { welcomeMsg, error in
    print(welcomeMsg ?? "") // 输出:Welcome, Sajjad!
}

bridge.callMultiParamJSFunction(num1: 5, num2: 6) { product, error in
    print(product ?? 0) // 输出:30
}

3. 调用异步JS函数,通过闭包接收回调结果

异步JS函数无法直接通过evaluateJavaScript返回结果,需要通过WKScriptMessageHandler实现双向通信:

extension JSBridgeHandler {
    func callAsyncJSFunction(completion: @escaping ([String: Any]?, Error?) -> Void) {
        guard webView.isLoading == false else {
            completion(nil, NSError(domain: "JSBridge", code: -1, userInfo: [NSLocalizedDescriptionKey: "WebView尚未加载完成"]))
            return
        }
        // 保存闭包,用于接收异步结果
        self.resultCallback = { result, error in
            completion(result as? [String: Any], error)
        }
        // 触发异步JS函数
        webView.evaluateJavaScript("loadRemoteData()") { _, error in
            if let error = error {
                self.resultCallback?(nil, error)
            }
        }
    }
    
    // 实现WKScriptMessageHandler协议,接收JS发送的消息
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if message.name == "swiftCallback" {
            self.resultCallback?(message.body, nil)
            // 清空闭包,避免重复调用
            self.resultCallback = nil
        }
    }
    
    deinit {
        // 移除消息处理器,防止内存泄漏
        webView.configuration.userContentController.removeScriptMessageHandler(forName: "swiftCallback")
    }
}

// 使用示例
bridge.callAsyncJSFunction { data, error in
    if let error = error {
        print("异步调用失败: \(error)")
        return
    }
    print("JS异步返回结果: \(data ?? [:])")
}

关键注意事项

  • 内存管理:记得在deinit中移除WKScriptMessageHandler,避免循环引用导致内存泄漏
  • 参数安全:字符串参数必须转义或序列化,防止JS注入攻击
  • 加载状态:确保WKWebView加载完成后再调用JS函数,否则会触发错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 15:38:17