Browse Source

[iOS] Implement Save File Picker Support (#19364)

* [iOS] Implement Save File Picker Support

* Delete folder instead

* Use StorageProviderHelpers

* Use FromBytes to create blank file
release/11.3.3
Tim Miller 6 months ago
committed by Julien Lebosquain
parent
commit
999bf886f8
  1. 3
      samples/ControlCatalog.iOS/Info.plist
  2. 70
      src/iOS/Avalonia.iOS/Storage/IOSStorageProvider.cs

3
samples/ControlCatalog.iOS/Info.plist

@ -16,7 +16,6 @@
<array>
<integer>1</integer>
<integer>2</integer>
<integer>3</integer>
</array>
<key>UIRequiredDeviceCapabilities</key>
<array>
@ -38,5 +37,7 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
</dict>
</plist>

70
src/iOS/Avalonia.iOS/Storage/IOSStorageProvider.cs

@ -26,7 +26,7 @@ internal class IOSStorageProvider : IStorageProvider
public bool CanOpen => true;
public bool CanSave => false;
public bool CanSave => true;
public bool CanPickFolder => true;
@ -161,10 +161,72 @@ internal class IOSStorageProvider : IStorageProvider
return Task.FromResult<IStorageFolder?>(new IOSStorageFolder(uri, wellKnownFolder));
}
public Task<IStorageFile?> SaveFilePickerAsync(FilePickerSaveOptions options)
public async Task<IStorageFile?> SaveFilePickerAsync(FilePickerSaveOptions options)
{
return Task.FromException<IStorageFile?>(
new PlatformNotSupportedException("Save file picker is not supported by iOS"));
/*
This requires a bit of dialog here...
To save a file, we need to present the user with a document picker
This requires a temp file to be created and used to "export" the file to.
When the user picks the file location and name, UIDocumentPickerViewController
will give back the URI to the real file location, which we can then use
to give back as an IStorageFile.
https://developer.apple.com/documentation/uikit/uidocumentpickerviewcontroller
Yes, it is weird, but without the temp file it will explode.
*/
// Create a temporary file to use with the document picker
var tempFileName = StorageProviderHelpers.NameWithExtension(
options.SuggestedFileName ?? "document",
options.DefaultExtension,
options.FileTypeChoices?.FirstOrDefault());
var tempDir = NSFileManager.DefaultManager.GetTemporaryDirectory().Append(Guid.NewGuid().ToString(), true);
if (tempDir == null)
{
throw new InvalidOperationException("Failed to get temporary directory for save file picker");
}
var isDirectoryCreated = NSFileManager.DefaultManager.CreateDirectory(tempDir, true, null, out var error);
if (!isDirectoryCreated)
{
throw new InvalidOperationException("Failed to create temporary directory for save file picker");
}
var tempFileUrl = tempDir.Append(tempFileName, false);
// Create an empty file at the temp location
NSData.FromBytes(0, 0).Save(tempFileUrl, false);
UIDocumentPickerViewController documentPicker;
if (OperatingSystem.IsIOSVersionAtLeast(14))
{
documentPicker = new UIDocumentPickerViewController(new[] { tempFileUrl }, asCopy: true);
}
else
{
#pragma warning disable CA1422
documentPicker = new UIDocumentPickerViewController(tempFileUrl, UIDocumentPickerMode.ExportToService);
#pragma warning restore CA1422
}
using (documentPicker)
{
if (OperatingSystem.IsIOSVersionAtLeast(13))
{
documentPicker.DirectoryUrl = GetUrlFromFolder(options.SuggestedStartLocation);
}
documentPicker.Title = options.Title;
var tcs = new TaskCompletionSource<NSUrl[]>();
documentPicker.Delegate = new PickerDelegate(urls => tcs.TrySetResult(urls));
var urls = await ShowPicker(documentPicker, tcs);
// Clean up the temporary directory
NSFileManager.DefaultManager.Remove(tempDir, out _);
return urls.FirstOrDefault() is { } url ? new IOSStorageFile(url) : null;
}
}
public async Task<IReadOnlyList<IStorageFolder>> OpenFolderPickerAsync(FolderPickerOpenOptions options)

Loading…
Cancel
Save