2

I am trying to capture the downloading of a zip file from a WKWebView in iOS. After logging in, going to this link, requesting the archive and clicking the download button, the zip file is not downloaded. However, the WKWebView does display the file in the web browser, or appears to do so (see screenshots below). When trying to download the file from the web view, I'm only pulling an HTML file.

Download Button Shows Up Zip File displayed in WKWebView

Can someone provide some direction here, regarding the right approach to catch and download the zip file? NOTE, the zip file has no direct link. Code for the WKWebView delegate method, and the Download Handler below.

webView didFinishNavigation

 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    NSURLComponents *comps = [[NSURLComponents alloc] initWithURL:webView.URL

                                          resolvingAgainstBaseURL:NO];
    comps.query = nil;

    NSLog(@"did finish nav URL: %@", webView.URL);

    if ([webView.URL.absoluteString isEqualToString:LI_DOWNLOAD_URL]) {

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
            [DownloadHandler downloadFileFromURL:webView.URL completion:^(NSString *filepath) {
                NSLog(@"%@",filepath);

            }];
        });
    }
    else if ([comps.string  isEqual: LI_REDIRECT_CATCH1] ||
        [comps.string  isEqual: LI_REDIRECT_CATCH2] ||
        [comps.string  isEqual: LI_REDIRECT_CATCH3]) {

        self.statusText.text = @"Tap the \"Sign In\" button to log into LinkedIn";
    }
    else if ([comps.string isEqual: LI_EXPORT_PAGE]) {
        NSString *javascript = @"javascript:" \
                                "var reqBtn = document.getElementById('request-button');" \
                                "var pndBtn = document.getElementById('pending-button');" \
                                "var dwnBtn = document.getElementById('download-button');" \
                                "if (reqBtn) {" \
                                "   window.scrollTo(reqBtn.offsetLeft, 0);" \
                                "   window.webkit.messageHandlers.dataExport.postMessage('willRequestData');" \
                                "   reqBtn.addEventListener('click', function() {" \
                                "       window.webkit.messageHandlers.dataExport.postMessage('didRequestData');" \
                                "   }, false);" \
                                "} else if (pndBtn) {" \
                                "   window.scrollTo(pndBtn.offsetLeft, 0);" \
                                "   window.webkit.messageHandlers.dataExport.postMessage('isRequestPending');" \
                                "} else if (dwnBtn) {" \
                                "   window.scrollTo(dwnBtn.offsetLeft, 0);" \
                                "   window.webkit.messageHandlers.dataExport.postMessage('willDownloadData');" \
                                "   dwnBtn.onclick = function() {" \
                                "       window.webkit.messageHandlers.dataExport.postMessage('didDownloadData');" \
                                "   };" \
                                "}";

        [self.webView evaluateJavaScript:javascript completionHandler:nil];
    }
}

Download Handler

+ (void)downloadFileFromURL:(NSURL *)url completion:(void (^)(NSString *filepath))completion {

    NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];

    NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";

    NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request
                                                    completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

            if (!error) {

                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{

                    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
                    NSString *documentsDirectory = [paths objectAtIndex:0];
                    NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:@"linkedin-file-export.zip"];

                    [data writeToFile:dataPath atomically:YES];

                    completion(dataPath);
                });
            }
            else {
                NSLog(@"%@",error);

                completion([NSString string]);
            }
    }];
    [postDataTask resume];
}

I have tried this through using a UIWebView as well, and am getting the same result. In Android, this can be accomplished with a download listener. I'd be open to an approach like this if its available, but I don't think it is in iOS.

Thanks for any help.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Noah Labhart
  • 157
  • 4
  • 18

1 Answers1

3

You are trying to download the zip after the webview finished loading the zip file url.. I think the approach should be to stop the webview from loading the zip file url and allow your API to download the file to the desired path..

Something like the below. The snippet below assumes the zip file url has the extension .zip

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

    NSURLRequest *request = navigationAction.request;
    NSString *fileExtension = [[request URL]absoluteString].pathExtension;
    if(fileExtension.length && [fileExtension caseInsensitiveCompare:@"zip"] == NSOrderedSame){
        //Do not load the zip url in the webview.
        decisionHandler(WKNavigationActionPolicyCancel);
        //Instead use the API to download the file
        [DownloadHandler downloadFileFromURL:[request URL] completion:^(NSString *filepath) {
            NSLog(@"%@",filepath);

        }];
    }

    //Allow all other request types to load in the webview
    decisionHandler(WKNavigationActionPolicyAllow);
}

Would love to know if this worked for you.

Subbu
  • 2,138
  • 14
  • 20
  • Thanks for the reply @Subbu. However, this did not work because the link does not have a fileExtension. If it did, I would be in much better shape. I think that the issue is WKWebView and NSURLConnection do not share cookies. It would appear I need to manually transfer them to the NSURLConnection for download. – Noah Labhart Jul 23 '15 at 03:13
  • Oh okay; @NoahLabhart yes WKWebView doesn't use the NSHTTPCookieStorage like UIWebView does.. Did you want to try comparing the url to your `LI_DOWNLOAD_URL` in this delegate method? – Subbu Jul 23 '15 at 03:21
  • I did that too. Same thing, it tries to route the URL back to the join page. Which says to me that the cookies aren't set. Which means that the authentication isn't shared between WKWebView and NSURLConnection. – Noah Labhart Jul 23 '15 at 03:29
  • I was able to get this to work by combining your solution with https://stackoverflow.com/a/27450860 to copy the cookies over. Thanks! – Billy Feb 09 '18 at 15:47