如何从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
相关产品推荐
相关产品推荐

