iOS: open pdf of webview in browser OR download pdf

Asked

Viewed 511 times

2

I’m working on an iOS app with Swift. It works perfectly, but when I click on the pdf download link, it just displays the pdf. I wanted him to downdload OR open the pdf in the browser outside the webview. Whoever has a solution, I’ll be grateful, because I’m already trying to figure it out. In some researches I saw a staff commenting on confiscating the Delegate, but how is this configuration? There is need to be done ?

import UIKit

class ViewController: UIViewController {
  @IBOutlet weak
  var myWebView: UIWebView!

    override func viewDidLoad() {
      super.viewDidLoad()

      let url = URL(string: "http://site/")
      let urlRequest = URLRequest(url: url!)
      myWebView.loadRequest(urlRequest)
 

 //Eu tentei fazer isso, mas ele abre o pdf no browser quando abro o aplicativo, sendo que a ideia é abrir o pdf só qnd eu clicar no link. Um link que gera um boleto 

if let url = URL(string: "https://prod-bepay-boletos.s3-us-west-2.amazonaws.com/boleto-A96C9542-79F4-9A8F-E2F1-F26120DABC45.pdf") {
  UIApplication.shared.open(url)
  }
 }
}

2 answers

1


What you really need is to monitor the user’s navigation. You can do this in Uiwebview as well but Uiwebview ta deprecated so I recommend using Wkwebview for this:

import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
    let webView = WKWebView()
    override func viewDidLoad() {
        super.viewDidLoad()
        print("Documents\n", FileManager.documents)
        let url = URL(string: "https://www.google.com")!
        webView.frame = view.bounds
        webView.navigationDelegate = self
        webView.load(URLRequest(url: url))
        webView.autoresizingMask = [.flexibleWidth,.flexibleHeight]
        view.addSubview(webView)
    }
}

extension FileManager {
    static let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
}

And implement the method func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) to detect when the user clicks a link and if this link is a pdf:

extension ViewController {
    public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        if navigationAction.navigationType == .linkActivated  {
            if let url = navigationAction.request.url,
                url.pathExtension == "pdf" {
                print("pdf url:\n", url, "\nNo need to open locally")
                decisionHandler(.cancel)

                // se voce quiser pode download o pdf
                URLSession.shared.downloadTask(with: url) { location, response, error in
                    guard let location = location, let response = response else {
                        print("error:", error ?? "nil")
                        return
                    }
                    print("Location:", location.path, "\nResponse:", response, "\nFilename:", response.suggestedFilename ?? "file.pdf")
                    do {
                        let destination = FileManager.documents.appendingPathComponent(response.suggestedFilename ?? "file.pdf")
                        print("destination:", destination.path)
                        try FileManager.default.moveItem(at: location,  to: destination)
                        print("file moved from temporary location to documents directory")
                    } catch {
                        print("File copy/move error:", error)
                    }
                }.resume()
                // ou exibir usando o safari
                // if UIApplication.shared.canOpenURL(url) {
                //    UIApplication.shared.open(url)
                //     print(url)
                //    print("abrindo pdf com default browser")
                // }
                //
            } else {
                print("user clicked on a link that it is not a pdf")
                decisionHandler(.allow)
            }
        } else {
            decisionHandler(.allow)
        }
    }
}
  • Certo, @Leo esse método func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: aescaping (WKNavigationActionPolicy) -> Void). That’s what’s going on at delegate.Swift ?

  • You can put it in any file within your project. You can also put it in Viewcontroler underneath everything. You can also take it out of the extension and put it right into your view controller

  • is presenting this error: "Wkwebview before iOS 11.0 (Nscoding support was Broken in Previous versions) " . Meaning I am working with version 10 and it seems that it does not support version less than 11.

  • I’m sure this error is not from my iOS10 code supports Wkwebview for sure. I’m not even using Nscoding. I can’t solve your problem without seeing your code. What you need your Viewcontroller inherit from Nscoding for?

  • I also found it strange, I’ll take a look at my "Main.storyboard" the problem is there. But as for your code, it’s all right, I just imported Webkit, it’s reset, no mistakes.

  • Solved dude! Thanks. I just don’t know why he’s zooming in on the app.

  • You’re welcome, good luck :)

  • You can use a customUserAgent in your Wkwebview using the Safari pro mac user agent and make it display the desktop version of the site

  • I get it, I’m going to look around here. Once again, thank you ! :)

  • It is difficult to take this effect "Pinch zoom". I tried the following..: func scrollViewWillBeginZooming(_ scrollView: Uiscrollview, with view: Uiwebviewnavigationtype?) { scrollView.pinchGestureRecognizer?. isEnabled = false } Did not roll... Have any idea how I can do this scheme that you said?

  • https://stackoverflow.com/a/31943976/2303865

  • That’s the first one I tried, but it didn’t work.

Show 7 more comments

0

Here’s an outline solution to your problem. The method downloadPDF downloads the file and saves to the device, using the class Filedownloader. The PDF will be saved inside the folder Documents.

The method displayPDF uses UIDocumentInteractionController to find applications that can show the file.

class ViewController: UIViewController, UIDocumentInteractionControllerDelegate {

    func pdfFileURL() -> URL {
        let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        let pdfFileURL = documentsURL.appendingPathComponent("file.pdf")
        return pdfFileURL
    }

    func downloadPDF() {
        let url = URL(string: "https://www.server.com/file.pdf")
        FileDownloader.load(url: url!, to: self.pdfFileURL(), completion: { (success) in
            print("Download succeeded? \(success)")
        })
    }

    func displayPDF() {
        let fileURL = self.pdfFileURL()
        if (!FileManager.default.fileExists(atPath: fileURL.path)) {
            print("File not found")
            return
        }
        let documentController = UIDocumentInteractionController.init(url: fileURL)
        documentController.delegate = self
        documentController.presentPreview(animated: true)
    }

    //MARK: UIDocumentInteractionControllerDelegate
    func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
        return self
    }
}

class FileDownloader {
    class func load(url: URL, to destination: URL, completion: @escaping (Bool) -> ()) {
        let task = URLSession.shared.downloadTask(with: url) { localURL, urlResponse, error in

            if let error = error {
                print("Error downloading file: %@", error.localizedDescription);
                completion(false)
                return
            }

            if let urlResponse = urlResponse as? HTTPURLResponse, urlResponse.statusCode == 200 {
                if let localURL = localURL {
                    do {
                        try FileManager.default.copyItem(at: localURL, to: destination)
                        completion(true)
                        return
                    } catch (let writeError) {
                        print("Error writing file to \(destination) : \(writeError)")
                    }
                }
            } else {
                print("Server returned an error")
            }

            completion(false)
        }
        task.resume()
    }
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.