skip to Main Content
import UIKit
import WebKit

class ViewController: UIViewController, WKUIDelegate{
    
    @IBOutlet weak var webVw: WKWebView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Load a URL
        if let url = URL(string: "https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_print") {
            let request = URLRequest(url: url)
            webVw.load(request)
        }
    }
}

enter image description here

"Print this page" button not showing anything but loading the same url in safari and clicking the same button showing the print page option.

enter image description here

2

Answers


  1. Alright, let’s tackle this printing issue! So, you’ve got a WKWebView and you want your users to be able to print the web page, just like they can in Safari. But, bummer, WKWebView doesn’t support the JavaScript window.print() function by default. No worries though, there’s a workaround!

    Here’s a more casual walkthrough of the solution:

    1. First things first, let’s create a JavaScript "script" that’ll catch the call to window.print(). Instead of doing the usual print thing, this sly script will send us a secret "print please!" message.
    let printScript = WKUserScript(
        source: "window.print = function(){window.webkit.messageHandlers.printHandler.postMessage('print')};",
        injectionTime: .atDocumentEnd,
        forMainFrameOnly: true
    )
    
    1. Now, let’s stick this script into the WKWebView configuration. Don’t forget to add a "printHandler" to handle the secret messages.
    let config = WKWebViewConfiguration()
    config.userContentController.addUserScript(printScript)
    config.userContentController.add(self, name: "printHandler")
    
    1. Then, we initialize our WKWebView with the new configuration.
    webVw = WKWebView(frame: .zero, configuration: config)
    webVw.uiDelegate = self
    
    1. Load your URL as usual. No changes here!
    if let url = URL(string: "https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_print") {
        let request = URLRequest(url: url)
        webVw.load(request)
    }
    
    1. And the final touch, we need to actually print when we receive the secret "print please!" message from the web page.
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if message.name == "printHandler" {
            printPage()
        }
    }
    
    private func printPage() {
        let printInfo = UIPrintInfo(dictionary: nil)
        printInfo.jobName = webVw.url?.absoluteString ?? "Document"
        printInfo.outputType = .general
    
        let printController = UIPrintInteractionController.shared
        printController.printInfo = printInfo
        printController.printFormatter = webVw.viewPrintFormatter()
    
        printController.present(animated: true, completionHandler: nil)
    }
    

    And there you have it! With a little JavaScript trickery, your users can now print the webpage as they can in Safari. You just took your WKWebView to the next level. High fives all around! 🙌

    Just remember to switch out webVw with the actual name of your WKWebView’s IBOutlet.

    Login or Signup to reply.
  2. Sorry I misunderstood you earlier.

    So, we need a workaround. We’ll employ JavaScript injection using WKUserScript to catch the window.print() function call within the WKWebView. This is possible due to the interoperability of Swift with JavaScript within WKWebView.

    Now, let’s dissect the code:

    1. Creating the print script: We construct a WKUserScript object with a JavaScript code snippet that redefines the window.print() function. In our version, it sends a message using the WebKit message handler, window.webkit.messageHandlers.printHandler.postMessage('print').
    let printScript = WKUserScript(
        source: "window.print = function(){window.webkit.messageHandlers.printHandler.postMessage('print')};",
        injectionTime: .atDocumentEnd,
        forMainFrameOnly: true
    )
    
    1. Injecting the script: We need to inject this script into the WKWebView via its configuration, which contains a user content controller. We also add a message handler for ‘printHandler’, which is going to catch the message we’ve set up in our JavaScript.
    let config = WKWebViewConfiguration()
    config.userContentController.addUserScript(printScript)
    config.userContentController.add(self, name: "printHandler")
    
    1. Setting up WKWebView: Now, initialize the WKWebView with the configuration. However, the earlier warning arises due to the WebView being assigned to a weak property, which leads to its immediate deallocation. Hence, we’re going to leverage the already set WKWebView from the storyboard, which is managed by ARC.

    2. URL Loading: Here, we load the URL as usual.

    3. Handling the script message: WKScriptMessageHandler protocol implementation helps us handle the receipt of the print message. When we receive the "printHandler" message, we call a native function printPage().

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if message.name == "printHandler" {
            printPage()
        }
    }
    
    1. Print Functionality: We use the UIPrintInteractionController to present the system print dialog in the printPage() function.
    private func printPage() {
        let printInfo = UIPrintInfo(dictionary: nil)
        printInfo.jobName = webVw.url?.absoluteString ?? "Document"
        printInfo.outputType = .general
    
        let printController = UIPrintInteractionController.shared
        printController.printInfo = printInfo
        printController.printFormatter = webVw.viewPrintFormatter()
    
        printController.present(animated: true, completionHandler: nil)
    }
    

    This solution is somewhat a bridge between the webpage’s JavaScript environment and our native code.

    Now, if window.print() function isn’t being called as expected, you should inspect the JavaScript code running on the webpage. It’s possible that the window.print() function is being defined after the page load, overriding your custom function. You can use polling to consistently check for the definition of window.print() function, and replace it with your custom function if it’s been overwritten.

    Finally, this solution assumes that the webpage is executing the window.print() function to initiate printing. If the page uses a different mechanism, this solution won’t work, and we’d need to inspect the page’s JavaScript further.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search