在Swift中异步挂载卷

时间:2014-10-12 15:50:04

标签: swift netfs

我正在尝试移植一个方法来挂载异步的卷,从Objective C到Swift。 但是我遇到了一些代码问题。我希望有人能给我一个答案。

问题是: kNAUIOptionKey,kNAUIOptionNoUI和kNetFSUseGuestKey:未解析的标识符。我找不到Swift中的等价物。

kCFBooleanTrue:CFBoolean不能转换为UnsafePointer。

/ *和* /之间的代码很难转换为Swift,而且欢迎提供一些帮助。

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

}

我仍在努力解决这个问题,但不幸的是我没有成功。 没有人回答这个问题,我仍在寻找答案。 我将代码更改为以下内容并进行编译,但不会运行。 在调试时我会发现以下错误:

打印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.>

我在NetFS框架中找到了这个信息:

  • 支持以下用于open_options的字典键:
  • kNetFSUseGuestKey:以访客用户身份登录。
  • kNetFSAllowLoopbackKey允许环回安装。
  • kNAUIOptionKey = UIOption抑制身份验证对话框UI。 *

  • 支持以下mount_options字典键:

  • kNetFSMountFlagsKey = MNT_DONTBROWSE此处没有可浏览数据(请参阅参考资料)。
  • kNetFSMountFlagsKey = MNT_RDONLY只读装载(请参阅参考资料)。
  • kNetFSAllowSubMountsKey = true允许从共享点下方的目录安装。
  • kNetFSSoftMountKey = true使用“软”故障语义挂载。
  • kNetFSMountAtMountDirKey = true挂载在指定的mountpath而不是它下面。
  • 请注意,如果未设置kNetFSSoftMountKey,则将其设置为TRUE。

我希望有人可以提供帮助。

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
}
}

2 个答案:

答案 0 :(得分:1)

你的Swift代码崩溃是因为将一个Swift字符串传递给一个接受UnsafePointer的函数,例如CFDictionarySetValue,会向它传递一个指向编码为UTF-8的字符串字符数据的指针,而不是字符串对象。此外,字符数据仅在函数调用结束之前有效,之后将被释放。

您正在寻找的符号在Swift中不存在,因为它们依赖于宏扩展。 Swift支持#define用于愚蠢的令牌(如#define FOO 1),但不支持像CFSTR这样的宏。您希望使用的任何此类密钥都需要复制到Swift端。要查找它们的值,请在C / Objective-C文件中使用它们并按住Command键单击它们以转到它们的定义。你会发现:

#define kNAUIOptionKey CFSTR("UIOption")
#define kNAUIOptionNoUI CFSTR("NoUI")
#define kNetFSUseGuestKey CFSTR("Guest")

kCFBooleanTrue由于同样的原因而丢失(尽管它不使用CFSTR)。

您有两个选项可以将CFSTR字符串添加到Swift代码中。第一个也是最简单的就是将值声明为Swift变量:

let kNAUIOptionKey = "UIOption"
let kNAUIOptionNoUI = "NoUI"
let kNetFSUseGuestKey = "Guest"

第二个选项是通过桥接标题公开值(使用不同的名称以避免在C方面发生冲突):

NSString* NAUIOptionKey;
NSString* NAUIOptionNoUI;
NSString* NetFSUseGuestKey;

然后在Objective-C文件的顶层有这样的东西:

#include <NetFS/NetFS.h>
NSString* NAUIOptionKey = (NSString*)kNAUIOptionKey;
NSString* NAUIOptionNoNui = (NSString*)kNAUIOptionNoUI;
NSString* NetFSUseGuestKey = (NSString*)kNetFSUseGuestKey;

NSStringCFStringRef是同样的事情。记录的行为是从一个到另一个很好(它被称为免费桥接)。

此解决方案可能是最向前兼容的,因为它不需要您知道定义的值扩展到什么。 Apple理论上可以改变SDK版本之间的字符串值,因此Swift版本可能会在未来中断(虽然编译后的二进制文件将继续工作,并且只要Apple支持它,针对相同SDK的编译将继续工作,因此它不是很大交易)。

kCFBooleanTrueNSNumber(bool: true)相同,因为再次进行免费桥接。

同样地,NSDictionary是免费的桥接到CFDictionaryRef,所以我鼓励你使用它而不是CFDictionary API,因为所有的void指针都很难使用Swift。这在Swift中特别有吸引力,因为编译器认识到NSDictionary*CFDictionaryRef相同的东西

那就是:

import Foundation

let kNAUIOptionKey = "UIOption"
let kNAUIOptionNoUI = "NoUI"
let kNetFSUseGuestKey = "Guest"

func exampleCFDictionary() -> CFDictionaryRef {
    var dict = NSMutableDictionary()
    dict[kNAUIOptionKey] = kNAUIOptionNoUI
    dict[kNetFSUseGuestKey] = NSNumber(bool: true)
    return dict
}

println(exampleCFDictionary())

我担心你会自己为NetFS本身,但这应该可以解决你的字典问题。你显然不需要单独的函数来创建字典,我这样做是因为它比复制你的代码更简单。

最后,我们鼓励您创建short, self-contained and correct examples您遇到的问题。很多人都不了解NetFS,但是看起来你的Swift和Core Foundation类型比NetFS更多。有了一个好的SSCCE,你可以将代码减少到这三行:

var openOptions = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nil, nil)
CFDictionarySetValue(openOptions, "kNAUIOptionKey", "NoUI")
println(openOptions)

很多人在这里知道这有什么问题,你可能会在几分钟内而不是三个月后得到答案。

答案 1 :(得分:0)

我做了一些修改,并使其与Xcode 7 beta 5和Swift 2.0一致。 Async比Sync版本更好,因为它不会阻塞线程。我不得不桥接任何Objective-C参数。常量完美地让它们独自存在,当我记录它们的名字时,它们会达到正确的值。这消除了上述一些额外的工作。但代码确实给了我一个很好的起点,所以我很感谢这两个条目。

我确实使用过CFMutableDictionary和NSMutableDictionary。 CFMutableRef不起作用。此类型最近可能在方法中已更改或被误读。开放和坐骑选项也是倒退的。

一些观察:只有afp://支持密码更改。密码更改标志也不起作用

public var kNetFSChangePasswordKey: String { get }

当用作打开选项时会导致无限循环。也许我错了。如果有人能够得到那面旗帜,那将会很好。我尝试在开放选项字典中设置它并将其设置为true,除了无休止地旋转之外没有太多运气。也许这是API中的一个错误,并没有人将其覆盖。

作为一种解决方法,当我希望用户更改密码时,我有一个密码更改的复选框,然后我通过afp://连接密码以及作为空白“”发送的密码,这允许用户在登录时手动更改密码,如果用户被迫通过服务器更改密码,它将起作用。在smb3下,用户没有该选项,它将跳过来自服务器的任何强制密码授权。然后,下次用户登录更改密码标志时,它将通过SMB3连接。如果您想知道为什么使用SMB3,因为它明显快于Apple文件协议。微软终于做对了。去搞清楚。

此外,afp和smb上的服务器与URL上的.local扩展名连接得更快。如果您尝试afp://没有它,您可能会遇到无限循环(可能是DNS问题)。但是。通过SMB连接时,.local也更快。虽然SMB将在没有.local扩展名的情况下连接,但它总是与.local附加即时连接。我不是通过互联网连接,但是当Google Fiber在我们的商店时,我们可能会尝试使用它。

func openOptionsDict() -> CFMutableDictionary {
    let dict = NSMutableDictionary()
    dict[kNAUIOptionKey] = kNAUIOptionAllowUI
    dict[kNetFSUseGuestKey] = false
    return dict
}

func mountOptionsDict() -> CFMutableDictionary {
    let dict = NSMutableDictionary()
    return dict
}

func asyncMountShare(serverAddress: String, shareName: String, userName: String, password: String) {
    let fm = NSFileManager.defaultManager()
    let mountPoint = "/Volumes/".stringByAppendingString(shareName)
    var isDir : ObjCBool = false
    if fm.fileExistsAtPath(mountPoint, isDirectory:&isDir) {
        if isDir {
            unmount(mountPoint, 0)
            print("unmount \(mountPoint)")
        }
    }

    let escapedAddress = serverAddress.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())
    let shareAddress = NSURL(string: escapedAddress!)!

    let openOptions : CFMutableDictionary = openOptionsDict()
    let mount_options : CFMutableDictionary = mountOptionsDict()

    var requestID: AsyncRequestID = nil
    let queue = dispatch_get_main_queue()

    NetFSMountURLAsync(shareAddress, nil, userName as NSString, password as NSString, openOptions, mount_options, &requestID, queue)
        {(stat:Int32,  requestID:AsyncRequestID,  mountpoints:CFArray!) -> Void in
            print("msg: \(stat) mountpoint: \(mountpoints)")
    }
}