Search code examples
macosswiftnsfilemanager

Check if file is alias Swift


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 an alias?


Solution

  • 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
    }