SwiftUI中WKWebView重复加载报NSURLErrorDomain -999问题求助
解决SwiftUI WKWebView重复加载引发NSURLErrorDomain -999错误的方案
问题根源
- WKWebView初始化方式错误:直接在
struct值类型中全局声明WKWebView实例,不符合SwiftUI视图生命周期管理规则,会导致视图更新时出现实例复用或重复创建的问题。 - 无限制重复加载请求:
updateUIView方法每次都会调用load(request),未判断当前是否需要加载相同请求,引发重复请求被系统取消,触发NSURLErrorCancelled(-999)错误。 - 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
相关产品推荐
相关产品推荐

