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

SwiftUI中WKWebView重复加载报NSURLErrorDomain -999问题求助

解决SwiftUI WKWebView重复加载引发NSURLErrorDomain -999错误的方案

问题根源

  1. WKWebView初始化方式错误:直接在struct值类型中全局声明WKWebView实例,不符合SwiftUI视图生命周期管理规则,会导致视图更新时出现实例复用或重复创建的问题。
  2. 无限制重复加载请求:updateUIView方法每次都会调用load(request),未判断当前是否需要加载相同请求,引发重复请求被系统取消,触发NSURLErrorCancelled(-999)错误。
  3. Coordinator持有值类型引用问题:原Coordinator持有PreferredAppsCustomWebView值类型实例,当视图更新时,Coordinator中的parent会变成旧实例,导致状态更新失效。

修复后的完整代码

1. 添加WebView包装类

用于在值类型的struct中安全持有引用类型的WKWebView实例:

import SwiftUI
import WebKit

class WebViewWrapper: ObservableObject {
    let webView = WKWebView()
}

2. 修改PreferredAppsCustomWebView组件

struct PreferredAppsCustomWebView: UIViewRepresentable {
    let url: URL
    @Binding var canGoBack: Bool
    @Binding var canGoForward: Bool
    var handleError: ((String) -> Void)?
    
    // 使用@StateObject持有WebView实例,保证生命周期与视图一致
    @StateObject private var webViewWrapper = WebViewWrapper()
    
    // MARK: - UIViewRepresentable
    func makeUIView(context: Context) -> WKWebView {
        return webViewWrapper.webView
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
        // 仅当当前URL不同且未在加载时,才发起新请求
        guard uiView.url != url, !uiView.isLoading else { return }
        let request = URLRequest(url: url)
        Logger.info("loading url: \(url)")
        uiView.load(request)
    }
    
    // MARK: - Coordinator
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, WKNavigationDelegate {
        // 使用弱引用避免循环引用,同时保证访问最新的视图实例
        weak var parent: PreferredAppsCustomWebView?
        
        init(_ parent: PreferredAppsCustomWebView) {
            self.parent = parent
            super.init()
            // 初始化时绑定导航代理
            parent.webViewWrapper.webView.navigationDelegate = self
        }
        
        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            DispatchQueue.main.async {
                self.parent?.canGoBack = webView.canGoBack
                self.parent?.canGoForward = webView.canGoForward
            }
        }
        
        func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
            Logger.error(error.localizedDescription)
            
            let nsError = error as NSError
            if nsError.code == NSURLErrorCancelled {
                Logger.error("重复加载请求已被系统取消")
                return
            } else {
                self.parent?.handleError?(error.localizedDescription)
            }
        }
        
        func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
            // 可在此添加导航拦截逻辑
            decisionHandler(.allow)
        }
    }
    
    // MARK: - 页面操作方法
    func goBack() {
        if webViewWrapper.webView.canGoBack {
            Logger.info("执行返回操作")
            webViewWrapper.webView.goBack()
        } else {
            Logger.info("无历史页面可返回")
        }
    }
    
    func goForward() {
        if webViewWrapper.webView.canGoForward {
            Logger.info("执行前进操作")
            webViewWrapper.webView.goForward()
        } else {
            Logger.info("无后续页面可前进")
        }
    }
    
    func reload() {
        webViewWrapper.webView.reload()
    }
}

关键修复点说明

  • 用@StateObject管理WKWebView:通过WebViewWrapper包装类,结合@StateObject保证WKWebView实例的生命周期与SwiftUI视图一致,避免重复初始化。
  • 避免重复加载:在updateUIView中判断当前webView的URL是否与目标URL一致,且未处于加载状态时才发起请求,彻底解决重复加载导致的-999错误。
  • Coordinator使用弱引用:修改Coordinator持有parent的弱引用,确保视图更新时能访问到最新的实例,正确更新canGoBack、canGoForward等绑定状态。

调用代码优化建议

无需提前创建视图变量,直接在SwiftUI的body中声明视图即可:

PreferredAppsCustomWebView(url: url, canGoBack: $canGoBack, canGoForward: $canGoForward) { error in
    self.errorMessage = error
    self.showAlert = true
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 09:03:14