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.