We have Xamarin.iOS
application that declares a custom file extension - *.msoisalesbak
as exported type declaration
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeIdentifier</key>
<string>com.ewsgroup.msoisalesbak</string>
<key>UTTypeConformsTo</key>
<array>
<string>com.pkware.zip-archive</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<string>msoisalesbak</string>
</dict>
<key>UTTypeDescription</key>
<string>MSO&I Sales application backup file</string>
</dict>
</array>
This means, that whenever iOS system detects a file with this extension, it will provide a possibility to open this file in our application.
Further, in our AppDelegate
we have a code like this to handle the delegating file share:
public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{
..................
var fileHandlerService = ServiceLocator.Container.Resolve<IAssociatedFileHandlerService>();
if(fileHandlerService.CanRestoreDatabaseFromOpenedAssociatedBackupFile(url))
{
...................
string filePath = url.Path;
var destinationPath = "PATH_TO_APP_INTERNAL_FOLDER"
using(FileStream sourceStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
using(FileStream destinationStream = File.Create(destinationPath))
{
sourceStream.CopyTo(destinationStream);
}
....................
return true;
}
return false;
}
We basically double check that the file is valid and something we can handle, and then try to copy the file to internal storage and do some additional processing, which is not related to this issue.
Now, we create the custom file that our application supports and put it into iCloud Downloads folder. When we click on that file, our application is successfully opened and OpenUrl
delegate is successfully invoked. However, doing so we receive the following exception
System.IO.DirectoryNotFoundException: Could not find a part of the path "/private/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/Downloads/637569517947945130-0.12.1.msoisalesbak".
at System.IO.FileStream..ctor (System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, System.Int32 bufferSize, System.Boolean anonymous, System.IO.FileOptions options) [0x00177]
Which means, for some reason the path that the OpenUrl
received, is inaccessible for our application.
The fact is that OpenUrl
works just fine if we have the custom file in an email, or we open the custom file from other applications like a chat app (Microsoft Teams as an example). However, it doesn't work from iCloud.
Is there any special care we should take, to allow access to iCloud folder? What is the thing that we miss?
Before you "unlock" the file from iCloud, you need to call startAccessingSecurityScopedResource
to makes the resource pointed to by a security-scoped URL available to the app. And then don't forget to call the stopAccessingSecurityScopedResource
.
You can also create a new boolean key LSSupportsOpeningDocumentsInPlace
in the Info.plist file. If set it to true, the url will point to the file in iCloud but it hasn't be downloaded. So you can't access the file. If set it to false, it will makes a copy of the file in the Application Sandbox.