How to block external resources to load on a WKWeb

2020-05-19 03:23发布

I have an app that loads a webpage, but prevents the downloads of images, fonts, javascripts, etc. For this I implemented an NSURLProtocol subclass which works very well with UIWebView.

However I'm migrating to WKWebview, and realise that my crafted NSURLProtocol class no longer works to filter out these resources.

Have anyone an insight as to how to achive the filtering/blocking?

In case you're wondering how am I doing the migration, I started with this post: http://floatlearning.com/2014/12/uiwebview-wkwebview-and-tying-them-together-using-swift/

3条回答
ゆ 、 Hurt°
2楼-- · 2020-05-19 03:45

As of iOS 9.0 there is no way to intercept network requests for the WKWebView. You can do this with JavaScript in a limited way.

Please file a WebKit bug or an Apple bug to request this functionality. Many of us are in need of these hooks.

查看更多
何必那么认真
3楼-- · 2020-05-19 03:46

Since iOS 11 you can use WKContentRuleList

First, create a Content Rule or a list. Each rule is comprised of a trigger and an action. See Apple's Documentation on Content Rule creation

This is a creation example, blocks all image and Style Sheet content, but allows those ended on jpeg by way of ignoring the previous rules:

     let blockRules = """
         [{
             "trigger": {
                 "url-filter": ".*",
                 "resource-type": ["image"]
             },
             "action": {
                 "type": "block"
             }
         },
         {
             "trigger": {
                 "url-filter": ".*",
                 "resource-type": ["style-sheet"]
             },
             "action": {
                 "type": "block"
             }
         },
         {
             "trigger": {
                 "url-filter": ".*.jpeg"
             },
             "action": {
                 "type": "ignore-previous-rules"
             }
         }]
      """        

Having your list of rules, you can add them to the ContentRuleListStore

    import WebKit
    @IBOutlet weak var wkWebView: WKWebView!

    let request = URLRequest(url: URL(string: "https://yourSite.com/")!)

    WKContentRuleListStore.default().compileContentRuleList(
        forIdentifier: "ContentBlockingRules",
        encodedContentRuleList: blockRules) { (contentRuleList, error) in

            if let error = error {
                return
            }

            let configuration = self.webView.configuration
            configuration.userContentController.add(contentRuleList!)

            self.wkWwebView.load(self.request)
    }

If later you want to remove all your rules, call:

    self.wkWebView.configuration.userContentController.removeAllContentRuleLists()
    self.wkWebView.load(self.request)

Here is the 2017 WWDC video

Best of lucks!

I've created a sample project on Github WKContentRuleExample

查看更多
再贱就再见
4楼-- · 2020-05-19 03:47

In case anybody else is interested in an offline-only WKWebView: The following approach is a modification of @dequin's answer. It uses content blocking rules to block all requests to remote resources (URLs that don't start with file://):

import Cocoa
import WebKit

// Block all URLs except those starting with "file://"
let blockRules = """
[
    {
        "trigger": {
            "url-filter": ".*"
        },
        "action": {
            "type": "block"
        }
    },
    {
        "trigger": {
            "url-filter": "file://.*"
        },
        "action": {
            "type": "ignore-previous-rules"
        }
    }
]
"""
/// `WKWebView` which only allows the loading of local resources
class OfflineWebView: WKWebView {
    override init(frame: CGRect, configuration: WKWebViewConfiguration) {
        WKContentRuleListStore.default().compileContentRuleList(
            forIdentifier: "ContentBlockingRules",
            encodedContentRuleList: blockRules
        ) { contentRuleList, error in
            if let error = error {
                // Handle error
            } else if let contentRuleList = contentRuleList {
                configuration.userContentController.add(contentRuleList)
            } else {
                // Handle error
            }
        }

        super.init(frame: frame, configuration: configuration)
    }
}
查看更多
登录 后发表回答