Skip to content

Commit

Permalink
feat: StorageFolder/File operations iOS
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinZikmund committed Mar 18, 2021
1 parent af63831 commit 6ac3961
Show file tree
Hide file tree
Showing 9 changed files with 495 additions and 47 deletions.
5 changes: 4 additions & 1 deletion src/Uno.UI/Resources/Strings/cs-CZ/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,13 @@
<data name="StorageProviderAndroidSafDisplayName" xml:space="preserve">
<value>Android Storage Access Framework</value>
</data>
<data name="StorageProviderIosSecurityScopedDisplayName" xml:space="preserve">
<value>iOS Security Scoped</value>
</data>
<data name="StorageProviderLocalDisplayName" xml:space="preserve">
<value>Tento počítač</value>
</data>
<data name="StorageProviderNativeWasmDisplayName" xml:space="preserve">
<value>JS File Access API</value>
</data>
</root>
</root>
5 changes: 4 additions & 1 deletion src/Uno.UI/Resources/Strings/en/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,13 @@
<data name="StorageProviderAndroidSafDisplayName" xml:space="preserve">
<value>Android Storage Access Framework</value>
</data>
<data name="StorageProviderIosSecurityScopedDisplayName" xml:space="preserve">
<value>iOS Security Scoped</value>
</data>
<data name="StorageProviderLocalDisplayName" xml:space="preserve">
<value>This PC</value>
</data>
<data name="StorageProviderNativeWasmDisplayName" xml:space="preserve">
<value>JS File Access API</value>
</data>
</root>
</root>
4 changes: 4 additions & 0 deletions src/Uno.UWP/Storage/Internal/StorageProviders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,9 @@ internal static class StorageProviders
#if __ANDROID__
public static StorageProvider AndroidSaf { get; } = new StorageProvider("androidsaf", "StorageProviderAndroidSafDisplayName");
#endif

#if __IOS__
public static StorageProvider IosSecurityScoped { get; } = new StorageProvider("iossecurityscoped", "StorageProviderIosSecurityScopedDisplayName");
#endif
}
}
4 changes: 3 additions & 1 deletion src/Uno.UWP/Storage/Pickers/FileOpenPicker.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ private async Task<FilePickerSelectedFilesArray> PickFilesAsync(bool multiple, C
return FilePickerSelectedFilesArray.Empty;
}

var files = nsUrls.Select(nsUrl => StorageFile.GetFromSecurityScopedUrl(nsUrl)).ToArray();
var files = nsUrls
.Where(url => url != null)
.Select(nsUrl => StorageFile.GetFromSecurityScopedUrl(nsUrl!, null)).ToArray();
return new FilePickerSelectedFilesArray(files);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Uno.UWP/Storage/Pickers/FolderPicker.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public partial class FolderPicker
return null;
}

return StorageFolder.GetFromSecurityScopedUrl(nsUrl);
return StorageFolder.GetFromSecurityScopedUrl(nsUrl, null);
}

private class FolderPickerDelegate : UIDocumentPickerDelegate
Expand Down
66 changes: 40 additions & 26 deletions src/Uno.UWP/Storage/StorageFile.iOS.cs
Original file line number Diff line number Diff line change
@@ -1,39 +1,45 @@
using System;
#nullable enable

using System;
using System.Threading;
using System.Threading.Tasks;
using Foundation;
using UIKit;
using Windows.Storage.FileProperties;
using Windows.Storage.Streams;
using Uno.Storage.Internal;
using System.IO;

namespace Windows.Storage
{
public partial class StorageFile
{
internal static StorageFile GetFromSecurityScopedUrl(NSUrl nsUrl) =>
new StorageFile(new SecurityScopedFile(nsUrl));
public partial class StorageFile
{
internal static StorageFile GetFromSecurityScopedUrl(NSUrl nsUrl, StorageFolder? parent) =>
new StorageFile(new SecurityScopedFile(nsUrl, parent));

internal class SecurityScopedFile : ImplementationBase
{
private readonly NSUrl _nsUrl;
private readonly StorageFolder? _parent;
private readonly UIDocument _document;
private DateTimeOffset? _dateCreated;

public SecurityScopedFile(NSUrl nsUrl)
public SecurityScopedFile(NSUrl nsUrl, StorageFolder? parent)
{
if (nsUrl is null)
{
throw new ArgumentNullException(nameof(nsUrl));
}

_nsUrl = nsUrl;
_parent = parent;
_document = new UIDocument(_nsUrl);
Path = _document.FileUrl?.Path ?? string.Empty;
}

public override StorageProvider Provider => new StorageProvider("iOSSecurityScopedUrl", "iOS Security Scoped URL");
public override StorageProvider Provider => StorageProviders.IosSecurityScoped;

public override DateTimeOffset DateCreated => throw new NotImplementedException();
public override DateTimeOffset DateCreated => _dateCreated ?? (_dateCreated = GetDateCreated()).Value;

public override async Task DeleteAsync(CancellationToken ct, StorageDeleteOption options)
{
Expand All @@ -42,31 +48,39 @@ public override async Task DeleteAsync(CancellationToken ct, StorageDeleteOption
using var coordinator = new NSFileCoordinator();
await coordinator.CoordinateAsync(new[] { intent }, new NSOperationQueue(), () =>
{
using (_nsUrl.BeginSecurityScopedAccess())
using var _ = _nsUrl.BeginSecurityScopedAccess();
NSError deleteError;
NSFileManager.DefaultManager.Remove(_nsUrl, out deleteError);
if (deleteError != null)
{
NSError deleteError;
if (options == StorageDeleteOption.Default)
{
NSFileManager.DefaultManager.TrashItem(_nsUrl, out var _, out deleteError);
}
else
{
NSFileManager.DefaultManager.Remove(_nsUrl, out deleteError);
}
if (deleteError != null)
{
throw new UnauthorizedAccessException($"Can't delete file. {deleteError}");
}
throw new UnauthorizedAccessException($"Can't delete file. {deleteError}");
}
});
}

public override Task<BasicProperties> GetBasicPropertiesAsync(CancellationToken ct) => throw new NotImplementedException();
public override Task<StorageFolder> GetParentAsync(CancellationToken ct) => throw new NotImplementedException();
public override Task<BasicProperties> GetBasicPropertiesAsync(CancellationToken ct)
{
using var _ = _nsUrl.BeginSecurityScopedAccess();
var fileInfo = new FileInfo(Path);
return Task.FromResult(new BasicProperties(0UL, fileInfo.LastWriteTimeUtc));
}

private DateTimeOffset GetDateCreated()
{
using var _ = _nsUrl.BeginSecurityScopedAccess();
return new FileInfo(Path).CreationTimeUtc;
}

public override Task<StorageFolder?> GetParentAsync(CancellationToken ct) => Task.FromResult(_parent);

public override Task<IRandomAccessStreamWithContentType> OpenAsync(CancellationToken ct, FileAccessMode accessMode, StorageOpenOptions options) => throw new NotImplementedException();

public override Task<StorageStreamTransaction> OpenTransactedWriteAsync(CancellationToken ct, StorageOpenOptions option) => throw new NotImplementedException();
protected override bool IsEqual(ImplementationBase implementation) => throw new NotImplementedException();

protected override bool IsEqual(ImplementationBase implementation) =>
implementation is SecurityScopedFile file && file._nsUrl.FilePathUrl.Path == _nsUrl.FilePathUrl.Path;
}
}
}
Loading

0 comments on commit 6ac3961

Please sign in to comment.