I am trying to port a method to mount volumes asynchronous, from Objective C to Swift. but i run into some trouble with some lines of code. I hope somebody can give me an answer.
The problems are : kNAUIOptionKey, kNAUIOptionNoUI and kNetFSUseGuestKey: unresolved identifiers. I can't find an equivalent in Swift.
kCFBooleanTrue : CFBoolean is not convertible to UnsafePointer.
Code between /* and */ is hard to convert to Swift and that's where some help would be welcome.
import Foundation
import Cocoa
import NetFS
func mountVolumeAsync(#username:String, #password:String, #ipadres:String, #proto:String, #mountpoint:String) -> Bool{
var theURLPath: NSURL
if username.lowercaseString == "guest" {
println("Volume will be mounted as Guest user.....")
let thestring = "\(proto)://\(ipadres)/\(mountpoint)"
let theURLPath = NSURL(string:thestring)
if theURLPath == nil {
println("Path to file is invalid.")
return false
}
} else {
let thestring = "\(proto)://\(username):\(password)@\(ipadres)/\(mountpoint)"
let theURLPath = NSURL( string:thestring)
if theURLPath == nil {
println("Path to file is invalid.")
return false
}
}
var mount_options = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, nil, nil);
if (mount_options != nil) {
CFDictionarySetValue(mount_options, kNAUIOptionKey, kNAUIOptionNoUI);
if username.lowercaseString == "guest" {
CFDictionarySetValue( mount_options, kNetFSUseGuestKey, kCFBooleanTrue);
}
}
var status = false
/* ---- Objective C Code to port to Swift Code ------
AsyncRequestID requestID = NULL;
dispatch_queue_t queue =dispatch_get_main_queue();
NetFSMountURLAsync(
CFBridgingRetain(theURLPath),
NULL,
(__bridge CFStringRef) username,
(__bridge CFStringRef) passwd,
mount_options,
NULL,
&requestID,
queue,
^(int status, AsyncRequestID requestID, CFArrayRef mountpoints) {
NSLog(@"mounted: %d - %@", status, (__bridge NSArray *) mountpoints);
});
if (!status) {
NSString *msg = [NSString stringWithFormat:@"[%@] is not mounted! Check your params",mntpt];
return NO;
}
*/
return status
}
I am still trying to solve this problem, but unfortunately I do not succeed. Nobody answered this question and I am still looking for an answer. I changed the code to the following and it compiles, but does not run. While debugging i could find the following error :
Printing description of open_options:
(CFMutableDictionary!) open_options = 1 key/value pair {
[0] =
<Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0x4f4955414e80).
The process has been returned to the state before expression evaluation.>
I found this info in the NetFS framework:
- The following dictionary keys for open_options are supported:
- kNetFSUseGuestKey: Login as a guest user.
- kNetFSAllowLoopbackKey Allow a loopback mount.
kNAUIOptionKey = UIOption Suppress authentication dialog UI. *
The following dictionary keys for mount_options are supported:
- kNetFSMountFlagsKey = MNT_DONTBROWSE No browsable data here (see ).
- kNetFSMountFlagsKey = MNT_RDONLY A read-only mount (see ).
- kNetFSAllowSubMountsKey = true Allow a mount from a dir beneath the share point.
- kNetFSSoftMountKey = true Mount with "soft" failure semantics.
- kNetFSMountAtMountDirKey = true Mount on the specified mountpath instead of below it.
- Note that if kNetFSSoftMountKey isn't set, then it's set to TRUE.
I hope somebody can help.
func mountVolumeAsync(#username:String, #password:String, #ipadres:String, #proto:String, #mountpoint:String) -> Bool{
var theURLPath: NSURL? = nil
if username.lowercaseString == "guest" {
// "Volume will be mounted as Guest user....."
let thestring = StringWithFormat("%@://%@/%@",proto,ipadres,mountpoint)
let theURLPath = NSURL(string:thestring)
if theURLPath == nil {
println("Path to file is invalid.")
return false
}
} else {
let thestring = StringWithFormat("%@://%@:%@@%@/%@",proto,username,password,ipadres,mountpoint)
let theURLPath = NSURL(string:thestring)
if theURLPath == nil {
println("Path to file is invalid.")
return false
}
}
var mount_options = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, nil, nil);
var open_options = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, nil, nil);
if open_options != nil {
CFDictionarySetValue(open_options,"kNAUIOptionKey","NoUI")
}
if (mount_options != nil) {
//CFDictionarySetValue(mount_options,kNAUIOptionKey"UIOption")
if username.lowercaseString == "guest" {
let kNetFSUseGuestKey = "Guest"
//CFDictionarySetValue( mount_options, kNetFSUseGuestKey, kCFBooleanTrue);
CFDictionarySetValue(open_options,kNetFSUseGuestKey, "kCFBooleanTrue")
}
}
var status = false
var requestID: AsyncRequestID = nil
let queue = dispatch_get_main_queue()
NetFSMountURLAsync(
theURLPath,
nil,
username as NSString,
password as NSString,
mount_options,
open_options,
&requestID,
queue)
{(stat:Int32, requestID:AsyncRequestID, mountpoints:CFArray!) -> Void in
println("mounted: \(stat) - \(mountpoints)")
}
if status == false {
let msg = "[\(mountpoint) is not mounted! Check your params"
println(msg)
return false
}
}
Your Swift code crashes because passing a Swift string to a function that accepts an
UnsafePointer, such asCFDictionarySetValue, will pass it a pointer to the string character data encoded as UTF-8, not the string object. Additionally, the character data is valid only until the end of the function call, after which it is freed.The symbols you are looking for do not exist in Swift because they depend on macro expansion. Swift supports
#definefor dumb tokens (like#define FOO 1), but not for macros likeCFSTR. Any such key that you wish to use needs to be copied over to the Swift side. To find their value, use them in a C/Objective-C file and command-click them to go to their definition. You'll find that:kCFBooleanTrueis missing for the same reason (though it doesn't useCFSTR).You have two options to bring the
CFSTRstrings to your Swift code. The first and simplest is just to declare the values as Swift variables:The second option is to expose the values (with a different name to avoid clashing on the C side of things) through your bridging header:
Then have something like that at the top level of an Objective-C file:
NSStringandCFStringRefare the same thing under the hood. It is documented behavior that casting from one to the other is fine (it's called toll-free bridging).This solution is probably the most forward-compatible because it doesn't require you to know what the defined values expand to. Apple could theoretically change the string values between SDK versions, so the Swift version could break in the future (though compiled binaries will keep working, and compiling against the same SDK will keep working for as long as Apple supports it, so it's not a big deal).
kCFBooleanTrueis the same asNSNumber(bool: true), because of toll-free bridging again.Similarly,
NSDictionaryis toll-free bridged toCFDictionaryRef, so I encourage you to use that instead of the CFDictionary API, which is a pain to use from Swift because of all the void pointers. This is especially attractive in Swift, because the compiler recognizes thatNSDictionary*andCFDictionaryRefare the same thing.So that would be:
I'm afraid that you'll be on your own for NetFS itself, but this should pretty much solve your dictionary woes. You obviously don't need a separate function to create the dictionaries, I did it because it was simpler for me than copying your code.
Finally, you are encouraged to create short, self-contained and correct examples of what you're having issues with. Not a lot of people know about NetFS, but it seems that you had issues with Swift and Core Foundation types more than NetFS. With a good SSCCE, you could have reduced your code to these three lines:
A lot of people on here know what's wrong with this and you'd probably have had your answer within minutes rather than three months later.