C# Xamarin.iOS UIDocumentBrowser无法在第三方容器中创建文件

C# Xamarin.iOS UIDocumentBrowser无法在第三方容器中创建文件,c#,ios,swift,xamarin,xamarin.ios,C#,Ios,Swift,Xamarin,Xamarin.ios,我一直在尝试使用UIDocumentBrowserViewController在Xamarin的iOS上实现一个“保存文件”对话框。在手机和iCloud中创建和选择文件时,一切都很有效。但在创建文档时,它在OneDrive中以静默方式失败(在您尝试在其他地方访问它之前,full-on会假装工作),并在Google Drive中显示一条错误消息(“操作无法完成。(com.apple.DocumentManager error 1.)”) 从我的阅读中可以看出,如果它在iCloud中工作,那么它应该

我一直在尝试使用UIDocumentBrowserViewController在Xamarin的iOS上实现一个“保存文件”对话框。在手机和iCloud中创建和选择文件时,一切都很有效。但在创建文档时,它在OneDrive中以静默方式失败(在您尝试在其他地方访问它之前,full-on会假装工作),并在Google Drive中显示一条错误消息(“操作无法完成。(com.apple.DocumentManager error 1.)”)

从我的阅读中可以看出,如果它在iCloud中工作,那么它应该在一个驱动器和Google驱动器中工作,但我似乎不知道我可能缺少什么。我已经在下面发布了代码;它松散地基于Xam.Filepicker nuget

public class SaveDocumentBrowser : UIDocumentBrowserViewControllerDelegate
    {
        //Request ID for current picking call
        private int requestId;

        //Task returned when all is completed
        private TaskCompletionSource<FilePlaceholder> completionSource;

        //Event which is invoked when a file was picked. Used elsewhere
        internal EventHandler<FilePlaceholder> Handler { get; set; }

        //Extensions used for choosing file names
        private string[] _extensions;

        //Called when a file has been chosen or created
        private async void OnFilePicked(NSUrl destination, bool creation = false)
        {
            if (destination == null || !destination.IsFileUrl)
            {
                this.Handler?.Invoke(this, null);
                return;
            }

            var document = new GenericDocument(destination);
            var success = await document.OpenAsync();

            if (!success)
            {
                this.Handler?.Invoke(this, null);
                return;
            }

            async Task StreamSetter(Stream stream, FilePlaceholder placeholder)
            {
                document.DataStream = stream;

                try
                {
                    if (!await document.SaveAsync(destination, creation ? UIDocumentSaveOperation.ForCreating : UIDocumentSaveOperation.ForOverwriting))
                    {
                        throw new Exception("Failed to Save Document.");
                    }
                }
                finally
                {
                    await document.CloseAsync();
                }
            }

            var placeHolder = new FilePlaceholder(destination.AbsoluteString, destination.LastPathComponent, StreamSetter, b => document.Dispose());

            this.Handler?.Invoke(null, placeHolder);
        }

        //Delegate for when user requests document creation
        public override void DidRequestDocumentCreation(UIDocumentBrowserViewController controller, Action<NSUrl, UIDocumentBrowserImportMode> importHandler)
        {
            //this is a custom view for choosing a name for the new file
            var editController = new FileNameInputViewController(_extensions);

            void OnEditControllerOnOnViewDidDisappear(object sender, EventArgs args)
            {
                editController.OnViewDidDisappear -= OnEditControllerOnOnViewDidDisappear;

                if (string.IsNullOrEmpty(editController.FileName))
                {
                    importHandler(null, UIDocumentBrowserImportMode.None);
                    return;
                }

                try
                {
                    var documentFolder = Path.GetTempPath();
                    var tempFileName = editController.FileName;

                    var path = Path.Combine(documentFolder, tempFileName);
                    var tempFile = File.Create(path);
                    tempFile.Dispose();

                    var url = NSUrl.CreateFileUrl(path, false, null);

                    importHandler(url, UIDocumentBrowserImportMode.Move);
                }
                catch(Exception e)
                {
                    Debug.WriteLine("Failed to create temp doc: " + e);
                    var dialog = UIAlertController.Create("Error", "Error creating temp file: " + e.Message, UIAlertControllerStyle.Alert);
                    dialog.AddAction(UIAlertAction.Create("Done", UIAlertActionStyle.Cancel, null));

                    controller.PresentViewController(dialog, false, null);
                    importHandler(null, UIDocumentBrowserImportMode.None);
                }
            }

            editController.OnViewDidDisappear += OnEditControllerOnOnViewDidDisappear;

            controller.PresentViewController(editController, true, null);
             
        }

        //Delegate for when user picks file
        public override void DidPickDocumentUrls(UIDocumentBrowserViewController controller, NSUrl[] documentUrls)
        {
            var dialog = UIAlertController.Create("Overwriting file", "Are you sure you want to overwrite this file?", UIAlertControllerStyle.Alert);
            dialog.AddAction(UIAlertAction.Create("Yes", UIAlertActionStyle.Default, action => OnFilePicked(documentUrls[0])));
            dialog.AddAction(UIAlertAction.Create("No", UIAlertActionStyle.Cancel, null));

            controller.PresentViewController(dialog, false, null);
        }

        //Delegate for when user picks files (not used at this time)
        public override void DidPickDocumentsAtUrls(UIDocumentBrowserViewController controller, NSUrl[] documentUrls)
        {
            var dialog = UIAlertController.Create("Overwriting file", "Are you sure you want to overwrite this file?", UIAlertControllerStyle.Alert);
            dialog.AddAction(UIAlertAction.Create("Yes", UIAlertActionStyle.Default, action => OnFilePicked(documentUrls[0])));
            dialog.AddAction(UIAlertAction.Create("No", UIAlertActionStyle.Cancel, null));

            controller.PresentViewController(dialog, false, null);
        }

        //Delegate for when created document successfully import
        public override void DidImportDocument(UIDocumentBrowserViewController controller, NSUrl sourceUrl, NSUrl destinationUrl)
        {
            OnFilePicked(destinationUrl);
        }

        //Delegate for when created document fails import
        public override void FailedToImportDocument(UIDocumentBrowserViewController controller, NSUrl documentUrl, NSError error)
        {
            Debug.WriteLine("Failed to import doc: " + error);
            var dialog = UIAlertController.Create("Error", "Error creating file: " + error, UIAlertControllerStyle.Alert);
            dialog.AddAction(UIAlertAction.Create("Done", UIAlertActionStyle.Cancel, null));

            controller.PresentViewController(dialog, false, null);
        }


        /// <summary>
        /// File picking implementation
        /// </summary>
        /// <param name="allowedTypes">list of allowed types; may be null</param>
        /// <returns>picked file data, or null when picking was cancelled</returns>
        public Task<FilePlaceholder> PickMediaAsync(string[] allowedTypes)
        {
            var id = this.GetRequestId();

            var ntcs = new TaskCompletionSource<FilePlaceholder>(id);

            if (Interlocked.CompareExchange(ref this.completionSource, ntcs, null) != null)
            {
                throw new InvalidOperationException("Only one operation can be active at a time");
            }

            var allowedUtis = new string[]
            {
                UTType.Content,
                UTType.Item,
                "public.data"
            };

            if (allowedTypes != null)
            {
                allowedUtis = allowedTypes.Where(x => x[0] != '.').ToArray();
                _extensions = allowedTypes.Where(x => x[0] == '.').ToArray();
            }
            else
            {
                _extensions = null;
            }

            //This is only custom so we can hook onto the dismissal event
            var documentBrowser = new CustomDocumentBrowserViewController(allowedUtis)
            {
                AllowsDocumentCreation = true,
                AllowsPickingMultipleItems = false,
                Delegate = this,
            };

            void OnDocumentBrowserOnOnViewDidDisappear(object sender, EventArgs args)
            {
                OnFilePicked(null);
            }

            documentBrowser.OnViewDidDisappear += OnDocumentBrowserOnOnViewDidDisappear;
            
            UIViewController viewController = GetActiveViewController();
            viewController.PresentViewController(documentBrowser, false, null);

            this.Handler = (sender, args) =>
            {
                documentBrowser.OnViewDidDisappear -= OnDocumentBrowserOnOnViewDidDisappear;
                documentBrowser.DismissViewController(false, null);

                var tcs = Interlocked.Exchange(ref this.completionSource, null);
                tcs?.SetResult(args);
            };

            return this.completionSource.Task;
        }

        //Get current view controller for presentation
        private static UIViewController GetActiveViewController()
        {
            UIWindow window = UIApplication.SharedApplication.KeyWindow;
            UIViewController viewController = window.RootViewController;

            while (viewController.PresentedViewController != null)
            {
                viewController = viewController.PresentedViewController;
            }

            return viewController;
        }

        //increment to a new id for Task completion sources
        private int GetRequestId()
        {
            var id = this.requestId;

            if (this.requestId == int.MaxValue)
            {
                this.requestId = 0;
            }
            else
            {
                this.requestId++;
            }

            return id;
        }

        //custom inheritance solely so we can see if user dismisses the view
        private class CustomDocumentBrowserViewController : UIDocumentBrowserViewController
        {
            public event EventHandler OnViewDidDisappear;
            public override void ViewDidDisappear(bool animated)
            {
                base.ViewDidDisappear(animated);
                OnViewDidDisappear?.Invoke(this, null);
            }

            public CustomDocumentBrowserViewController(string[] contentTypes) : base(contentTypes)
            {

            }
        }
    }
public类SaveDocumentBrowser:UIDocumentBrowser服务控制器delegate
{
//当前分拣呼叫的请求ID
私有int请求id;
//全部完成后返回的任务
私有TaskCompletionSource completionSource;
//事件,该事件在拾取文件时调用。在别处使用
内部事件处理程序{get;set;}
//用于选择文件名的扩展名
私有字符串[]_扩展名;
//在选择或创建文件时调用
私有异步无效OnFilePicked(NSUrl目标,bool创建=false)
{
if(destination==null | |!destination.IsFileUrl)
{
this.Handler?.Invoke(this,null);
返回;
}
var文件=新的通用文件(目的地);
var success=wait document.OpenAsync();
如果(!成功)
{
this.Handler?.Invoke(this,null);
返回;
}
异步任务流设置器(流、文件占位符占位符)
{
document.DataStream=流;
尝试
{
如果(!await document.SaveAsync(目标,创建?UIDocumentSaveOperation.ForCreating:UIDocumentSaveOperation.ForOverwrite))
{
抛出新异常(“保存文档失败”);
}
}
最后
{
等待document.CloseAsync();
}
}
var placeHolder=newfileplaceholder(destination.AbsoluteString、destination.LastPathComponent、StreamSetter、b=>document.Dispose());
this.Handler?.Invoke(null,占位符);
}
//当用户请求创建文档时的委托
public override void DidRequestDocumentCreation(UIDocumentBrowserViewController控制器,操作导入器)
{
//这是为新文件选择名称的自定义视图
var editController=新文件名InputViewController(_扩展名);
void OneDitControllerOnViewDidEnglish(对象发送者,事件参数)
{
editController.OnViewDidEnglish-=OneDitControllerOnViewDidEnglishe;
if(string.IsNullOrEmpty(editController.FileName))
{
importHandler(null,UIDocumentBrowserImportMode.None);
返回;
}
尝试
{
var documentFolder=Path.GetTempPath();
var tempFileName=editController.FileName;
var path=path.Combine(documentFolder,tempFileName);
var tempFile=File.Create(路径);
tempFile.Dispose();
var url=NSUrl.CreateFileUrl(路径,false,null);
导入器(url,UIDocumentBrowserImportMode.Move);
}
捕获(例外e)
{
Debug.WriteLine(“未能创建临时文档:+e”);
var dialog=UIAlertController.Create(“错误”,“创建临时文件时出错:”+e.Message,UIAlertControllerStyle.Alert);
AddAction(UIAlertAction.Create(“完成”,UIAlertActionStyle.Cancel,null));
controller.PresentViewController(对话框,false,null);
importHandler(null,UIDocumentBrowserImportMode.None);
}
}
editController.OnViewDidDesile+=OneDitControllerOnViewDidDesile;
controller.PresentViewController(editController,true,null);
}
//当用户选择文件时的委托
public override void DidPickDocumentUrls(UIDocumentBrowserViewController控制器,NSUrl[]documentUrls)
{
var dialog=UIAlertController.Create(“覆盖文件”、“是否确实要覆盖此文件?”,UIAlertControllerStyle.Alert);
AddAction(UIAlertAction.Create(“是”,UIAlertActionStyle.Default,action=>OnFilePicked(documentUrls[0]));
AddAction(UIAlertAction.Create(“否”,UIAlertActionStyle.Cancel,null));
controller.PresentViewController(对话框,false,null);
}
//用户拾取文件时的委托(此时不使用)
public override void didpickDocumentsThatls(UIDocumentBrowserViewController控制器,NSUrl[]documentUrls)
{
var dialog=UIAlertController.Create(“覆盖文件”、“是否确实要覆盖此文件?”,UIAlertControllerStyle.Alert);
AddAction(UIAlertAction.Create(“是”,UIAlertActionStyle.Default,action=>OnFilePicked(documentUrls[0]));
AddAction(UIAlertAction.Create(“否”,UIAlertActionStyle.Cancel,null));
controller.PresentViewController(对话框,false,null);
}
//成功导入创建的文档时的委托
public override void DidImportDocument(UIDocumentBrowserViewController控制器,NSUrl源URL