Intercepting requests from WebView

Have you ever wondered how you can intercept ANY requests that your WebView makes in both Android and iOS? Well, turns out that this is possible and not so hard to do. Android For Android it's actually pretty simple! You need to create a custom web view client and override the shouldInterceptRequest function. So it would look like this: WebView(context).apply { ... webViewClient = object : WebViewClientCompat() { override fun shouldInterceptRequest( view: WebView, request: WebResourceRequest, ): WebResourceResponse? { val requestHost = request.url.host val isRequestAllowed = allowedHostsList.entries.any { it.host == requestHost } if (isRequestAllowed) { return null // Here you can return null or call super.shouldInterceptRequest(view, request) } return WebResourceResponse("text/html", "utf-8", 403, "Blocked", emptyMap(), ByteArrayInputStream(ByteArray(0))) } } ... } This method allows you to intercept the request and handle it as you may see fit. Remember that the request.url.host is a string that contains only the host, so no pathname or protocol there. If you want to get this information, you can get it from the request.url object. iOS For iOS it's not so straightforward. Actually there's no way to intercept the request, so we need to resort to a little trick. You can intercept navigation requests (going from one url to another), with WKNavigationDelegate, but not requests made by the javascript from the website, for example. To our rescue, we can use WKContentRuleList, which in essence is a list that works in the same way as the content blockers from Safari (the browser). We can use this content rule list to block requests to specific urls. First, we need to define the rule list, compile it and add to our webview. There's a gotcha, tough, we need to first block everything and then allow the domains we want (it's a trick, after all). The rule list is a simple json array: [ { "trigger": { "url-filter": ".*" }, "action": { "type": "block" } }, { "trigger": { "url-filter": "google.com" }, "action": { "type": "ignore-previous-rules" } } ] The composition of the rule is pretty simple, you have a trigger object, and a action one. The trigger key component is the url-filter, which accepts a regex to match the url, and .* will match all urls. The action key component is the type, which will define what will happen for the matched url. You can check the complete list of options for the trigger here and for the action block here. In this example, we first block all requests, and then we allow only requests to google.com by defining the action of type ignore-previous-rules, which as the name implies, will ignore any rule that the url matched before. After having your list set up, you need to compile and apply it to the userContentController. let contentRulesList = """ [ { "trigger": { "url-filter": ".*" }, "action": { "type": "block" } }, { "trigger": { "url-filter": "google.com" }, "action": { "type": "ignore-previous-rules" } } ] """ let compiledContentRulesList = try await WKContentRuleListStore .default() .compileContentRuleList( forIdentifier: "MyAppList", encodedContentRuleList: contentRulesList ) webViewConfiguration.userContentController .add(compiledContentRulesList!) After that, you are all set! Conclusion Now you know how to intercept and block/allow requests for webviews in both android and iOS. For android it's pretty simple and straighforward, but for iOS we need the WKContentRuleList trick. Hopefully we will have a more direct way in the future. Thanks for reading! If you have any questions, let me know.

Mar 28, 2025 - 16:49
 0
Intercepting requests from WebView

Have you ever wondered how you can intercept ANY requests that your WebView makes in both Android and iOS?

Well, turns out that this is possible and not so hard to do.

Android

For Android it's actually pretty simple! You need to create a custom web view client and override the shouldInterceptRequest function. So it would look like this:

WebView(context).apply {
    ...
    webViewClient =
        object : WebViewClientCompat() {
            override fun shouldInterceptRequest(
                view: WebView,
                request: WebResourceRequest,
            ): WebResourceResponse? {
                val requestHost = request.url.host
                val isRequestAllowed =
                    allowedHostsList.entries.any { it.host == requestHost }
                if (isRequestAllowed) {
                    return null // Here you can return null or call super.shouldInterceptRequest(view, request)
                }

                return WebResourceResponse("text/html", "utf-8", 403, "Blocked", emptyMap(), ByteArrayInputStream(ByteArray(0)))
            }
        }
    ...
}

This method allows you to intercept the request and handle it as you may see fit. Remember that the request.url.host is a string that contains only the host, so no pathname or protocol there. If you want to get this information, you can get it from the request.url object.

iOS

For iOS it's not so straightforward. Actually there's no way to intercept the request, so we need to resort to a little trick. You can intercept navigation requests (going from one url to another), with WKNavigationDelegate, but not requests made by the javascript from the website, for example.

To our rescue, we can use WKContentRuleList, which in essence is a list that works in the same way as the content blockers from Safari (the browser).

We can use this content rule list to block requests to specific urls. First, we need to define the rule list, compile it and add to our webview. There's a gotcha, tough, we need to first block everything and then allow the domains we want (it's a trick, after all).

The rule list is a simple json array:

[
    {
                "trigger": {
                    "url-filter": ".*"
                },
                "action": {
                    "type": "block"
                }
    },
    {
                "trigger": {
                    "url-filter": "google.com"
                },
                "action": {
                    "type": "ignore-previous-rules"
                }
    }
]

The composition of the rule is pretty simple, you have a trigger object, and a action one.

The trigger key component is the url-filter, which accepts a regex to match the url, and .* will match all urls.
The action key component is the type, which will define what will happen for the matched url.

You can check the complete list of options for the trigger here and for the action block here.

In this example, we first block all requests, and then we allow only requests to google.com by defining the action of type ignore-previous-rules, which as the name implies, will ignore any rule that the url matched before.

After having your list set up, you need to compile and apply it to the userContentController.

let contentRulesList = """
[
    {
                "trigger": {
                    "url-filter": ".*"
                },
                "action": {
                    "type": "block"
                }
    },
    {
                "trigger": {
                    "url-filter": "google.com"
                },
                "action": {
                    "type": "ignore-previous-rules"
                }
    }
]
"""

let compiledContentRulesList = try await WKContentRuleListStore
                .default()
                .compileContentRuleList(
                    forIdentifier: "MyAppList",
                    encodedContentRuleList: contentRulesList
                )

webViewConfiguration.userContentController
                .add(compiledContentRulesList!)

After that, you are all set!

Conclusion

Now you know how to intercept and block/allow requests for webviews in both android and iOS.
For android it's pretty simple and straighforward, but for iOS we need the WKContentRuleList trick. Hopefully we will have a more direct way in the future.

Thanks for reading! If you have any questions, let me know.