Downloading files WKWebView ios

16,493

Solution 1

Well heres what I ended up with, and all seems to be working fine.

- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
    NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response;
    NSArray *cookies =[NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:response.URL];
    for (NSHTTPCookie *cookie in cookies) {
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
    }

    decisionHandler(WKNavigationResponsePolicyAllow);
    //NSLog(@"decidePolicyForNavigationResponse");
}


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

    NSURLRequest *request = navigationAction.request;
    fileURL = request.URL;

    HCDownloadViewController *dlvc = [[HCDownloadViewController alloc] init];
    UINavigationController *vc = [[UINavigationController alloc] initWithRootViewController:dlvc];
    vc.transitioningDelegate  = self;
    dlvc.delegate = self;

    if (navigationAction.navigationType == WKNavigationTypeLinkActivated) {

        //Internal file links
        NSString *internalFileExtension = fileURL.absoluteString.pathExtension;
        if ([fileExtensions containsObject:[internalFileExtension lowercaseString]]) {

            //Fire download
            [dlvc downloadURL:fileURL userInfo:nil];
            [self presentViewController:vc animated:YES completion:nil];
            [vc release];

            NSLog(@"internalURL is %@", fileURL);
            if (decisionHandler) {
                decisionHandler(WKNavigationActionPolicyCancel);
            }
            return;
        }

        //External file extensions
        NSString *externalFileExtension = fileURL.pathExtension;
        if ([fileExtensions containsObject:[externalFileExtension lowercaseString]]) {

            //Fire download
            [dlvc downloadURL:fileURL userInfo:nil];
            [self presentViewController:vc animated:YES completion:nil];
            [vc release];

            NSLog(@"externalURL is %@", fileURL);
            if (decisionHandler) {
                decisionHandler(WKNavigationActionPolicyCancel);
            }
            return;
        }
    }

    if (decisionHandler) {
        decisionHandler(WKNavigationActionPolicyAllow);
    }
}

Solution 2

You are probably downloading a not-authorized page because your WKWebView and HCDownload instances don't share cookie sessions like UIWebView could. That's a necessary trade-off for the WK2 process model's speed & security improvements.

I added downloading to my WKWebKit-based OSX/Swift mini-browser by implementing _WKDownloadDelegate. Its a totally undocumented private protocol as of El Cap and iOS9. It lets your navigation delegate call decisionHandler(_WKNavigationResponsePolicyBecomeDownload) whereby WebKit will download in the background after giving you the chance to pick/change a file name. I haven't the foggiest idea on how to implement a file picker in iOS yet, nor if using that protocol would be allowed by the App Store reviewers. But my app now does correctly handle downloading from session-authentication sites like your PHP forum.

Solution 3

Swift 4:

ChrisOSX's sollution also solved my issue. I'm trying to download a document from a link on a site after logging to that site. I've used WKZombie to scrape the link, and then I wanted to download the file using Alamofire. I added this

if let httpResponse = navigationResponse.response as? HTTPURLResponse, let url = httpResponse.url {
    let allHeaders = httpResponse.allHeaderFields.reduce([String : String](), { (result, next) -> [String : String] in
    guard let stringKey = next.key as? String, let stringValue = next.value as? String else { return result }
        var buffer = result
        buffer[stringKey] = stringValue
        return buffer
    })
    let cookies = HTTPCookie.cookies(withResponseHeaderFields: allHeaders, for: url)
    for cookie in cookies {
        HTTPCookieStorage.shared.setCookie(cookie)
    }
}

to:

func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void)

After this, the cookies are stored in the shared cookies, and I was able to download the file with Alamofire without and changes to the download methods..

Share:
16,493
ChrisOSX
Author by

ChrisOSX

Updated on June 23, 2022

Comments

  • ChrisOSX
    ChrisOSX almost 2 years

    I'm in the progress of migrating my app from UIWebView to WKWebView. All is going well and working as I tinker more with it. However, I notice now that I can't download forum attachments.

    I'm using HCDownload, and thus far has always worked perfect for me, so I know it's not on that end. I believe its the request, but I cant figure it out.

    I know the following:

    UIWebView => WKWebView Equivalent
    --------------------------------------------------------------
    didFailLoadWithError => didFailNavigation
    webViewDidFinishLoad => didFinishNavigation
    webViewDidStartLoad => didStartProvisionalNavigation
    shouldStartLoadWithRequest => decidePolicyForNavigationAction
    

    So with that known I am trying below:

    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    
        NSURLRequest *request = navigationAction.request;
        NSURL *fileURl = [request URL];
        NSString *externalFileExtension = [fileURl pathExtension];
        NSString *internalFileExtension = [[fileURl absoluteString] pathExtension];
    
        HCDownloadViewController *dlvc = [[HCDownloadViewController alloc] init];
        UINavigationController *vc = [[UINavigationController alloc] initWithRootViewController:dlvc];
        dlvc.delegate = self;
        vc.transitioningDelegate  = self;
    
        if (navigationAction.navigationType == WKNavigationTypeLinkActivated) {
    
            //External file extensions
            if ([fileExtensions containsObject:[externalFileExtension lowercaseString]]) {
                [dlvc downloadURL:fileURl userInfo:nil];
                [self presentViewController:vc animated:YES completion:nil];
                [vc release];
    
                NSLog(@"externalURL is %@", fileURl);
                decisionHandler(WKNavigationActionPolicyCancel);
                return;
            }
    
            //Internal file links
            if ([fileExtensions containsObject:[internalFileExtension lowercaseString]]) {
                [self presentViewController:vc animated:YES completion:nil];
                [dlvc downloadURL:fileURl userInfo:nil];
                [vc release];
    
                NSLog(@"internalURL is %@", fileURl);
                decisionHandler(WKNavigationActionPolicyCancel);
                return;
    
            }
    }
    

    It will fire my download controller by finding the file extension, but instead it downloads the index.php, not the file.

    Example url:

    https://example.com/index.php?app=core&module=attach&section=attach&attach_id=1234=example.zip;
    

    What am I doing wrong. It worked fine before in UIWebView and I know things are done differently. But how can it download something from say dropbox just fine, but an attached file on a forum gets goofed up.

    Any help would be appreciated

  • Marco
    Marco about 6 years
    this does not work, if cookies are set in a response with code 302