Accessing Finder aliases in your own code: a walk through alisma’s source

On Wednesday, I posted a new command tool alisma, which can create and resolve Finder aliases at the command line. It comes with its source code, and to help anyone wanting to access Finder aliases in their own Swift code, this article explains how it works.

The code at the start and end of the source concerns itself with handling the command options and the interface of the tool. The two functions which actually do the work are:

  • getPath(), which takes the path to an alias as a String, and returns the path pointed to by that alias, as a String;
  • makeAlias(), which takes the path of the item to which the alias will point, and the path to the alias file to be created, and returns the path to the new alias file, all as Strings.

I explain them in the same order.

Resolving a Finder alias

func getPath(alias: String) -> String {
var thePath = ""

The first task is to turn the path to the alias into a URL
let aliasFileURL = URL.init(fileURLWithPath: alias)

The body of the code then runs inside a do … catch … structure, to handle throws
do {

This is the key function, which tries to create a URL by resolving the alias file
let theAliasURL = try URL.init(resolvingAliasFileAt: aliasFileURL)
thePath = theAliasURL.path

If resolution didn’t succeed, but doesn’t throw either, the path returned is the same as that to the URL alias provided, so use that as a test for silent failures
if (thePath == aliasFileURL.path) {
thePath = "-1"
fputs("Error: couldn't resolve alias.\n", stderr)
} } catch {

Handle errors
thePath = "-1"
fputs("Error: couldn't find alias file.\n", stderr)
}

And return the path from the alias resolution
return thePath
}

alisma01

Creating a Finder alias

func makeAlias(toPath: String, atPath: String) -> String {
var thePath = ""

First, turn both paths supplied into URLs
let theAliasFURL = URL.init(fileURLWithPath: atPath)
let theOrigURL = URL.init(fileURLWithPath: toPath)

Again, set the main body in a do … catch … structure to handle throws
do {

First, obtain the opaque Bookmark data required for the alias. This must be called with the creation option set to make them suitable for an alias, termed here a Bookmark file
let theData = try theOrigURL.bookmarkData(options: [URL.BookmarkCreationOptions.suitableForBookmarkFile], includingResourceValuesForKeys: nil, relativeTo: nil)

Then supply that data to be written to the output alias file. As this is a type (not class) method, call its using the URL type
try URL.writeBookmarkData(theData, to: theAliasFURL)

Finally get the path to the new alias file to return as the result
thePath = theAliasFURL.path
} catch {

Handle throws
thePath = "-1"
fputs("Error: couldn't create new alias.\n", stderr)
}

And return the path
return thePath
}

alisma02

Like so much of Swift in macOS, it’s actually very easy. When you don’t know how, you can be left blundering around hoping to stumble across an example. You have now.