如何在Swift中获取文件引用URL?

时间:2016-10-02 23:43:20

标签: swift cocoa filesystems nsurl

我无法在Swift中使用文件引用URL。首先,Swift的URL类型甚至没有定义将文件路径URL转换为文件引用URL的方法。 NSURL有这样一种方法:fileReferenceURL() -> URL?,但我无法弄清楚如何让它发挥作用。

我尝试了什么:

let myURL = URL(string: "file:///Users/Me/Desktop/CoolFolder")!
let refURL = (myURL as NSURL).fileReferenceURL()!
NSLog("\(myURL)")   // file:///Users/Me/Desktop/CoolFolder (ok)
NSLog("\(refURL)")  // file:///Users/Me/Desktop/CoolFolder (wha?!?)
logFolderContents() // Logs contents of folder pointed to by refURL
// ...
// Rename CoolFolder -> UncoolFolder in Finder.
// ...
logFolderContents() // Fails!
// ...
// Rename UncoolFolder -> CoolFolder.
// ...
logFolderContents() // Successfully logs contents again.

基本上,refURL继续指向原始文件路径。对fileReferenceURL()的调用似乎什么也没做。

如何在Swift中获取文件引用URL?

更新 用户@Sam在评论中指出这似乎是一个Swift 3回归错误,如下所示:https://bugs.swift.org/browse/SR-2728。请访问该链接并对此错误进行投票。该线程还包含一个解决方法。

1 个答案:

答案 0 :(得分:1)

遗憾的是,截至 2021 年 4 月,该错误仍未修复。这使得比较文件 URL 的相等性变得非常困难。

使用 ObjC 效果很好:

NSString *p1 = @"/etc/hosts";
NSString *p2 = @"/private/etc/hosts";
    
NSURL *url1 = [NSURL fileURLWithPath:p1];
NSURL *url2 = [NSURL fileURLWithPath:p2];
    
NSURL *ref1 = url1.fileReferenceURL;
NSURL *ref2 = url2.fileReferenceURL;
    
BOOL equal = [ref1 isEqual:ref2];

在 Swift 中,我得到了几乎相同的结果(即包含卷 ID 和文件 ID 的 64 位):

let p1 = "/etc/hosts"
let p2 = "/private/etc/hosts"

let url1 = URL(fileURLWithPath: p1)
let url2 = URL(fileURLWithPath: p2)

// This does not work because the refs do not use fileIDs as they should
// See https://bugs.swift.org/browse/SR-2728
let ref1bad = (url1 as NSURL).fileReferenceURL()!
let ref2bad = (url2 as NSURL).fileReferenceURL()!
let equalBad = ref1bad == ref2bad
print("\(ref1bad) = \(ref2bad): \(equalBad)")

// This works:
var ref1: NSObject?
var ref2: NSObject?
if let resourceValues = try? url1.resourceValues(forKeys: [.fileResourceIdentifierKey]) {
    ref1 = resourceValues.fileResourceIdentifier as? NSObject
}
if let resourceValues = try? url2.resourceValues(forKeys: [.fileResourceIdentifierKey]) {
    ref2 = resourceValues.fileResourceIdentifier as? NSObject
}
let equal = ref1!.isEqual(ref2) // the use of "!" is probably not good here
print("\(String(describing: ref1)) = \(String(describing: ref2)): \(equal)")

如果你想要一个真正的文件引用 URL,你必须从对象中的两个 32 位值(它似乎是 NSData 类型)重建 URL,并使用第一个作为文件 ID 和第二个作为volumeID。不过,为此编写代码超出了我目前相当微薄的 Swift 技能。