最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to intercept a WKWebView request to detect which local resource files (css, js, png, ...) load together with a

programmeradmin2浏览0评论

I have a HTML file which contains local resource files such as css, js and png files inside its content. These local resource files are in zip format. My app use WKWebView to display this html file. I want to find a solution to intercept the web view request to detect which local resource files are load together with this html file -> then unzip them if they are still zip format.

My HTML data content contains thousands of these local resource file so I can't unzip all of them before display content. With UIWebView, we are using NSURLProtocol subclass to intercept the request, detect local resource files and un-zip it on demand based on the html page which user is viewing.

I am having this issue when convert UIWebView to WKWebView. Similar problem was post here:

======= Update =======>

I figured it out by using WKURLSchemeHandler.

Note: You need to change the file scheme to a custom scheme in order to use WKURLSchemeHandler because it will not work with standard schemes like file, http, https.

1. Register custom scheme with WKWebView

    let configuration = WKWebViewConfiguration()
    configuration.setURLSchemeHandler(self, forURLScheme: "x-file")
    webView = WKWebView(frame: view.bounds, configuration: configuration)

2. Convert file scheme to the custom scheme (x-file) 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: "x-file", forURL: htmlURL)
    self.webView.load(URLRequest(url: htmlURL))


3. Implement 2 methods of WKURLSchemeHandler protocol and handle 3 delegate methods of WKURLSchemeTask.

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

        // Your local resource files will be catch here. You can determine it by checking the urlSchemeTask.request.url.
        // From here I will unzip local resource files (js, css, png,...) if they are still in zip format
        ....

        // 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")
    }

I have a HTML file which contains local resource files such as css, js and png files inside its content. These local resource files are in zip format. My app use WKWebView to display this html file. I want to find a solution to intercept the web view request to detect which local resource files are load together with this html file -> then unzip them if they are still zip format.

My HTML data content contains thousands of these local resource file so I can't unzip all of them before display content. With UIWebView, we are using NSURLProtocol subclass to intercept the request, detect local resource files and un-zip it on demand based on the html page which user is viewing.

I am having this issue when convert UIWebView to WKWebView. Similar problem was post here: https://forums.developer.apple.com/thread/87474

======= Update =======>

I figured it out by using WKURLSchemeHandler.

Note: You need to change the file scheme to a custom scheme in order to use WKURLSchemeHandler because it will not work with standard schemes like file, http, https.

1. Register custom scheme with WKWebView

    let configuration = WKWebViewConfiguration()
    configuration.setURLSchemeHandler(self, forURLScheme: "x-file")
    webView = WKWebView(frame: view.bounds, configuration: configuration)

2. Convert file scheme to the custom scheme (x-file) 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: "x-file", forURL: htmlURL)
    self.webView.load(URLRequest(url: htmlURL))


3. Implement 2 methods of WKURLSchemeHandler protocol and handle 3 delegate methods of WKURLSchemeTask.

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

        // Your local resource files will be catch here. You can determine it by checking the urlSchemeTask.request.url.
        // From here I will unzip local resource files (js, css, png,...) if they are still in zip format
        ....

        // 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")
    }
Share Improve this question edited Oct 15, 2019 at 21:05 Tony Pham asked Oct 14, 2019 at 23:51 Tony PhamTony Pham 3931 gold badge3 silver badges9 bronze badges 4
  • This comment was excellent urlSchemeTask.request.url. I couldn't work out why webView.url never showed the CSS, JS requests. Thanks again. – rustyMagnet Commented Dec 17, 2019 at 14:09
  • 1 where do you get changeURLScheme(newScheme: forURL:) method from? – Piotr Z Commented Feb 28, 2020 at 15:15
  • changeURLScheme is our custom method. We just simple change the custom scheme back to file:// scheme - (NSURL *)changeURLSchemeTo:(NSString *)newScheme { NSURLComponents *components = [NSURLComponents componentsWithURL:self resolvingAgainstBaseURL:YES]; if (components) { components.scheme = newScheme; return components.URL ? components.URL : self; } return self; } – Tony Pham Commented Aug 13, 2020 at 20:26
  • @TonyPham Isolate your solution to an answer and pick it as the correct answer. – user31208 Commented Feb 17, 2021 at 15:19
Add a comment  | 

2 Answers 2

Reset to default 10

If WKWebView loads a local html file you could just give the WKWebView access to local app resources like so:

NSURL *documentDirectoryURL = [NSURL fileURLWithPath:DOCUMENTS_DIRECTORY];

// This code gives acces to a file that is outside of our webview html file root directory
[self.webView loadFileURL:documentDirectoryURL allowingReadAccessToURL:documentDirectoryURL];

// If you use one of these loads after, they will too have access if they are in app html files
[self.webView loadRequest:<inAppHTMLFile>];
[self.webView loadHTMLString: baseURL:];

This here gives access to all files in the document directory.

I found your solution helpful when I wanted to link local resources to online pages.

WKUserContentController *contentController = [[WKUserContentController alloc]init];
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc]init];
CustomFileShemeHandler *schemeHandler = [[CustomFileShemeHandler alloc] init];
[config setURLSchemeHandler:schemeHandler forURLScheme:@"myApp-images"];
config.userContentController = contentController;
self.webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];



//CustomFileShemeHandler.m

@implementation CustomFileShemeHandler

- (void)webView:(nonnull WKWebView *)webView startURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask {
    NSURL *customFileURL = urlSchemeTask.request.URL;
    NSString *securityFilter = [NSString stringWithFormat:@"Documents/LocalAppImages"];

    if (![customFileURL.absoluteString containsString:securityFilter]) {
        return;
    }

    NSURL *fileURL = [self changeURLScheme:customFileURL toScheme:@"file"];
    NSURLRequest* fileUrlRequest = [[NSURLRequest alloc] initWithURL:fileURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:.1];

    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:fileUrlRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSURLResponse *response2 = [[NSURLResponse alloc] initWithURL:urlSchemeTask.request.URL MIMEType:response.MIMEType expectedContentLength:data.length textEncodingName:nil];
        if(error){
            [urlSchemeTask didFailWithError:error];
        }
        [urlSchemeTask didReceiveResponse:response2];
        [urlSchemeTask didReceiveData:data];
        [urlSchemeTask didFinish];
    }];

    [dataTask resume];
}

- (void)webView:(nonnull WKWebView *)webView stopURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask {
    NSLog(@"DUNNO WHAT TO DO HERE");
}

- (NSURL *)changeURLScheme:(NSURL *)url toScheme:(NSString *)newScheme {
    NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:YES];
    components.scheme = newScheme;
    return components.URL;
}

With this any page open in the webView can link to a local resource using its full path an the custom scheme:

<img src="myApp-images:///<YourDocumentsAppPahtHere>/LocalAppImages/funnyDog.jpg">

Following the answer of h3dkandi this comment helped me solve my problem: (updated code in swift)

If you first use:

 // use this in order to access images, css files etc..
 webView.loadFileURL(baseURL, allowingReadAccessTo: baseURL) 

 // use this method to actually load the html with images, css etc..
 webView.loadHTMLString(htmlString, baseURL: baseURL)

Where baseURL is the path that my local .html file exists.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论