I have a WebView in a MAUI app that generally works, but whenever I click a link on iOS that is supposed to download a file (link returns a Content-Disposition header) it is opened in the WebView. I would like it to be downloaded (and opened in the default iOS app) instead.
How is this supposed to be implemented? There is apparently a IWKDownloadDelegate
interface with a DecideDestination()
method but I can't find examples of how to wire it all up in MAUI. I got it working on Android by writing some platform-specific code, and I imagine something similar can be done for iOS.
<WebView
x:Name="WebView"
Source=".." />
public partial class WebClientPage : ContentPage
{
public WebClientPage()
{
InitializeComponent();
}
protected override void OnHandlerChanged()
{
base.OnHandlerChanged();
#if IOS
var iosWebView = WebView.Handler.PlatformView as WebKit.WKWebView;
// Otherwise swiping doesn't work
iosWebView.AllowsBackForwardNavigationGestures = true;
#endif
}
}
Related question for Android: Download files in MAUI Android WebView
You can download files on iOS by handling the DecideDestination
event of the WKDownloadDelegate
. Also, you need to implement IWKDownloadDelegate
and IWKNavigationDelegate
and override the default response to show the downloaded file. Here's a solution provided by Tim for your reference:
using ObjCRuntime;
using WebKit;
namespace WebviewTestCatalyst;
[Register("AppDelegate")]
public class AppDelegate : UIApplicationDelegate
{
public override UIWindow? Window { get; set; }
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// create a new window instance based on the screen size
Window = new UIWindow(UIScreen.MainScreen.Bounds);
// create a UIViewController with a single UILabel
var vc = new UIViewController();
var webview = new TestDownloadWebView(Window!.Frame, new WebKit.WKWebViewConfiguration())
{
AutoresizingMask = UIViewAutoresizing.All
};
vc.View!.AddSubview(webview);
Window.RootViewController = vc;
// make the window visible
Window.MakeKeyAndVisible();
webview.LoadRequest(new NSUrlRequest(
new NSUrl("https://file-examples.com/index.php/sample-documents-download/sample-pdf-download/")));
return true;
}
public class TestDownloadWebView : WKWebView, IWKDownloadDelegate, IWKNavigationDelegate
{
public TestDownloadWebView(CGRect frame, WKWebViewConfiguration configuration) : base(frame, configuration)
{
this.NavigationDelegate = this;
}
public void DecideDestination(WKDownload download, NSUrlResponse response, string suggestedFilename,
Action<NSUrl> completionHandler)
{
var destinationURL = GetDestinationURL();
completionHandler?.Invoke(destinationURL);
}
[Export("webView:decidePolicyForNavigationResponse:decisionHandler:")]
public void DecidePolicy(WKWebView webView, WKNavigationResponse navigationResponse, Action<WKNavigationResponsePolicy> decisionHandler)
{
var url = navigationResponse.Response.Url;
var mimeType = navigationResponse.Response.MimeType;
Console.WriteLine($"Content-Type: {mimeType}");
// Perform any actions based on the content type
if (mimeType == "application/pdf")
{
// Download the PDF file separately instead of loading it in the WKWebView
DownloadPDF(url);
decisionHandler?.Invoke(WKNavigationResponsePolicy.Cancel);
}
else
{
decisionHandler?.Invoke(WKNavigationResponsePolicy.Allow);
}
}
private void DownloadPDF(NSUrl url)
{
var downloadTask = NSUrlSession.SharedSession.CreateDownloadTask(url, (location, _, error) =>
{
if (location is NSUrl sourceURL && error == null)
{
var destinationURL = GetDestinationURL();
try
{
NSFileManager.DefaultManager.Move(sourceURL, destinationURL, out error);
Console.WriteLine($"PDF file downloaded and saved at: {destinationURL.Path}");
// Perform any additional actions with the downloaded file
}
catch (Exception ex)
{
// Handle file moving error
}
}
else
{
// Handle download error
}
});
downloadTask.Resume();
}
private NSUrl GetDestinationURL()
{
// Customize the destination URL as desired
var documentsURL =
NSFileManager.DefaultManager.GetUrls(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomain.User)
[0];
var destinationURL = documentsURL.Append("downloaded_file.pdf", false);
return destinationURL;
}
}
}