3

How can I check if a file is an Alias on Mac? Here is my code so far:

public func getFiles(){
    let folderPath = "/Users/timeBro/Desktop/testfolder"
    let fileManager = NSFileManager.defaultManager()
    let enumerator:NSDirectoryEnumerator = fileManager.enumeratorAtPath(folderPath)!

    for url in enumerator.allObjects {

        let newurl = NSURL(string: url as! String)
        print("\(url as! String)")
        print(url);
        print(newurl?.isFileReferenceURL())
    }
}

How can I check if the file is and alias?

Silve2611
  • 2,198
  • 2
  • 34
  • 55

2 Answers2

3

There is an easy solution which completely gets by without any pointer handling:

extension URL {
    func isAlias() -> Bool? {
        let values = try? url.resourceValues(forKeys: [.isSymbolicLinkKey, .isAliasFileKey])
        let alias = values?.isAliasFile
        let symbolic = values?.isSymbolicLink

        guard alias != nil, symbolic != nil else { return nil }
        if alias! && !symbolic! {
            return true
        }
        return false
    }
}

Explanation: resourceValues(forKeys:) returns .isAliasFile and .isSymbolicLink for symbolic links so you have to make sure the former is returned and the latter isn’t when checking for aliases. If the path doesn’t exist the function returns nil.

Tom E
  • 1,530
  • 9
  • 17
2

Update: I initially mistakenly assumed that the only option is to use a CoreFoundation (C API) method, but that's not actually true (thanks): the Foundation (ObjC API) class NSURL does provide a way to detect Finder aliases:

// OSX 10.9+
// Indicates if the specified filesystem path is a Finder alias.
// Returns an optional Boolean: if the lookup failed, such when the path doesn't exist,
// nil is returned.
// Example: isFinderAlias("/path/to/an/alias")
func isFinderAlias(path:String) -> Bool? {
    let aliasUrl = NSURL(fileURLWithPath: path)
    var isAlias:AnyObject? = nil
    do {
        try aliasUrl.getResourceValue(&isAlias, forKey: NSURLIsAliasFileKey)
    } catch _ {}
    return isAlias as! Bool?
}

[Not recommended, except as an exercise in using UnsafeMutablePointer<Void>]
Here's how to do it with the C-based CoreFoundation API:

  • IsAliasFile() was deprecated in OS X 10.4,
  • succeeded by FSIsAliasFile(), which was deprecated in 10.8.
  • The current method is to use CFURLCopyResourcePropertyForKey(), which isn't fun to deal with in Swift, due to having to use manual memory management with UnsafeMutablePointer<Void>.

I hope I got the memory management right:

import Foundation

// Indicates if the specified filesystem path is a Finder alias.
// Returns an optional Boolean: if the lookup failed, such when the path
// doesn't exist, nil is returned.
// Example: isFinderAlias("/path/to/an/alias")
func isFinderAlias(path:String) -> Bool? {

    var isAlias:Bool? = nil // 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.
    let fUrl = CFURLCreateWithFileSystemPath(nil, path, CFURLPathStyle.CFURLPOSIXPathStyle, false)

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

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

    // Deallocate the pointer
    ptrPropVal.dealloc(1)

    return isAlias
}
Community
  • 1
  • 1
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Nice solution. Before I post another question. How would I get the filepath of the document the alias is pointing to? – Silve2611 Oct 26 '15 at 10:29
  • @Silve2611: That's even more involved, so I suggest you ask a new question; the documentation says "First use CFURLCreateBookmarkDataFromFile, then use CFURLCreateByResolvingBookmarkData." Feel free to notify me here once you've asked the new question, if you still need help. – mklement0 Oct 26 '15 at 14:36
  • Ok I will. I also did a great part of it already but I only get the Fileid not the path. – Silve2611 Oct 26 '15 at 14:43
  • I added the new question http://stackoverflow.com/questions/33348653/getting-alias-path-of-file-in-swift – Silve2611 Oct 26 '15 at 14:48
  • @Silve2611: While my original answer worked, I've led you down the wrong path: there _is_ a more convenient, Foundation-based way of doing this, using `NSURL` - please see my update. – mklement0 Oct 26 '15 at 22:13