Search code examples
iosswiftpdfwebviewwkwebview

WKWebView save pdf to ibooks, save pdf from link


I'm Junior and currently working on project where Im in WKWebView and there are links to open pdf. I can open it in Safari and then open in iBooks, but i want it to do it inside my app. Is it possible ?

Here are pics how it looks like:

picture where i can select pdf

description1

picture what it will open

description2 my webview class

class WebVC: UIViewController, WKUIDelegate {



var webView: WKWebView!

override func viewDidLoad() {
    super.viewDidLoad()
    let myURL = NSURL(string: "\(savedURL!)")
    let myRequest = URLRequest(url: myURL! as URL)
    webView.load(myRequest)
    webView.allowsBackForwardNavigationGestures = true
    webView.allowsLinkPreview = false
}

override func viewWillAppear(_ animated: Bool) {
    self.navigationController?.toolbar.isHidden = false
}

override func loadView() {
    let webConfiguration = WKWebViewConfiguration()
    webView = WKWebView(frame: CGRect(x: 100, y: 100, width: 110, height: 110), configuration: webConfiguration)
    webView.uiDelegate = self
    view = webView

}

@IBAction func logoutPressed(_ sender: AnyObject) {
    defaults.set(false, forKey: "isLogged")
    defaults.set("EMPTY URL", forKey: "savedURL")
    _ = self.navigationController?.popToRootViewController(animated: true)
}


@IBAction func goBack(_ sender: Any?) {
    if (self.webView.canGoBack) {
        self.webView.goBack()
    }
}

}

Solution

    • To open pdf in webview

      //step 1

      webview.uiDelegate = self
      

      //step 2 - implement delegate func (This function will let you open any clicked pdf in webview)

      func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
      
           // open in current view
           webView.load(navigationAction.request)
           // don't return a new view to build a popup into (the default behavior).
           return nil;
       }
      
    • Download

    In my case once i have opened pdf in webview, i have download button which i use to download the current opened pdf

    //step 3 - delegate funcs

        func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
            decisionHandler(WKNavigationActionPolicy.allow)
        }
        //find the mimeTime of the current url opened in webview, If it's pdf, we'll give option to download 
        func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
            
            if let mimeType = navigationResponse.response.mimeType {
                //check if mime exists in our acceptable mimes list
                if self.isMimeTypeConfigured(mimeType) {
                    self.mime = mimeType
                    self.currentUrl = navigationResponse.response.url
                    addDownloadButton()
                }
            }
            decisionHandler(.allow)
        }
    

    //step 4 - Create a extension with helper functions

    //=== PDF downloading ===
    struct MimeType {
        var type:String
        var fileExtension:String
    }
    extension DownloadManual {
        //button
        private func addDownloadButton(){
            let btn = UIBarButtonItem(image: UIImage(systemName: "tray.and.arrow.down.fill"), style: .plain, target: nil, action: #selector(downloadTapped))
            self.navigationItem.rightBarButtonItem = btn
        }
        @objc func downloadTapped(){
            self.showActivityIndicator(show: true)
            if let url = currentUrl {
                print("download from: \(url)")
                let filename = getDefaultFileName(forMimeType: self.mime)
                
                downloadData(fromURL: url, fileName: filename) { success, destinationURL in
                    if success, let destinationURL = destinationURL {
                        
                        self.showActivityIndicator(show: false)
                        print("download result: \(success), \(destinationURL)")
                        self.fileDownloadedAtURL(url: destinationURL)
                    }
                }
            }
        }
        
        //helper funcs
        private func isMimeTypeConfigured(_ mimeType:String) -> Bool {
            for record in self.mimeTypes {
                if mimeType.contains(record.type) {
                    return true
                }
            }
            return false
        }
        
        func fileDownloadedAtURL(url: URL) {
            print("downloaded at: \(url)")
            DispatchQueue.main.async {
                let activityVC = UIActivityViewController(activityItems: [url], applicationActivities: nil)
                activityVC.popoverPresentationController?.sourceView = self.view
                activityVC.popoverPresentationController?.sourceRect = self.view.frame
                activityVC.popoverPresentationController?.barButtonItem = self.navigationItem.rightBarButtonItem
                self.present(activityVC, animated: true, completion: nil)
            }
        }
        private func downloadData(fromURL url:URL,
                                  fileName:String,
                                  completion:@escaping (Bool, URL?) -> Void) {
            webview.configuration.websiteDataStore.httpCookieStore.getAllCookies() { cookies in
                let session = URLSession.shared
                print("downloading ....")
                session.configuration.httpCookieStorage?.setCookies(cookies, for: url, mainDocumentURL: nil)
                let task = session.downloadTask(with: url) { localURL, urlResponse, error in
                    if let localURL = localURL {
                        let destinationURL = self.moveDownloadedFile(url: localURL, fileName: fileName)
                        completion(true, destinationURL)
                    }
                    else {
                        completion(false, nil)
                    }
                }
    
                task.resume()
            }
        }
        private func getDefaultFileName(forMimeType mimeType:String) -> String {
            for record in self.mimeTypes {
                if mimeType.contains(record.type) {
                    return "default." + record.fileExtension
                }
            }
            return "default"
        }
        
        private func moveDownloadedFile(url:URL, fileName:String) -> URL {
            let tempDir = NSTemporaryDirectory()
            let destinationPath = tempDir + fileName
            let destinationURL = URL(fileURLWithPath: destinationPath)
            try? FileManager.default.removeItem(at: destinationURL)
            try? FileManager.default.moveItem(at: url, to: destinationURL)
            return destinationURL
        }
    

    HOPE this helps someone :)