I wasted at least an hour debugging what I incorrectly assumed was a Mac app sandboxing issue, because I believed what the NSFileManager error told me. Below is the source code of a command-line tool to demonstrate the issue.
#import <Foundation/Foundation.h>
#define SourcePath @"/Library/Receipts/InstallHistory.plist"
#define DestinationPath @"/Users/Shared/nonexistent/foobar.plist"
int main(int argc, const char * argv[]) {
@autoreleasepool {
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:SourcePath];
NSLog(@"exists? %i", exists);
NSError *error = nil;
if ([[NSFileManager defaultManager] copyItemAtPath:SourcePath toPath:DestinationPath error:&error]) {
NSLog(@"copied");
} else {
NSLog(@"%@\n\n%@", [error localizedDescription], error);
}
}
return 0;
}
Here's the output from running the command-line tool:
exists? 1
The file “InstallHistory.plist” doesn’t exist.
Error Domain=NSCocoaErrorDomain Code=4 "The file “InstallHistory.plist” doesn’t exist." UserInfo={NSSourceFilePathErrorKey=/Library/Receipts/InstallHistory.plist, NSUserStringVariant=(
Copy
), NSDestinationFilePath=/Users/Shared/nonexistent/foobar.plist, NSFilePath=/Library/Receipts/InstallHistory.plist, NSUnderlyingError=0x600000c00c00 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
The error says that the source file InstallHistory.plist doesn't exist, but the file does exist! The true reason for the copy failure is that the destination directory /Users/Shared/nonexistent/ doesn't exist. Sigh.
I tested my command-line tool all the way back to macOS 10.13 High Sierra, and the behavior is the same! This is an old bug in NSFileManager. And note that the bug is not restricted to path-based API: it also affects NSFileManager URL-based API.
Hopefully this blog post helps someone in the future. I'm not going to file a bug report with Apple, though, because I'm boycotting Feedback Assistant.