'Swift WKWebView Class

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)")
        }
    }
}
}


Solution 1:[1]

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

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1