Friday, December 2, 2016

File Reference URLs Don’t Work in Swift 3

SR-2728:

In Swift 3.0 (i.e. Xcode 8.0 or Xcode 8.1 beta 1), a call to fileReferenceURL does not give a file reference URL anymore... this code

import Foundation
let string = "file:///Users/admin"
if let url = NSURL(string: string) {
  if let ref = url.fileReferenceURL() {
    print ("ref = (ref)")
  }
}

prints

ref = file:///Users/admin/

while it should have been printing

ref = file:///.file/id=6571367.437879/

as it did, correctly, in Swift 2.2 (and before) in Xcode 7.3.1 for example.

Via Charles Srstka:

.fileReferenceURL doesn’t exist on URL. It does exist on NSURL, but thanks to the bridging magic it returns a URL, which the ObjC-Swift bridge turns into a normal file path URL.

Frédéric Blondiau has some workarounds, but these are distasteful to me because they rely on the internal details of both NSURLFileResourceIdentifierKey and file reference URLs.

It seems like the proper short-term solution would be to write some Objective-C code to return an object that is not bridged to URL. This could probably be the actual NSURL typed as id. Or, if you’re going to use an opaque token anyway, another option would be to store bookmark data. I assume that would be slower, though.

Update (2018-09-08): Christian Tietze:

Swift 4.1 has a simpler mechanism to ensure you get a NSURL instead of a URL – the only type that supports file reference URLs as of September 2018, still.

if let fileRefURL = (url as NSURL).fileReferenceURL() as NSURL? { 
    print(fileRefURL)
}

Try that in the Swift 4.1 REPL to see that it works. Whew.

I do understand that there may be reasons to remove fileReferenceURL from URL and leave it on NSURL, but when you do invoke it on NSURL, I think it should at least return another NSURL object that works as expected instead of bridging to Swift’s URL struct that, for some reason, won’t work.

Update (2019-01-08): Jens Ayton:

No, wait! It actually crashes when you bridge the file reference NSURL to URL.

3 Comments RSS · Twitter

This issue came up on the mailing list today and Tony Parker posted this great explanation why the Foundation team decided to not support reference URLs for URL (a value type):

"Basically: if struct URL could be a reference, then almost all of its methods/functions needed to be marked as ‘throws’, including all of the path manipulation stuff, because all of them touch the file system and touching the file system has the possibility of introducing all kinds of errors. This made the API very cumbersome. In order to dramatically simplify the API, we decided that struct URL should not hold references. If references were still important, they should just be another type. For now, that other type is NSURL." (Source: https://lists.swift.org/pipermail/swift-corelibs-dev/Week-of-Mon-20161205/001052.html)

@Ole Thanks for the link. The problem is that you can’t actually use file references as NSURL from Swift.

@Michael: yeah, that is indeed a pretty big problem.

Leave a Comment