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

如何在SwiftUI中通过WKWebView实现POST请求?

SwiftUI中WKWebView实现POST请求跳转支付网关的解决方案

核心实现思路

针对页面加载自动提交表单到支付网关的场景,推荐两种可靠方案,同时解决NSURLErrorDomain error -999(请求被取消)和[ServicesDaemonManager]日志警告问题:


方案一:构造自动提交的HTML表单(适配依赖表单上下文的支付网关)

很多支付网关要求通过表单提交传递参数(依赖表单上下文或隐藏字段),这种方式最贴合Android WebView的实现逻辑:

  1. 封装WKWebView的SwiftUI视图(处理导航和认证)
import SwiftUI
import WebKit

struct WebView: UIViewRepresentable {
    let request: URLRequest
    
    func makeUIView(context: Context) -> WKWebView {
        let webView = WKWebView()
        webView.navigationDelegate = context.coordinator
        return webView
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
        // 避免重复加载导致请求被取消(解决error -999)
        guard uiView.url != request.url || !uiView.loading else { return }
        uiView.load(request)
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator()
    }
    
    class Coordinator: NSObject, WKNavigationDelegate {
        // 允许所有跳转,防止支付网关重定向被拦截
        func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
            decisionHandler(.allow)
        }
        
        // 处理HTTPS证书认证(如果支付网关用自签名证书)
        func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
            if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
                completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
            } else {
                completionHandler(.performDefaultHandling, nil)
            }
        }
    }
}
  1. 构造自动提交的HTML并加载
struct PaymentView: View {
    var body: some View {
        let paymentGatewayURL = "https://your-payment-gateway.com/authorize"
        // 构造包含自动提交逻辑的HTML表单
        let autoSubmitHTML = """
        <html>
            <body onload="document.getElementById('paymentForm').submit();">
                <form id="paymentForm" action="\(paymentGatewayURL)" method="POST">
                    <!-- 替换为支付网关要求的所有参数 -->
                    <input type="hidden" name="merchant_id" value="YOUR_MERCHANT_ID">
                    <input type="hidden" name="amount" value="100.00">
                    <input type="hidden" name="order_id" value="ORDER_12345">
                    <input type="hidden" name="return_url" value="https://your-app.com/callback">
                </form>
            </body>
        </html>
        """
        
        // 构造请求加载HTML(用about:blank作为基准URL)
        var request = URLRequest(url: URL(string: "about:blank")!)
        request.setValue("text/html; charset=utf-8", forHTTPHeaderField: "Content-Type")
        request.httpMethod = "POST"
        request.httpBody = autoSubmitHTML.data(using: .utf8)
        
        WebView(request: request)
            .edgesIgnoringSafeArea(.all)
    }
}

方案二:直接构造POST请求(适合不依赖表单上下文的场景)

如果支付网关支持直接接收POST参数,可以跳过HTML表单,直接构造请求:

struct PaymentView: View {
    var body: some View {
        guard let paymentURL = URL(string: "https://your-payment-gateway.com/authorize") else {
            return Text("无效的支付网关地址")
                .foregroundColor(.red)
        }
        
        // 构造POST请求
        var request = URLRequest(url: paymentURL)
        request.httpMethod = "POST"
        request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
        
        // 组装请求参数(URL编码)
        let parameters = [
            "merchant_id": "YOUR_MERCHANT_ID",
            "amount": "100.00",
            "order_id": "ORDER_12345",
            "return_url": "https://your-app.com/callback"
        ]
        let parameterString = parameters.map { key, value in
            "\(key)=\(value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!)"
        }.joined(separator: "&")
        request.httpBody = parameterString.data(using: .utf8)
        
        WebView(request: request)
            .edgesIgnoringSafeArea(.all)
    }
}

常见问题解决

  1. NSURLErrorDomain error -999(请求被取消)

    • 原因:WKWebView重复加载请求、导航被拦截、视图生命周期变化
    • 解决:在updateUIView中添加判断避免重复加载;确保Coordinator中允许所有跳转;保证WKWebView在请求过程中不被销毁。
  2. [ServicesDaemonManager]相关错误

    • 这是iOS系统内部日志警告,和你的代码无关,不影响功能,无需处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 19:50:20