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 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
NSString. This means an application can simply cast the return type to a
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
NSSearchPathDomainMask arguments are enums using
core-foundation crate a program to print out the Application Support Directory is below.
The above program prints out the following on my machine:
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
The declaration is:
sysdir_search_path_domain_mask_t are enums identical to the
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.
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.↩︎