Swift Network Shares

20 May 2017 ∞

There is no good way to interact with network file systems in Swift without a third-party library. This leaves us needing to mount a share in order to use normal FileManager and various .write(…) methods. Unfortunately leaving the task of mounting a server to the User (and thus, the Finder) has a few problems.

The main hurdle is that the Finder doesn't mound servers consistently. Behind the scenes directories are created at /Volumes and the shares mounted to them. While they show up as expected to the user they don't behave deterministically as far as UNIX paths are concerned. A network share I was testing left behind dozens of hidden folders with no good way to determine where the drive was actually mounted.

Options

There are a few ways of programmatically mounting a network share.

AppleScript

The easiest option to mount a network share is to spin up an AppleScript object and run mount afp://someshare. While this works it does exactly what the Finder does, so it's a no-go.

Shell

Ok, AppleScript is clunky anyway. That's what Process is for. Using mount we can mount any file system anywhere we want! Sadly this was spitting out errors saying my destination path didn't exist. I could copy the exact command into the Terminal and it would work. Moving right along…

NetFS

Finally, there is NetFS. This has almost no documentation outside of some header files buried in system directories. Fortunately it doesn't require dropping down to Objective-C and it actually works.

There isn't a reference page for any of the NetFS methods, but if there was this is what one of them would look like. A network drive can be mounted either synchronously or asynchronously. I've documented the synchronous method, but the async version is similar.

NetFSMountURLSync(_:_:_:_:_:_:_)

Mounts a network file share at the specified file system node

Declaration

NetFSMountURLSync(
    _ url: CFURL!,
    _ mountpath: CFURL!,
    _ user: CFString!,
    _ passwd: CFString!,
    _ open_options: CFMutableDictionary!,
    _ mount_options: CFMutableDictionary!,
    _ mountpoints: UnsafeMutablePointer<Unmanaged<CFArray>?>!
    ) -> Int32

Parameters

url URL to mount, e.g. nfs://server/path

mountpath Path for the mountpoint

user Auth user name (overrides URL)

passwd Auth password (overrides URL)

open_options Options for session open (see below)

mount_options Options for mounting (see below)

mountpoints Array of mountpoints

Return Value

Returns 0 if successful, or an error code from /usr/include/sys/errno.h

Discussion

Most of the options are fairly self-explanatory. You can pass nil for any parameters you don't need. In fact, only the server URL is required.

If the server requires authentication and a username or password aren't supplied the standard system prompt will open.

Open Options

Information from the mount header file /usr/include/sys/mount.h

The following dictionary keys for open_options are supported:

kNetFSUseGuestKey: Bool Login as a guest user.

kNetFSAllowLoopbackKey: Bool Allow a loopback mount.

kNAUIOptionKey If this key is set to UIOption: Suppress authentication dialog UI.

Mount Options

There are a number of options that can be passed in to mount_options as an NSMutableDictionary.

Information from the NetFS header file /System/Library/Frameworks/NetFS.framework/Headers/NetFS.h

kNetFSMountFlagsKey There are two options for this key: - MNT_DONTBROWSE the share won't be visible to the user as a drive. - MNT_RDONLY the share will be mounted as read-only.

kNetFSAllowSubMountsKey Allow a mount from a dir beneath the share point. If this key is true a subdirectory of the share will be mounted directly instead of mounting the root the the share.

kNetFSSoftMountKey: Bool Mount with "soft" failure semantics. If this key isn't specified it defaults to true

Network errors, e.g. timeouts, will be retried for a much shorter amount of time. If the network errors persist, then the mount will be force unmounted.

kNetFSMountAtMountDirKey: Bool Mount on the specified mountpath instead of below it.

Return Codes

Information from the error number header file /usr/include/sys/errno.h

The header file contains far more error numbers than NetFS returns, so you'll just have to cross reference.