This solution is only for readonly access to the files that I was trying open with Launcher. Though readonly, it is a solution that can open a wide variety of filetypes. If you need read/write, I still don't know how to use Launcher for either MACATALYST or iOS.
The MAUI replacement for Xamarin's Dependency Service is called Platform Specific Conditional Code. Combining the old with the new, gives me what I need. For example, I have a Command-tied OpenSelectedFile method in my ViewModel which contains this fragment:
#if (IOS || MACCATALYST)
AppleFileHandler.OpenFile(fileAndPath);
#elif ANDROID
// unkown - in Xamarin we didn't support Android. We'd like to for MAUI.
#else
var roFile = new ReadOnlyFile(fileAndPath);
ret = await Launcher.Default.OpenAsync(new OpenFileRequest(roFile.FileName, roFile));
#endif
AppleFileHandler is what I wrote to limit the complexity of the above statements. Much of the code I took from my Xamarin dependency service, which was written about 8 years ago and still works. (Note: I still do not know what the Android equivalent is, but Launcher works fine in Windows.)
#if (IOS || MACCATALYST)
using QuickLook;
using UIKit;
public class AppleFileHandler
{
public static void OpenFile(string fileName)
{
var fileInfo = new FileInfo(fileName);
using (QLPreviewController previewController = new())
{
previewController.DataSource = new PreviewControllerDataSource(Path.Combine(fileInfo.DirectoryName, fileInfo.Name), fileInfo.Name);
UINavigationController controller = FindNavigationController();
controller?.PresentViewController(previewController, true, null);
controller = null;
}
}
private static UINavigationController FindNavigationController()
{
foreach(UIWindow window in UIApplication.SharedApplication.Windows)
{
if (window.RootViewController.NavigationController != null)
return window.RootViewController.NavigationController;
else
{
UINavigationController value = CheckSubs(window.RootViewController.ChildViewControllers);
if (value != null)
return value;
}
}
return new UINavigationController();
}
private static UINavigationController CheckSubs(UIViewController[] controllers)
{
foreach(UIViewController controller in controllers)
{
if (controller.NavigationController != null)
return controller.NavigationController;
else
{
UINavigationController value = CheckSubs(controller.ChildViewControllers);
if (value != null)
return value;
}
return null;
}
return new UINavigationController();
}
}
#endif
The above code depends on the next two classes. As I understand it, this code could be in the the relevant platform folder or outside, like I have, with preprocessor statements.
#if (IOS || MACCATALYST)
using Foundation;
using QuickLook;
public class DocumentItem : QLPreviewItem
{
private string title;
private string url;
public DocumentItem(string title, string url)
{
this.title = title;
this.url = url;
}
public override string PreviewItemTitle => title;
public override NSUrl PreviewItemUrl => NSUrl.FromFilename(url);
}
#endif
and
using System;
#if (IOS || MACCATALYST)
using QuickLook;
public class PreviewControllerDataSource : QLPreviewControllerDataSource
{
private string url;
private string fileName;
public PreviewControllerDataSource(string url, string fileName)
{
this.url = url;
this.fileName = fileName;
}
public override IQLPreviewItem GetPreviewItem(QLPreviewController controller, nint index)
{
return new DocumentItem(fileName, url);
}
public override nint PreviewItemCount(QLPreviewController controller)
{
return 1;
}
}
#endif