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§ion=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
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);
}
}
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.
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..