how to mount a network share in Swift

I've tried multiple variations of the below but nothing seem to work. The output is always

Error: sharePath blabla Not Valid


For some reason NetFSMountURLSync is not working in Swift 4.


import Cocoa

import NetFS

@NSApplicationMain

class AppDelegate: NSObject, NSApplicationDelegate {



var fileSystemProtocol: String = "smb"

var logServer: String = "smb://myservername"

var logShareName: String = "/Scripts"

var userName: String = ""

var password: String = ""



func applicationDidFinishLaunching(_ aNotification: Notification) {

mountShare(serverAddress: logServer, shareName: logShareName, userName: userName, password: password)

}


func applicationWillTerminate(_ aNotification: Notification) {

}


func mountShare( serverAddress: String, shareName: String, userName: String, password: String) {

let fm = FileManager.default

let mountPoint = "/Volumes/".appending(shareName)

var isDir : ObjCBool = false

if fm.fileExists(atPath: mountPoint, isDirectory:&isDir) {

if isDir.boolValue {

unmount(mountPoint, 0)

print("unmount \(mountPoint)")

}

}

let sharePath = NSURL(string: "\(serverAddress)\(shareName)")!

let mounted: Int32 = NetFSMountURLSync(sharePath, nil, nil, nil, nil, nil, nil)

if mounted > 0 {

print("Error: sharePath: \(sharePath) Not Valid")

} else {

print("Mounted: \(sharePath)")

}

}

}

Replies

Unless it's one of the multiple variations you've already tried, instead of:

  let mountPoint = "/Volumes/".appending(shareName)


Try:

  let mountPoint = "/Volumes/".stringByAppendingString(shareName)

a. In your example code, this line:


        let mountPoint = "/Volumes/".appending(shareName)// shareName is "/Scripts"


is going to produce "/Volumes//Scripts", which doesn't look like anything you'd want.


b. After getting an error from the mount:


        let mounted: Int32 = NetFSMountURLSync(sharePath, nil, nil, nil, nil, nil, nil)


why on earth would you not log the returned OSStatus code? That is presumably going to tell you what failed.

What is the context of this app? Is it running in the sandbox? If so, I doubt that you would have access to /Volumes.

Is this just a demo or proof-of-concept app? You would never, ever want to call NetFSMountURLSync. Call NetFSMountURLAsync instead.

Speaking of asyncronous operations, you should assume that unmount will take a non-trivial time to complete and will fail a significant part of the time.


This particular function is extremely delicate.

Sorry, I am new to Xcode and Swift. OSStatus returns 0. Interstingly, after I downloaded Xcode 8.3 and compiled, it ran just fine and mounted the share. So not sure why it's not running correctly under Xcode 9. I will take a closer look at NetFSMountURLAsync.

>> OSStatus returns 0.


According to the header file for this API (Command-click on "NetFSMountURLSync" in your source code; if a menu pops up, choose "Jump to Definition"; then scroll up a bit to read the comments):


* If the return value is zero the mount has succeeded.
*
* A positive non-zero return value represents an errno value
* (see /usr/include/sys/errno.h).  For instance, a missing mountpoint
* error will be returned as ENOENT (2).
*
* A negative non-zero return value represents an OSStatus error.


So your "mount > 0" test seems to be wrong.

Oops, I mean, either there was no error, so your claim that it always prints "Error: sharePath blabla Not Valid" is wrong, or there was an error, so your claim that the result was 0 is wrong.


Either way, the "mounted > 0" ought to be "mounted != 0", right?

Just a word to the wise. If you ask for help on an internet forum, and someone asks you really, really specific questions, it is nice to at least try to answer them.


At this point, all I can say is that you are trying to execute a method that is particularly noteable for its complexity and deep hooks into different parts of the operating system. You tried the demo, sample-code version and it returned a zero result. Then you tried it again under some different configuration that at least consists of a new development toolchain. Chances are, there were more changes than just upgrading Xcode. I'm guessing you did an OS upgrade too. Does this function even work at all in the new OS? I can tell you from experience that this function has a history of breaking with OS updates. Can you even perform this mount manually in the Finder and from the command line? That is an important plot point. But, of couse, I'm just guessing, because guesses are all I have.

Lots of folks have posted lots of good suggestions here, but I wanted to add a few other things:

  • NetFSMountURLSync
    takes a URL. I strongly recommend against constructing URLs by hand, but rather you should construct them using
    URLComponents
    . For example:
    var uc = URLComponents()
    uc.scheme = "smb"
    uc.host = "fluffy.local"
    uc.path = "/Fluffy Storage"
    let url = uc.url!
    print(url)  // -> smb://fluffy.local/Fluffy%20Storage

    This takes care of a bunch of fiddly details, most notably percent encoding.

  • Once you have a

    URL
    you can pass it to
    NetFSMountURLSync
    (which takes a
    CFURL!
    ) by converting it to an
    NSURL
    .
    let result = NetFSMountURLSync(url as NSURL, …)

    .

  • Unless you’re doing something weird, it is much better to let NetFS choose the mount point (by passing

    nil
    to the
    mountpath
    parameter) rather than trying to construct your own mount point. Doing the latter is an exercise in much complexity.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Unless you’re doing something weird, it is much better to let NetFS choose the mount point (by passing

nil
to the
mountpath
parameter) rather than trying to construct your own mount point. Doing the latter is an exercise in much complexity.



Unfortunately this isn't the case in a Sandboxed app - a nil mount point will cause the mount to fail. Is there a way around this?

Calling that function from the sandbox is really weird. It might still work since I used it, but you have to jump through some hoops to make it useable.

a nil mount point will cause the mount to fail. Is there a way around this?

Have you tried passing in the path of a directory within your app’s container?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Yes - this is how we mount the shares at the moment, but it's a pretty terrible user experience if they expect it to be mounted in /Volumes.


I find it strange that Sandboxed apps cannot mount into /Volumes. NetFS already handles the creation of the mount point in /Volumes for a non-sandboxed app (abstracting having to sudo up and create a mount point). Why should a sandboxed app be any different?


At the moment it's actually less secure, as a Sandboxed app by default can access any file within a mounted share inside its container, without requesting access from the user. Surely it would be best to stick to the standards macOS has upheld for years, and simply mount into /Volumes.

Yes - this is how we mount the shares at the moment …

Cool. It’s good to know that I’m not totally off in the weeds.

I find it strange that Sandboxed apps cannot mount into /Volumes.

I tend to agree with you. My recommendation is that you make your case with the App Sandbox team by filing an enhancement request explaining your use case.

Please post your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks Quinn!


Bug number here: 45795638

Unfortunately Apple will not be resolving this problem, so I guess it will remain to be an issue.