Jak mogę monitorować żądania w WKWebview?

Jak mogę monitorować żądania w WKWebview?

Próbowałem użyć NSURLprotocol (canInitWithRequest), ale nie będzie monitorować żądań ajax (XHR), tylko żądania nawigacji (żądania dokumentów)

Author: Benzi Heler, 2015-02-27

3 answers

W końcu to rozwiązałem

Ponieważ nie mam kontroli nad treścią widoku sieci Web, wstrzyknąłem do WKWebview skrypt java, który zawiera słuchacz żądań jQuery AJAX.

Gdy słuchacz przechwyci żądanie, wysyła do aplikacji natywnej treść żądania w metodzie:

webkit.messageHandlers.callbackHandler.postMessage(data);

Natywna aplikacja wychwytuje wiadomość w delegacie o nazwie:

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

I wykonać odpowiednie działania

Oto odpowiedni kod:

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');
    }
}

Mój delegat ViewController to:

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

I w moim viewDidLoad(), tworzę 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://#############"]]];                                                     

Oto 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];
}
 28
Author: Benzi Heler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-29 21:28:25

Jeśli masz kontrolę nad zawartością wewnątrz WkWebView Możesz wysyłać wiadomości do swojej natywnej aplikacji za pomocą window.webkit.messageHandlers za każdym razem, gdy złożysz żądanie ajax, które zostanie odebrane jako WKScriptMessage które mogą być przetwarzane przez cokolwiek określiłeś jako swój WKScriptMessageHandler. Wiadomości mogą zawierać dowolne informacje i będą automatycznie konwertowane na natywne obiekty / wartości w kodzie Objective-C lub Swift.

Jeśli nie masz kontroli nad treścią, nadal możesz to zrobić to poprzez wstrzyknięcie własnego JavaScript poprzez WKUserScript aby śledzić żądania ajax i wysyłać wiadomości zwrotne za pomocą metody podanej powyżej.

 5
Author: Justin Michael,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-02 06:25:38

@ Benzi Heler odpowiedź jest świetna, ale używa jQuery, który wygląda na to, że nie działa już w WKWebView, więc znalazłem rozwiązanie bez używania jQuery.

Oto implementacja ViewController, która pozwala być powiadamianym o każdym żądaniu AJAX jest zakończone w 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)
        }
    }
}

Dość standardowa implementacja. Istnieje WKWebView tworzony programowo. Jest wstrzykiwany skrypt, który jest ładowany z pliku script.js.

A najważniejszą częścią jest script.js plik:

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 metoda delegate będzie wywoływana za każdym razem, gdy zostanie załadowane żądanie AJAX. Przechodzę tam status i responseURL, ponieważ tego potrzebowałem w moim przypadku, ale możecie też uzyskać więcej informacji na temat request. Oto lista wszystkich dostępnych właściwości i metod: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest

Moje rozwiązanie jest inspirowane tą odpowiedzią napisaną przez @ John Culviner: https://stackoverflow.com/a/27363569/3448282

 0
Author: user3448282,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-21 07:03:44