32

Now i'm using UIWebView and with canInitWithRequest: of NSURLProtocol i can intercept all requests and do with it what I want.

In the new WKWebView this method there isn't, and i not found something similar.

Has someone resolved this problem?

Pol
  • 948
  • 1
  • 8
  • 19
  • 1
    No `WKWebView` doesn't support this. It is weird though they improved the memory and didn't give option to intercept the calls. – Sachin Vas Oct 19 '16 at 15:42
  • 1
    If it is simple you can check here http://stackoverflow.com/questions/24208229/wkwebview-and-nsurlprotocol-not-working. – Sachin Vas Oct 19 '16 at 15:43
  • In iOS 11 you can add scheme handlers to your web view configuration (https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/2875766-seturlschemehandler?language=objc). Intercepting requests in iOS 10 and earlier is more complex. You should use a custom HTTP server on your device. – clemens Oct 24 '17 at 05:50
  • how can I do this HTTP server? – Pol Oct 25 '17 at 12:22
  • I have created a server with `NSNetService`, and implemented a simple HTTP implementation by myself. But there are out of the box solutions available, e.g.: https://github.com/swisspol/GCDWebServer. But I would definitely recommend that you use the possibilities of the WKWebView under iOS 11. – clemens Oct 25 '17 at 17:12
  • Does this answer your question? [WKWebView and NSURLProtocol not working](https://stackoverflow.com/questions/24208229/wkwebview-and-nsurlprotocol-not-working) – Alex Cohn Oct 18 '21 at 10:27
  • @AlexCohn The answer is incomplete. Replace NSURLProtocol with WKURLSchemeHandler for http scheme exposes you to several problems, and if you read my answer here (https://stackoverflow.com/a/67228256/6360152) I explained some of these and how to deal with them. – Pol Oct 18 '21 at 15:11
  • @Pol I agree that your answer contains important hints for people who encounter the same problem. Still, it's a refinement of the accepted answer in https://stackoverflow.com/questions/24208229. – Alex Cohn Oct 19 '21 at 17:27

7 Answers7

17

I see that after 5 years this question still generates curiosity, so I describe how I solved it and about some main problems I faced up. As many who answered here, I have implemented WKURLSchemeHandler and used new schemes.

First of all the URL that wkwebview launches must not be HTTP (or HTTPS) but one of yours new schemes.

Example

mynewscheme://your-server-application.com

In your WKWebViewConfiguration conf, I set the handler:

[conf setURLSchemeHandler:[CustomSchemeHandler new] forURLScheme:@"mynewscheme"];
[conf setURLSchemeHandler:[CustomSchemeHandler new] forURLScheme:@"mynewschemesecure"];

In CustomSchemeHandler I have implemented webView:startURLSchemeTask: and webView:stopURLSchemeTask:.

In my case I check if the request is for a file that I just saved locally, otherwise I change actual protocol ("mynewscheme or "mynewschemesecure") with http (or https) and I make request by myself.

At this point I solved the "interception problem".

In this new way we have the webview "location" (location.href via javascript) with my new scheme and with it new problems started.

  • First problem is that my applications work mainly with javascript, and document.cookie has stopped working. I'm using Cordova framework, so I've develeped a plugin to set and get cookie to replace document.cookie (I had to do this, because, obviously, I have also http header set-cookie).

  • Second problem is that I've got a lot of "cross-origin" problems, then I changed all my urls in relative url (or with new schemes)

  • Third problem is that browser automatically handle server port 80 and 443, omitting them, but has now stopped (maybe because of "not http location"). In my server code I had to handle this.

Writing down these few rows I admit that it seems to was an easy problem to solve, but I ensure that find out a workaround, how to solve it and integrate with the infinite amount of code has been hard. Every step towards the solution corresponded to a new problem.

Pol
  • 948
  • 1
  • 8
  • 19
  • Does that mean it will not work for "file://" for locally loaded files? And one has to create a new protocol for loading local files and intercepting the request? – F C Jul 11 '21 at 18:30
  • Yes you can, but I had encountered some problems. Sorry but I don't remember which ones. Perhaps with cookies, or with links with server port or with encoding. My local files are webpages, I save them in a different process that happens at start. I need that webview treat the contents of my files as if they were loaded directly from the web. And somethings was wrong – Pol Jul 16 '21 at 15:41
  • Hi @Pol, great answer, so you said you found a workaround for the second problem? would you mind sharing how you overcame this issue? Or Maybe point me in the right direction on how to solve this? – Guy S Mar 09 '22 at 12:51
  • Hi @GuyS, are you talking about cross-origin? – Pol Feb 27 '23 at 16:01
13

You can intercept requests on WKWebView since iOS 8.0 by implementing the decidePolicyFor: navigationAction: method for the WKNavigationDelegate

 func webView(_ webView: WKWebView, decidePolicyFor 
       navigationAction: WKNavigationAction, 
       decisionHandler: @escaping (WKNavigationActionPolicy) -> Swift.Void) {

    //link to intercept www.example.com

    // navigation types: linkActivated, formSubmitted, 
    //                   backForward, reload, formResubmitted, other

    if navigationAction.navigationType == .linkActivated {
        if navigationAction.request.url!.absoluteString == "http://www.example.com" {
            //do stuff

            //this tells the webview to cancel the request
            decisionHandler(.cancel)
            return
        }
    }

    //this tells the webview to allow the request
    decisionHandler(.allow)

}
Matthew Cawley
  • 2,828
  • 1
  • 31
  • 42
  • Any way to catch the posted body? I have tried navigationAction.request.httpBody but this is empty. Apparently a bug in WKWebView, but do you know about a workaround? – Christopher Smit Sep 14 '18 at 08:27
  • I'm not entirely sure about that to be honest, as I've not tried to grab the post at this point. Logically the way you are trying should be the recommended way. I know when I originally started with WKWebView there was quite a few issues and I just dropped back to UIWebView to get around them. – Matthew Cawley Sep 19 '18 at 00:49
  • Yeah I had to drop back to that as well. My app is making use of credit card payments and requires the posted body which is sent back by the 3D Secure Authentication page from the issuing bank. If I cannot catch the body, I cannot continue with payment. Its just irritating because the log is full of warnings about using UIWebView – Christopher Smit Sep 19 '18 at 04:22
  • 3
    Do not use webView.url!.absoluteString but navigationAction.request.url!.absoluteString instead – Ariel Malka Jun 20 '19 at 09:58
  • This doesn't intercept URLs of alert popups in the webview. Any workaround for that? – AnupamChugh Sep 14 '20 at 06:16
  • This doesn't intercept HTTP requests – 6rchid Feb 06 '23 at 09:16
9

there are many ways to implement intercepter request.

  1. setup a local proxy, use WKNavigationDelegate

    -[ViewController webView:decidePolicyForNavigationAction:decisionHandler:]
    

    load new request and forward to local server, and at the local server side you can do something(eg. cache).

  2. private api. use WKBrowsingContextController and custom URLProtocol

    Class cls = NSClassFromString(@"WKBrowsingContextController");
    SEL sel = NSSelectorFromString(@"registerSchemeForCustomProtocol:");
    if ([(id)cls respondsToSelector:sel]) {
        // 把 http 和 https 请求交给 NSURLProtocol 处理
        [(id)cls performSelector:sel withObject:@"http"];
        [(id)cls performSelector:sel withObject:@"https"];
    }
    
  3. use KVO to get system http/https handler.(ios 11, *)

    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    URLSchemeHandler *schemeHandler = [[URLSchemeHandler alloc] init];
    [configuration setURLSchemeHandler:schemeHandler forURLScheme:@"test"];
    NSMutableDictionary *handlers = [configuration valueForKey:@"_urlSchemeHandlers"];
    handlers[@"http"] = schemeHandler;
    handlers[@"https"] = schemeHandler;
    

    all the three ways you probably need handle CORS & post bodies to be stripped,you overwrite change browser`s option request( Preflighted_requests),you can custom http header to replace http body for post bodies to be stripped.

user9517377
  • 91
  • 1
  • 2
  • 2
    3/ does not work since macOS 10.14 (probably iOS 12 too) because WKWebViewConfiguration does not store url scheme handlers in an ivar. Everything is handled at a lower (core webkit, C++) level. That's too bad :) – Altimac Oct 15 '19 at 11:43
5

in iOS 11 WKWebView has come up with Custom Scheme Handler called WKURLSchemeHandler, which you can use to intercept the custom events. for more info check out this project. https://github.com/BKRApps/KRWebView

Kumar Reddy
  • 792
  • 6
  • 15
  • 3
    we cannot handle https and http with WKURLSchemeHandler. – Mitsuaki Ishimoto Mar 15 '18 at 12:00
  • Why does the fact you can't intercept `https` matter? You just specify the `wkfoobar` and your intercepter can handle the translate `wkfoobar` to `https` logic. I sent all html, js, css, images, fonts through my interceptor. But you need to be careful to use `relative path` URLs and do not reference `absolute paths` that would bypass your interceptor. – rustyMagnet Jul 12 '19 at 17:24
  • It matters because secure origins can’t embed insecure iframes or even images. All Apple examples online showing off this scheme thing embed an image, but even their own example is disallowed now. – Gregory Magarshak Jan 16 '20 at 00:29
  • @rustyMagnet did you ever try it with an https document as the parent loaded from some server, loading resources that are intercepted by you? – Gregory Magarshak Jan 16 '20 at 00:30
  • hi @GregoryMagarshak. Apple don't allow `http` or `https` with `WKURLSchemeHandler`. The summary is: `A protocol for loading resources with URL schemes that WebKit doesn't know how to handle.` – rustyMagnet Jan 16 '20 at 08:34
  • Right, and the point is that if the top-level document is loaded with https, you cannot actually embed any images or iframes in it that are loaded with this thing, making it USELESS – Gregory Magarshak Jan 17 '20 at 03:43
4

I know I am late but I am able to solve this problem. I can intercept each and every request even your http/https call using below trick. I can also trace the call made from html to server calls. I can also use this to render html with offline content.

  1. Download the html of the website that we want to render in offline or online to intercept the request.
  2. Either place the html in document directory of the user or place it inside the archive. But we should know the path of the html file.
  3. Place all your js, cs, woff, font of our website at the same level as our base html. We need to given permission while loading the web view.
  4. Then we have to register our own custom handler scheme with WKWebView. When wkwebview see the pattern "myhandler-webview" then it will give you control and you will get the callback to 'func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask)' delegate implementation. You can play around with url in this delegate like mentioned in point 6.
      let configuration = WKWebViewConfiguration();
        configuration.setURLSchemeHandler(self, forURLScheme: "myhandler-webview");
        webView = WKWebView(frame: view.bounds, configuration: configuration);
  1. Convert file scheme to the custom scheme (myhandler-webview) then load it with WKWebView
    let htmlPath = Bundle.main.path(forResource: "index", ofType: "html")
    var htmlURL = URL(fileURLWithPath: htmlPath!, isDirectory: false)                    
    htmlURL = self.changeURLScheme(newScheme: "myhandler-webview", forURL: htmlURL)
    self.webView.load(URLRequest(url: htmlURL))
  1. Implement below methods of WKURLSchemeHandler protocol and handle didReceiveResponse, didReceiveData, didFinish delegate methods of WKURLSchemeTask.
    func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
        print("Function: \(#function), line: \(#line)")
        print("==> \(urlSchemeTask.request.url?.absoluteString ?? "")\n")

    // You can find the url pattern by using urlSchemeTask.request.url. and create NSData from your local resource and send the data using 3 delegate method like done below.
    // You can also call server api from this native code and return the data to the task.
    // You can also cache the data coming from server and use it during offline access of this html.
    // When you are returning html the the mime type should be 'text/html'. When you are trying to return Json data then we should change the mime type to 'application/json'.
    // For returning json data you need to return NSHTTPURLResponse which has base classs of NSURLResponse with status code 200. 

    // Handle WKURLSchemeTask delegate methods
        let url = changeURLScheme(newScheme: "file", forURL: urlSchemeTask.request.url!)

        do {
            let data = try Data(contentsOf: url)

            urlSchemeTask.didReceive(URLResponse(url: urlSchemeTask.request.url!, mimeType: "text/html", expectedContentLength: data.count, textEncodingName: nil))
            urlSchemeTask.didReceive(data)
            urlSchemeTask.didFinish()
        } catch {
            print("Unexpected error when get data from URL: \(url)")
        }
    }

    func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
        print("Function: \(#function), line: \(#line)")
        print("==> \(urlSchemeTask.request.url?.absoluteString ?? "")\n")
    }

Let me know if this explanation is not enough.

Objective c example mentioned below

intercepting request with wkwebview

Vikash
  • 109
  • 1
  • 7
1

One can use WKURLSchemeHandler to intercept each and every request to be loaded in WKWebView, Only disadvantage is that you cannot register http or https scheme for interception,

Solution over that is, Replace your http/https scheme of url with custom scheme url like xyz:// for e.g. https://google.com can be loaded like xyz://google.com Now you will get a callback in WKURLSchemeHandler there you again replace it back to https and load data programmatically and call urlSchemeTask.didReceive(response)

This way each and every https request will come to your handler.

Mayur Kothawade
  • 544
  • 4
  • 14
  • 1
    To confirm, you can't intercept regular http/https requests? (without a workaround) – Ask P Feb 16 '20 at 20:55
  • @AskP that is correct. However, so long as URLs are relative, they should use the same scheme and thus go through your handler. – Bob9630 May 08 '20 at 01:33
  • So, if the existing web is still use http/https instead of custom scheme URL, we should ask the web developer to change it to custom scheme? What if we have no control/access to the web to change the scheme? @Bob9630 – Aji Saputra Raka Siwi May 23 '23 at 17:35
-1

I am blindly taking guesses since I only have my windows computer with me. By reading the Apple Developer documentation here is information I gathered that might lead to some ideas on how to solve the question.

Based on WKWebView,

Set the delegate property to an object conforming to the WKUIDelegate protocol to track the loading of web content.

Also, I see we can set our navigationDelegate with the,

weak var navigationDelegate: WKNavigationDelegate? { get set }

The methods of the WKNavigationDelegate protocol help you implement custom behaviors that are triggered during a web view's process of accepting, loading, and completing a navigation request.

Then after we create and set our custom WKNavigationDelegate, we would override some methods to intercept something we might be looking for. I found the Responding to Server Actions section of some interest since they receive a WKNavigation as parameter. Moreover, you might want to skim through WKNavigationAction and WKNavigationResponse see if there is perhaps something which might help us achieve our goal.

BTW, I am just giving some ideas on what to try so that we can solve this question, ideas which might be 100% wrong cause I have not tried them myself.

ArtiomLK
  • 2,120
  • 20
  • 24