3

(code examples in macruby syntax)

If I create an url with:

u1 = NSURL.fileURLWithPath('/tmp')

and then try to create an url with symlinks resolved:

u2=u1.URLByResolvingSymlinksInPath

I get back /tmp:

irb(main):010:0> u2.pathComponents
=> ["/", "tmp"]

This is a problem because if I create an NSDirectoryEnumerator with this NSURL (or /var or /etc), and prefetch the NSURLParentDirectoryURLKey, the NSURLs returned will have path /private/tmp/...

I need to get the components of the directory paths that are relative to the root of the NSDirectoryEnumerator. Is there a robust way to do this, other than hardcoded detection of the special cases I know about (which so far are /tmp, /var, and /etc)?

Is anyone aware of other "special" directories on which URLByResolvingSymlinksInPath doesn't work?

Michael Johnston
  • 5,298
  • 1
  • 29
  • 37

2 Answers2

3

I was never able to find a complete answer to this. For my case, I added a method to NSURL that handles /tmp, /var & /etc:

class NSURL
  def URLByResolvingSymlinksInPathIncludingPrivate
    url = self.URLByResolvingSymlinksInPath
    parts = url.pathComponents
    if (parts.first == '/') && ['tmp', 'var', 'etc'].include?(parts[1])
      url = NSURL.fileURLWithPathComponents(['/', 'private'] + parts[1..-1])
    end
    url
  end
end

I decided it was best to add an explicit method and not monkeypatch URLByResolvingSymlinksInPath.

Michael Johnston
  • 5,298
  • 1
  • 29
  • 37
  • 1
    You're not the only one to find this. In Final Cut Pro X, Apple themselves have a workaround method called "-[NSURL(symlinkAdditions) URLByReallyResolvingSymlinksInPath]" :-) – Chris Dolan May 09 '13 at 16:35
0

Matt Gallagher - Resolving a path containing a mixture of aliases and symlinks

StackOverflow - Resolving Finder alias files

My own solution only resolves symlinks, but not alias files.

extension URL {
    func resolveSymlinks() -> URL? {
        let fm = FileManager.default
        var url = self.resolvingSymlinksInPath()
        for _ in 0..<100 { // prevent infinite loop
            guard let target: String = try? fm.destinationOfSymbolicLink(atPath: url.path) else {
                return url
            }
            if target.hasPrefix("/") {
                url = URL(fileURLWithPath: target)
                continue
            }
            url = url.deletingLastPathComponent().appendingPathComponent(target)
        }
        return nil
    }
}
// URL(fileURLWithPath: "/tmp").resolveSymlinks()
// file:///private/tmp/
Community
  • 1
  • 1
neoneye
  • 50,398
  • 25
  • 166
  • 151