Creating Virtual File System in .NET for macOS

This article describes specific steps required to implement the IT Hit User File System on macOS platform. You can find how to implement the cross-platform Engine interfaces in the Creating Virtual File System in .NET article.

On macOS your solution typically consists of at least two projects: host (or container) application and file provider extension project. Your Engine is running in the extension project and it can not have any user interface. While your host application installs and uninstalls extension and can have UI.

Registering File System

To inform the macOS platform about a new virtual file system and to receive file system API calls you must register a new file system (add a file provider extension domain in terms of Apple macOS file provider API). You will typically do this inside your host application during your application first start.

Create the NSFileProviderDomain instance and pass it to the NSFileProviderManager.AddDomainAsync() method:

var domain = new NSFileProviderDomain("com.userfilesystem.vfs.app", "ITHitFS");
await NSFileProviderManager.AddDomainAsync(domain);

You will do registration one time on first app start. To check if your file provider extension was already registered you can list the registered domains using the NSFileProviderManager.GetDomainsAsync() method:

var domains = await NSFileProviderManager.GetDomainsAsync();
foreach (NSFileProviderDomain domain in domains)
{
    if (domain.Identifier == extensionIdentifier)
    {
        // File provider extension already registered.
    }
}

Unregistering File System

To unregister file system use the NSFileProviderManager.RemoveDomainAsync() method:

var domain = new NSFileProviderDomain(extensionIdentifier, displayName);
await NSFileProviderManager.RemoveDomainAsync(domain);

Implementing Engine

On macOS your Engine runs inside your file provider extension. It starts when your file provider extension is loaded and stops when it is unloaded.

In your Engine constructor you must set the remote storage item ID for your root folder:  

[Register(nameof(VirtualEngine))]
public class VirtualEngine : EngineMac
{
    private WebDavSession webDavSession;

    [Export("initWithDomain:")]
    public VirtualEngine(NSFileProviderDomain domain)
        : base(domain)
    {
        License = AppGroupSettings.GetLicense();

        // Set remote root storage item id.
        SetRemoteStorageRootItemId(GetRootStorageItemIdAsync().Result);
    }

    /// <inheritdoc/>
    public override async Task<IFileSystemItem> GetFileSystemItemAsync(
        string userFileSystemPath, 
        FileSystemItemType itemType, 
        byte[] remoteStorageItemId = null, 
        ILogger logger = null)
    {
        ...
    }
...
}

Note that macOS does not provide file path in user file system, you can not map it to remote storage path, like on Windows. The only way to reference your remote storage items is via remote storage ID. 

In your info.plist file specify the Engine class name in NSExtensionPrincipalClass

...
<plist version="1.0">
<dict>
...
	<key>NSExtension</key>
	<dict>
		<key>NSExtensionPointIdentifier</key>
		<string>com.apple.fileprovider-nonui</string>
		<key>NSExtensionPrincipalClass</key>
		<string>VirtualEngine</string>
		<key>NSExtensionFileProviderSupportsEnumeration</key>
		<true/>
	</dict>
</dict>
</plist>

 

Group ID and App Identifies

In the following steps, we will describe how to configure bundle identifier and group ID for your host app and file provider extension projects in the development environment 

  1. Bundle identifier. 

    • Container project.
      Edit the Info.plist file.
      CFBundleIdentifier set to something like com.userfilesystem.vfs.app. Choose any value you like.
    • Extension project. Edit the Info.plist file.
      CFBundleIdentifier set to something like com.userfilesystem.vfs.app.extension. Choose any value you like not overlapping with container project bundle identifier.

     

  2. Group ID.  Group ID value must be the same for every following update.


    • Container project. Edit the Entitlements.plist file.
      com.apple.security.application-groups key set to something like 65S3A9JQ36.group.com.userfilesystem.vfs.
    • Extension project.
      • Edit the Info.plist file.
        NSExtensionFileProviderDocumentGroup field set to same 65S3A9JQ36.group.com.userfilesystem.vfs.
      • Edit the Entitlements.plist file.
        com.apple.security.application-groups key set to same 65S3A9JQ36.group.com.userfilesystem.vfs.

Next Article:

Creating Custom macOS Finder Context Menu