WKWebView causes my view controller to leak

后端 未结 6 1947
礼貌的吻别
礼貌的吻别 2020-12-12 11:52

My view controller displays a WKWebView. I installed a message handler, a cool Web Kit feature that allows my code to be notified from inside the web page:

o         


        
6条回答
  •  孤城傲影
    2020-12-12 12:50

    Details

    • Swift 5.1
    • Xcode 11.6 (11E708)

    Solution

    based on Matt's answer

    protocol ScriptMessageHandlerDelegate: class {
        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage)
    }
    
    class ScriptMessageHandler: NSObject, WKScriptMessageHandler {
    
        deinit { print("____ DEINITED: \(self)") }
        private var configuration: WKWebViewConfiguration!
        private weak var delegate: ScriptMessageHandlerDelegate?
        private var scriptNamesSet = Set()
    
        init(configuration: WKWebViewConfiguration, delegate: ScriptMessageHandlerDelegate) {
            self.configuration = configuration
            self.delegate = delegate
            super.init()
        }
    
        func deinitHandler() {
            scriptNamesSet.forEach { configuration.userContentController.removeScriptMessageHandler(forName: $0) }
            configuration = nil
        }
        
        func registerScriptHandling(scriptNames: [String]) {
            for scriptName in scriptNames {
                if scriptNamesSet.contains(scriptName) { continue }
                configuration.userContentController.add(self, name: scriptName)
                scriptNamesSet.insert(scriptName)
            }
        }
    
        func userContentController(_ userContentController: WKUserContentController,
                                   didReceive message: WKScriptMessage) {
            delegate?.userContentController(userContentController, didReceive: message)
        }
    }
    

    Full Sample

    Do not forget to paste the Solution code here

    import UIKit
    import WebKit
    
    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 40))
            button.setTitle("WebView", for: .normal)
            view.addSubview(button)
            button.center = view.center
            button.addTarget(self, action: #selector(touchedUpInsed(button:)), for: .touchUpInside)
            button.setTitleColor(.blue, for: .normal)
        }
        
        @objc func touchedUpInsed(button: UIButton) {
            let viewController = WebViewController()
            present(viewController, animated: true, completion: nil)
        }
    }
    
    class WebViewController: UIViewController {
    
        private weak var webView: WKWebView!
        private var scriptMessageHandler: ScriptMessageHandler!
        private let url = URL(string: "http://google.com")!
        deinit {
            scriptMessageHandler.deinitHandler()
            print("____ DEINITED: \(self)")
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
            let configuration = WKWebViewConfiguration()
            scriptMessageHandler = ScriptMessageHandler(configuration: configuration, delegate: self)
            let scriptName = "GetUrlAtDocumentStart"
            scriptMessageHandler.registerScriptHandling(scriptNames: [scriptName])
    
            let jsScript = "webkit.messageHandlers.\(scriptName).postMessage(document.URL)"
            let script = WKUserScript(source: jsScript, injectionTime: .atDocumentStart, forMainFrameOnly: true)
            configuration.userContentController.addUserScript(script)
            
            let webView = WKWebView(frame: .zero, configuration: configuration)
            self.view.addSubview(webView)
            self.webView = webView
            webView.translatesAutoresizingMaskIntoConstraints = false
            webView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
            webView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
            view.bottomAnchor.constraint(equalTo: webView.bottomAnchor).isActive = true
            view.rightAnchor.constraint(equalTo: webView.rightAnchor).isActive = true
            webView.load(URLRequest(url: url))
        }
    }
    
    extension WebViewController: ScriptMessageHandlerDelegate {
        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            print("received \"\(message.body)\" from \"\(message.name)\" script")
        }
    }
    

    Info.plist

    add in your Info.plist transport security setting

    NSAppTransportSecurity
    
        NSAllowsArbitraryLoads
        
    
    

提交回复
热议问题