macosswiftcfurl-encoding

Getting alias path of file in swift


I'm having trouble resolving the alias link on mac. I'm checking if the file is an alias and then I would want to receive the original path. Instead I'm only getting a File-Id. Anly ideas?

func isFinderAlias(path:String) -> Bool? {

    var isAlias:Bool? = false // Initialize result var.

    // Create a CFURL instance for the given filesystem path.
    // This should never fail, because the existence isn't verified at this point.
    // Note: No need to call CFRelease(fUrl) later, because Swift auto-memory-manages CoreFoundation objects.
    print("path before \(path)");
    let fUrl = CFURLCreateWithFileSystemPath(nil, path, CFURLPathStyle.CFURLPOSIXPathStyle, false)
    print("path furl \(fUrl)");
    // Allocate void pointer - no need for initialization,
    // it will be assigned to by CFURLCopyResourcePropertyForKey() below.
    let ptrPropVal = UnsafeMutablePointer<Void>.alloc(1)

    // Call the CoreFoundation function that copies the desired information as
    // a CFBoolean to newly allocated memory that prt will point to on return.
    if CFURLCopyResourcePropertyForKey(fUrl, kCFURLIsAliasFileKey, ptrPropVal, nil) {

        // Extract the Bool value from the memory allocated.
        isAlias = UnsafePointer<CFBoolean>(ptrPropVal).memory as Bool


        // it will be assigned to by CFURLCopyResourcePropertyForKey() below.
        let ptrDarwin = UnsafeMutablePointer<DarwinBoolean>.alloc(1)

        if ((isAlias) == true){
            if let bookmark = CFURLCreateBookmarkDataFromFile(kCFAllocatorDefault, fUrl, nil){
                let url = CFURLCreateByResolvingBookmarkData(kCFAllocatorDefault, bookmark.takeRetainedValue(), CFURLBookmarkResolutionOptions.CFBookmarkResolutionWithoutMountingMask, nil, nil, ptrDarwin, nil)
                print("getting the path \(url)")
            }
        }

        // Since the CF*() call contains the word "Copy", WE are responsible
        // for destroying (freeing) the memory.
        ptrDarwin.destroy()
        ptrDarwin.dealloc(1)
        ptrPropVal.destroy()
    }

    // Deallocate the pointer
    ptrPropVal.dealloc(1)

    return isAlias
}

EDIT: Both Answers are correct! I would choose the answer of mklement0 due to the originally not stated requirement that the code run on 10.9 which makes it more flexible


Solution

  • vadian's answer works great on OS X 10.10+.

    Here's an implementation that also works on OS X 10.9:

    // OSX 10.9+
    // Resolves a Finder alias to its full target path.
    // If the given path is not a Finder alias, its *own* full path is returned.
    // If the input path doesn't exist or any other error occurs, nil is returned.
    func resolveFinderAlias(path: String) -> String? {
      let fUrl = NSURL(fileURLWithPath: path)
      var targetPath:String? = nil
      if (fUrl.fileReferenceURL() != nil) { // item exists
        do {
            // Get information about the file alias.
            // If the file is not an alias files, an exception is thrown
            // and execution continues in the catch clause.
            let data = try NSURL.bookmarkDataWithContentsOfURL(fUrl)
            // NSURLPathKey contains the target path.
            let rv = NSURL.resourceValuesForKeys([ NSURLPathKey ], fromBookmarkData: data) 
            targetPath = rv![NSURLPathKey] as! String?
        } catch {
            // We know that the input path exists, but treating it as an alias 
            // file failed, so we assume it's not an alias file and return its
            // *own* full path.
            targetPath = fUrl.path
        }
      }
      return targetPath
    }
    

    Note: