Getting standard macOS directories
macOS applications are supposed to store certain kinds of data in specific folders. From Apple’s File System Programming Guide the guide says:
Put data cache files in the Library/Caches/ directory.
Put app-created support files in the Library/Application Support/ directory.
It’s possible to hardcode these paths in an application but macOS provides APIs to programmatically discover these directories. Using these APIs can ensure an application is storing data in the “right place” no matter what.
For non Objective-C/Swift applications there are two APIs available from macOS. This post will show how to use them with Rust, but it’s applicable for any application linking against the macOS SDK.
NSSearchPathForDirectoriesInDomains
The NSSearchPathForDirectoriesInDomains
is located in the Foundation
framework. Typically Foundation
contains Objective-C APIs but this function is unique. It’s declared in Foundation.framework/Versions/C/Headers/NSPathUtilities.h
The declaration is:
It’s a C function, but it returns Objective-C objects. Normally this would be very difficult to use without the Objective-C runtime, but macOS APIs have “toll free bridging” 1 between select Core Foundation and Foundation objects including NSArray
and NSString
. This means an application can simply cast the return type to a CFArray
of CFString
instead. So long as the caller deallocates the returned array, there should be no issues from this approach.
It’s important to note that the NSSearchPathDirectory
and NSSearchPathDomainMask
arguments are enums using NSUInteger
.
Using the core-foundation
crate a program to print out the Application Support Directory is below.
The above program prints out the following on my machine:
sysdir
The second option is the sysdir
API, it appears to be less documented and less used, but it’s located at /usr/include/sysdir.h
and comes with a manpage sysdir(3)
.
The declaration is:
sysdir_search_path_directory_t
and sysdir_search_path_domain_mask_t
are enums identical to the NSSearchPathForDirectoriesInDomains
API:
Using this API via FFI is awkward compared to the NSSearchPathForDirectoriesInDomains
API because the application has to manually iterate over the results.
This approach does not require any external crates or linking against any frameworks. The sysdir
API is located in libSystem
and is available to any application in macOS. The above program prints out the following on my machine:
It’s important to note that this API will always return ~
to represent the user’s home directory which might make the return value hard to use with other APIs.
Conclusion
There are two APIs available to get standard application directories on macOS for non Objective-C/Swift applications. sysdir
is available in libSystem
but does not expand ~
in user specific paths. Alternatively, NSSearchPathForDirectoriesInDomains
is available in Foundation.framework
and returns a CFArray
and can expand ~
in user specific paths.
See this Apple document for all of the details.↩︎