Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IOS - How to get cached resources from WKWebView?

As I know, caching for the all resources loaded in WKWebView is set by default. There are several post about How to remove cached resources from WKWebView? but I can't find any posts about the way to get it from WKWebView.

For example, when I use WKWebView to load this url, it displays a pdf file and what I want is getting this pdf file from WKWebView to share after url is loaded completely

I have checked on Chrome, if I want to share the file from above link, it will request and download content from url again after that displaying share dialog. It means that they don't get it from current WKWebView (cached resources).

But on Safari, somehow they show share dialog immediately after clicking on share button and seem like without downloading again. I think they get pdf file from cache or somewhere in WkWebView and share it.

Another way to understand the question is How to get a file which is presented on WKWebView without downloading content again?

enter image description here

If the question doesn't have enough information for an answer, feel free to leave a comment and I will make it clearer.

like image 238
trungduc Avatar asked Jun 12 '18 11:06

trungduc


People also ask

Is WKWebView the same as Safari?

WKWebView - This view allows developers to embed web content in your app. You can think of WKWebView as a stripped-down version of Safari. It is responsible to load a URL request and display the web content. WKWebView has the benefit of the Nitro JavaScript engine and offers more features.

What is UIWebView WKWebView?

Overview. A WKWebView object is a platform-native view that you use to incorporate web content seamlessly into your app's UI. A web view supports a full web-browsing experience, and presents HTML, CSS, and JavaScript content alongside your app's native views.

How can I clear the contents of a UIWebView WKWebView?

To clear old contents of webview With UIWebView you would use UIWebViewDelegate 's - webViewDidFinishLoad: .


1 Answers

As I know, caching for the all resources loaded in WKWebView is set by default: True

By keeping that in mind if you request for the same resource, webview will not load content from internet it will give you the content from the cached resources. For requesting the same resource, you can use some JavaScript to get the content.

Look at the below code. Once the PDF is loaded and you hit the save button. it will execute JavaScript Code and when the data is ready to deliver by JavaScript

It will fire window.webkit.messageHandlers.myInterface.postMessage(base64)

to let your ViewController know that data is ready to be shared.

You can verify the same by

  1. Let it load the PDF

  2. Turn off the simulator's internet(see)

  3. Hit the save button

You will get your pdf data in base64 format. save it and share it :)

    import UIKit
    import WebKit
    class ViewController: UIViewController {

        @IBOutlet weak var webView: WKWebView!
        var activityIndicator: UIActivityIndicatorView?
        override func viewDidLoad() {
            super.viewDidLoad()

        }

        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
            webView.navigationDelegate = self
            activityIndicator?.center = self.view.center
            self.view.addSubview(activityIndicator!)
            webView.configuration.userContentController.add(self, name: "myInterface")
            webView.load(URLRequest(url: URL(string: "http://www.africau.edu/images/default/sample.pdf")!))
            activityIndicator?.startAnimating()
        }

        @IBAction func saveAction(_ sender: Any) {
            let s = """
            var xhr = new XMLHttpRequest();
            xhr.open('GET', "\(webView.url?.absoluteString ?? "")", true);
            xhr.responseType = 'arraybuffer';
            xhr.onload = function(e) {
            if (this.status == 200) {
            var uInt8Array = new Uint8Array(this.response);
            var i = uInt8Array.length;
            var binaryString = new Array(i);
            while (i--){
            binaryString[i] = String.fromCharCode(uInt8Array[i]);
            }
            var data = binaryString.join('');
            var base64 = window.btoa(data);

       window.webkit.messageHandlers.myInterface.postMessage(base64);
            }
            };
            xhr.send();
            """




            webView?.evaluateJavaScript(s, completionHandler: {(string,error) in
                print(error ?? "no error")
            })
        }
    }

    extension ViewController: WKScriptMessageHandler{
      func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    //    print("Message received: \(message.name) with body: \(message.body)")

        guard
          var documentsURL = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)).last,
          let convertedData = Data.init(base64Encoded: message.body as! String)
          else {
            //handle error when getting documents URL
            return
        }

        //name your file however you prefer
        documentsURL.appendPathComponent("sample.pdf")

        do {
          try convertedData.write(to: documentsURL)
        } catch {
          //handle write error here
        }

        //if you want to get a quick output of where your
        //file was saved from the simulator on your machine
        //just print the documentsURL and go there in Finder
        print(documentsURL)

        let activityViewController = UIActivityViewController.init(activityItems: [documentsURL], applicationActivities: nil)
        present(activityViewController, animated: true, completion: nil)
      }
    }

    extension ViewController: WKNavigationDelegate{
        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            self.activityIndicator?.stopAnimating()
            self.activityIndicator?.removeFromSuperview()
            self.activityIndicator = nil
        }
    }

BTW the pdf link you provided is using HTTP not HTTPS. So for testing purpose add the following in your info.plist

<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoadsInWebContent</key>
        <true/>
    </dict>
like image 169
Sahil Avatar answered Oct 05 '22 19:10

Sahil