WKWebView does not call `didFinish` delegate method

854 views Asked by At

I am implementing a custom WKWebViewClass. The init() is settings the navigationDelegate to self, and starting the load. However, didFinish is never called. I am getting a didFailProvisionalNavigation error

didFailProvisionalNavigation The operation couldn’t be completed. (kCFErrorDomainCFNetwork error 1.)

let pdf = generatePDF(frame: view.bounds)
view.addSubview(pdf)

pdf.startGenerating(){

}



public class generatePDF: WKWebView, WKUIDelegate, WKNavigationDelegate {
static var observer: NSObjectProtocol!

public override init(frame: CGRect, configuration: WKWebViewConfiguration = WKWebViewConfiguration()) {
    super.init(frame: frame, configuration: configuration)
    doInit()
}

func doInit() {
    DocMakerInfo.isDoneLoading = false
    self.navigationDelegate = self
    self.uiDelegate = self
    print("didInit")
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

public func startGenerating(completion: @escaping () -> Void) {
    openDocument(fileName: DocMakerInfo.fileName, extenstion: DocMakerInfo.extenstion)
    completion()
}

private func openDocument(fileName:String, extenstion:String){
    do {
        #if targetEnvironment(macCatalyst)
            let homeDirURL = (NSSearchPathForDirectoriesInDomains(.downloadsDirectory, .userDomainMask, true) as [String]).first!
            let docURL = URL(string: "file://\(homeDirURL)/")!
        #else
            let docURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
        #endif
        let contents = try FileManager.default.contentsOfDirectory(at: docURL, includingPropertiesForKeys: [.fileResourceTypeKey], options: .skipsHiddenFiles)
        for url in contents {
            #if targetEnvironment(macCatalyst)
                if url.description.contains("/\(fileName)\(extenstion)") {
                    webView.frame = view.bounds
                    let fileURL = docURL.appendingPathComponent("\(fileName)\(extenstion)")
                    let dataFileMimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
                    do {
                        let fileContent = try Data(contentsOf: url)
                        self.load(fileContent, mimeType: dataFileMimeType, characterEncodingName: "", baseURL: url)
                        //self.view = self.webView
                        Dynamic.NSWorkspace.sharedWorkspace.activateFileViewerSelectingURLs([url])
                    } catch let error {
                        print(error.localizedDescription)
                    }
                }
            #else
                if url.description.contains("\(fileName)\(extenstion)") {
                    self.bounds = CGRect(x: 0, y: 0, width: 700.00, height: 1000.00)
                    let fileURL = docURL.appendingPathComponent("\(fileName)\(extenstion)")
                    let fileURL = docURL.appendingPathComponent("\(fileName)\(extenstion)")
                    self.loadFileURL(fileURL, allowingReadAccessTo: fileURL)
                    self.load(URLRequest(url: fileURL))
                    print("startedLoading")
                }
            #endif
        }
    } catch {
        Swift.print(#file, #function, "could not locate \(extenstion) file !!!!!!!")
    }
}

public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
    print("didStartProvisionalNavigation")
}

public func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
    decisionHandler(.allow)
}

public func webView(_ webView: WKWebView, didFailProvisionalNavigation: WKNavigation!, withError error: Error) {
    print("didFailProvisionalNavigation", error.localizedDescription)
}

public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
    print("didFail", error.localizedDescription)
}

public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    print("doneloading")
    let config = WKPDFConfiguration()
    webView.createPDF(configuration: config) { result in
        switch result {
        case .success(let data):
            #if targetEnvironment(macCatalyst)
                
            #else
            let destURL = FileManager.default.temporaryDirectory.appendingPathComponent("\(DocMakerInfo.fileName).pdf")
            do {
                try data.write(to: destURL)
                print("createpdf")
                DocMakerInfo.isDoneLoading = true
            } catch {
                print("didFinish", error)
            }
            #endif
        case .failure(let error):
            print("create pdf failure: \(error)")
        }
    }
}
}
1

There are 1 answers

6
Shawn Frank On

Few things I can note:

  1. Give your PDFView a frame

What you have:

let pdf = generatePDF()

What you should do is give it a frame using autolayout or frames because even if it does load something, without a frame you cannot see anything

So for example:

// View is the container the pdf view will be in
let pdf = generatePDF(frame: view.bounds)
  1. Probably the most important one, add your pdf view to the view hierarchy or nothing will happen
let pdf = generatePDF(frame: view.bounds)

// Add this
view.addSubview(pdf)

pdf.startGenerating(){
    
}
  1. I am assuming everything in your openDocument works well as it cannot be tested at my end, however, I just used my local PDF and this worked fine with all the notifications working

Give this a try and see if you have any better results on your end

Update to include the full source code

This is the same as your PDF class, I have only changed the openDocument function to open a local PDF file so this is the only difference

public class generatePDF: WKWebView, WKUIDelegate, WKNavigationDelegate {
    static var observer: NSObjectProtocol!
    
    public override init(frame: CGRect, configuration: WKWebViewConfiguration = WKWebViewConfiguration()) {
        super.init(frame: frame, configuration: configuration)
        doInit()
    }
    
    func doInit() {
        self.navigationDelegate = self
        self.uiDelegate = self
        print("didInit")
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public func startGenerating(completion: @escaping () -> Void) {
        openDocument(fileName: "test", extension: "pdf")
        completion()
    }
    
    private func openDocument(fileName: String, extension: String) {
        
        // I have replaced your file with a local PDF file in my bundle
        
        if let pdfURL = Bundle.main.url(forResource: "test",
                                        withExtension: "pdf",
                                        subdirectory: nil,
                                        localization: nil)  {
            do {
                let data = try Data(contentsOf: pdfURL)
                self.load(data, mimeType: "application/pdf", characterEncodingName:"", baseURL: pdfURL.deletingLastPathComponent())
                print("started loading")
            }
            catch {
                // catch errors here
            }
            
        }
        
        
    }
    
    public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
        print("didStartProvisionalNavigation")
    }
    
    public func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
        decisionHandler(.allow)
    }
    
    public func webView(_ webView: WKWebView, didFailProvisionalNavigation: WKNavigation!, withError error: Error) {
        print("didFailProvisionalNavigation", error.localizedDescription)
    }
    
    public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
        print("didFail", error.localizedDescription)
    }
    
    public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        print("doneloading")
    }
}

Then I have a ViewController

class WebViewTestVC: UIViewController {
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        let pdf = generatePDF(frame: view.bounds)
        
        // Add this
        view.addSubview(pdf)
        
        pdf.startGenerating(){
            
        }
    }
}

The result is a webview loading the PDF:

PDF loaded in a WKWebView swift iOS

The print out on my console:

didInit
started loading
didStartProvisionalNavigation
doneloading

Update based on Will's comments

I am now getting this error. didFailProvisionalNavigation The operation couldn’t be completed. (kCFErrorDomainCFNetwork error 1.)

This could be a file permission error based on what I read here and here

Instead of what you do here

let fileContent = try Data(contentsOf: url)
self.load(fileContent, mimeType: dataFileMimeType, characterEncodingName: "", baseURL: url)

I changed this to, so give that a try:

loadFileURL(url, allowingReadAccessTo: url)

I have added an excel file to my documents directory and tried to load it in the webview, here are my changes in the generatePDF class:

public func startGenerating(completion: @escaping () -> Void) {
    openDocument(fileName: "Products", ext: "xlsx")
    completion()
}


private func openDocument(fileName: String, ext: String) {
    
    do {
        let docURL = try FileManager.default.url(for: .documentDirectory,
                                                 in: .userDomainMask,
                                                 appropriateFor: nil,
                                                 create: false)
        
        let contents
            = try FileManager.default.contentsOfDirectory(at: docURL,
                                                          includingPropertiesForKeys: [.fileResourceTypeKey],
                                                          options: .skipsHiddenFiles)
        
        for url in contents {
            if url.description.contains("/\(fileName).\(ext)") {
                
                loadFileURL(url, allowingReadAccessTo: url)
            }
        }
    }
    catch {
        print(error)
    }
}

The excel loads fine and also the notifications of done loading is called properly

Load Excel xlsx in WKWebView swift iOS