How can i monitor requests on WKWebview?

后端 未结 4 933
情书的邮戳
情书的邮戳 2020-12-02 07:15

How can i monitor requests on WKWebview?

I\'v tried using NSURLprotocol (canInitWithRequest) but it won\'t monitor ajax requests (XHR), only navig

相关标签:
4条回答
  • 2020-12-02 07:46

    @Benzi Heler answer is great, but it uses jQuery which seems like is not working in WKWebView anymore, so I have found solution without using jQuery.

    Here is ViewController implementation that lets you be notified every AJAX request is completed in WKWebView:

    import UIKit
    import WebKit
    
    class WebViewController: UIViewController {
    
        private var wkWebView: WKWebView!
        private let handler = "handler"
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let config = WKWebViewConfiguration()
            let userScript = WKUserScript(source: getScript(), injectionTime: .atDocumentStart, forMainFrameOnly: false)
            config.userContentController.addUserScript(userScript)
            config.userContentController.add(self, name: handler)
    
            wkWebView = WKWebView(frame:  view.bounds, configuration: config)
            view.addSubview(wkWebView)
    
            if let url = URL(string: "YOUR AJAX WEBSITE") {
                wkWebView.load(URLRequest(url: url))
            } else {
                print("Wrong URL!")
            }
        }
    
        private func getScript() -> String {
            if let filepath = Bundle.main.path(forResource: "script", ofType: "js") {
                do {
                    return try String(contentsOfFile: filepath)
                } catch {
                    print(error)
                }
            } else {
                print("script.js not found!")
            }
            return ""
        }
    }
    
    extension WebViewController: WKScriptMessageHandler {
        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            if let dict = message.body as? Dictionary<String, AnyObject>, let status = dict["status"] as? Int, let responseUrl = dict["responseURL"] as? String {
                print(status)
                print(responseUrl)
            }
        }
    }
    

    Pretty standard implementation. There is a WKWebView created programmatically. There is injected script that is loaded from script.js file.

    And the most important part is script.js file:

    var open = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function() {
        this.addEventListener("load", function() {
            var message = {"status" : this.status, "responseURL" : this.responseURL}
            webkit.messageHandlers.handler.postMessage(message);
        });
        open.apply(this, arguments);
    };
    

    userContentController delegate method will be called every time there is AJAX request loaded. I'm passing there status and responseURL, because this was what I needed in my case, but you can also get more informations about request. Here is the list of all properties and methods available: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest

    My solution is inspired by this answer written by @John Culviner: https://stackoverflow.com/a/27363569/3448282

    0 讨论(0)
  • 2020-12-02 07:54

    If you have control of the content inside the WkWebView you can send messages to your native app using window.webkit.messageHandlers whenever you make an ajax request, which will be received as a WKScriptMessage that can be processed by whatever you've designated as your WKScriptMessageHandler. The messages can contain whatever information you wish, and will be automatically converted into native objects/values in your Objective-C or Swift code.

    If you don't have control over the content you can still do this by injecting your own JavaScript via a WKUserScript to track ajax requests and send back messages using the method stated above.

    0 讨论(0)
  • 2020-12-02 08:00

    Finally I solved it

    Since I don't have control over the web view content, I injected to the WKWebview a java script that include a jQuery AJAX request listener.

    When the listener catches a request it sends the native app the request body in the method:

    webkit.messageHandlers.callbackHandler.postMessage(data);
    

    The native app catches the message in a delegate called:

    (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
    

    and perform the corresponding actions

    here is the relevant code:

    ajaxHandler.js -

    //Every time an Ajax call is being invoked the listener will recognize it and  will call the native app with the request details
    
    $( document ).ajaxSend(function( event, request, settings )  {
        callNativeApp (settings.data);
    });
    
    function callNativeApp (data) {
        try {
            webkit.messageHandlers.callbackHandler.postMessage(data);
        }
        catch(err) {
            console.log('The native context does not exist yet');
        }
    }
    

    My ViewController delegate are:

    @interface BrowserViewController : UIViewController <UIWebViewDelegate, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler, UIWebViewDelegate>
    

    And in my viewDidLoad(), I'm creating a WKWebView:

    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
    [self addUserScriptToUserContentController:configuration.userContentController];
    appWebView = [[WKWebView alloc]initWithFrame:self.view.frame configuration:configuration];
    appWebView.UIDelegate = self;
    appWebView.navigationDelegate = self;
    [appWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString: @"http://#############"]]];                                                     
    

    Here is the addUserScriptToUserContentController:

    - (void) addUserScriptToUserContentController:(WKUserContentController *) userContentController{
        NSString *jsHandler = [NSString stringWithContentsOfURL:[[NSBundle mainBundle]URLForResource:@"ajaxHandler" withExtension:@"js"] encoding:NSUTF8StringEncoding error:NULL];
        WKUserScript *ajaxHandler = [[WKUserScript alloc]initWithSource:jsHandler injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:NO];
        [userContentController addScriptMessageHandler:self name:@"callbackHandler"];
        [userContentController addUserScript:ajaxHandler];
    }
    
    0 讨论(0)
  • 2020-12-02 08:10

    You can use this to respond to requests from the WKWebView. It works similar to UIWebView.

    - (void)webView:(WKWebView *)webView2 decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
        
        if (navigationAction.navigationType == WKNavigationTypeLinkActivated) {
         
            NSString *url = [navigationAction.request.URL absoluteString];
            
           // Handle URL request internally
    
        }
    
        decisionHandler(WKNavigationActionPolicyAllow); // Will continue processing request
    
        decisionHandler(WKNavigationActionPolicyCancel); // Cancels request
    }
    
    0 讨论(0)
提交回复
热议问题