swiftuiwkwebviewuiviewrepresentablensurlerrordomain

SwiftUI WkwebView UIViewRepresentable is called twice or more and get error NSURLErrorDomain error -999


this page is loading twice. I Tried to intercept the error, using some "isLoading" @State var, but is not working. I think in my setup there is some other error, maybe in the way I initialize the webView, I think it should not be initialized in struct but in makeUIView, but as you can see I need to have webView global.

I get error

The operation couldn’t be completed. (NSURLErrorDomain error -999).

I'm calling the code this way:

let customWebView = PreferredAppsCustomWebView(url: url, canGoBack: $canGoBack, canGoForward: $canGoForward) { error in
                            self.errorMessage = error
                            self.showAlert = true
                        }
customWebView //not clear why this is required in order the element to work, but that is

the whole code for the web view

import SwiftUI
import WebKit

struct PreferredAppsCustomWebView: UIViewRepresentable {

let url: URL
@Binding var canGoBack: Bool
@Binding var canGoForward: Bool

var webView = WKWebView()

var handleError: ((String) -> Void)?

//MARK: - UIViewRepresentable
func makeUIView(context: Context) -> WKWebView {
    webView.navigationDelegate = context.coordinator
    return webView
}

func updateUIView(_ uiView: WKWebView, context: Context) {
    let request = URLRequest(url: url)
    Logger.info("loading url: \(url)")
    uiView.load(request)
}


//MARK: - Delegate methods section
func makeCoordinator() -> Coordinator {
    Coordinator(self)
}

class Coordinator: NSObject, WKNavigationDelegate {
    
    var parent: PreferredAppsCustomWebView
    
    init(_ parent: PreferredAppsCustomWebView) {
        self.parent = parent
    }
    
    func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
    }
    
    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)
        
        //this one is here only for info purposes.
        let nsError = error as NSError
        if nsError.code == NSURLErrorCancelled {
            Logger.error("maybe we are loading this webPage twice")
            return
        } else {
            //self.parent.handleError?(error.localizedDescription) //this one is necessary for propagating errors for webView.
        }
    }
    
    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    }
    
}

func goBack() {
    if webView.canGoBack {
        Logger.info("ok")
        webView.goBack()
    } else {
        Logger.info("no")
    }
}

func goForward() {
    if webView.canGoForward {
        Logger.info("ok")
        webView.goForward()
    } else {
        Logger.info("no")
    }
}

func reload() {
    self.webView.reload()
}

}

Solution

  • if it doesn't have the problem with other correct urls, then I guess your url is redirected or has another problem.
    To find it, you can check the httpResponse

    func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
        if let httpResponse = navigationResponse.response as? HTTPURLResponse {
            if httpResponse.statusCode >= 300 && httpResponse.statusCode < 400 {
                print("Redirected to: \(httpResponse.url?.absoluteString ?? "unknown")")
            } else {
                print("Not redirected")
            }
        }
        decisionHandler(.allow)
    }