WKWebView

Feb 21, 2018


WebKit

WKWebView 那些坑 教你使用 WKWebView 的正确姿势

拦截请求

1. 使用URLProtocol

首先定义协议

import Foundation
import WebKit

private let session = URLSession.init(configuration: .default)

class WKURLProtocol: URLProtocol {
    
    internal var dataTask: URLSessionDataTask?
    
    override class func canInit(with request: URLRequest) -> Bool {
        return true
    }
    
    override class func canInit(with task: URLSessionTask) -> Bool {
        return false
    }
    
    override class func canonicalRequest(for request: URLRequest) -> URLRequest {
        return request
    }
    
    override func startLoading() {
        if let url = self.request.url,
            let fileURL = Bundle.main.url(forResource: "static\(url.path)", withExtension: nil),
            FileManager.default.fileExists(atPath: fileURL.path) {
            let task = session.dataTask(with: fileURL) { [weak self] (data, response, error) in
                guard let `self` = self else { return }
                if let da = data, let rep = response {
                    self.client?.urlProtocol(self, didReceive: rep, cacheStoragePolicy: URLCache.StoragePolicy.notAllowed)
                    self.client?.urlProtocol(self, didLoad: da)
                    self.client?.urlProtocolDidFinishLoading(self)
                } else if let err = error {
                    self.client?.urlProtocol(self, didFailWithError: err)
                } else {
                    self.client?.urlProtocolDidFinishLoading(self)
                }
            }
            self.dataTask = task
            task.resume()
        } else {
            let task = session.dataTask(with: self.request) { [weak self] (data, response, error) in
                guard let `self` = self else { return }
                if let da = data, let rep = response {
                    self.client?.urlProtocol(self, didReceive: rep, cacheStoragePolicy: URLCache.StoragePolicy.notAllowed)
                    self.client?.urlProtocol(self, didLoad: da)
                    self.client?.urlProtocolDidFinishLoading(self)
                } else if let err = error {
                    self.client?.urlProtocol(self, didFailWithError: err)
                } else {
                    self.client?.urlProtocolDidFinishLoading(self)
                }
            }
            self.dataTask = task
            task.resume()
        }
    }
    
    override func stopLoading() {
        dataTask?.cancel()
    }
    
}


// MARK: - register

extension String {
    fileprivate func fromBase64() -> String? {
        guard let data = Data(base64Encoded: self) else {
            return nil
        }
        return String(data: data, encoding: .utf8)
    }
}

private let browsingContextControllerClass: AnyClass = {
    // browsingContextController
    if let str = "YnJvd3NpbmdDb250ZXh0Q29udHJvbGxlcg==".fromBase64(),
        let obj = WKWebView.init().value(forKey: str),
        let c = type(of: obj) as? AnyClass {
        // WKBrowsingContextController
        return c
    } else  {
        return NSObject.self
    }
}()

private let registerSelector: Selector = {
    // registerSchemeForCustomProtocol:
    let str = "cmVnaXN0ZXJTY2hlbWVGb3JDdXN0b21Qcm90b2NvbDo=".fromBase64() ?? ""
    let sel = Selector.init((str))
    return sel
}()

private let unregisterSelector: Selector = {
    // unregisterSchemeForCustomProtocol:
    let str = "dW5yZWdpc3RlclNjaGVtZUZvckN1c3RvbVByb3RvY29sOg==".fromBase64() ?? ""
    let sel = Selector.init((str))
    return sel
}()

extension WKURLProtocol {
    
    @discardableResult
    static func register(scheme: String) -> Bool {
        if browsingContextControllerClass.responds(to: registerSelector) {
            browsingContextControllerClass.performSelector(onMainThread: registerSelector, with: scheme, waitUntilDone: false)
            return true
        } else  {
            return false
        }
    }
    
    @discardableResult
    static func unregister(scheme: String) -> Bool {
        if browsingContextControllerClass.responds(to: registerSelector) {
            browsingContextControllerClass.performSelector(onMainThread: unregisterSelector, with: scheme, waitUntilDone: false)
            return true
        } else  {
            return false
        }
    }
    
}

再进行注册

URLProtocol.registerClass(WKURLProtocol.self)
WKURLProtocol.register(scheme: "https")

参考:

WKWebViewWithURLProtocol

wkwebview-and-nsurlprotocol-not-working

2. 使用WKURLSchemeHandler