There's a similar question for iOS, but I found that the proprosed solutions do not work on macOS in all cases.
On a Mac, there are many possible Trash folders:
/.Trashes
~/.Trash
~/Library/Mobile Documents/com~apple~CloudDocs/.Trash
– this one is from iCloud/Users/xxx/.Trash
– any other user's trash/Volumes/xxx/.Trashes
This code should work but doesn't for the case of the iCloud trash:
NSURL *theURL = ...;
NSURLRelationship relationship = NSURLRelationshipOther;
NSError *error = nil;
[NSFileManager.defaultManager
getRelationship: &relationship
ofDirectory: NSTrashDirectory
inDomain: 0
toItemAtURL: theURL
error: &error];
BOOL insideTrash = !error && (relationship == NSURLRelationshipContains);
If the URL points to any iCloud folder (including the Trash folder shown above), I get this error:
Error Domain=NSCocoaErrorDomain Code=3328
"The requested operation couldn’t be completed because the feature is not supported."
Curiously, even the header file of "NSFileManager" in the 10.15 SDK suggests to use this same code:
/* trashItemAtURL:resultingItemURL:error: [...]
To easily discover if an item is in the Trash, you may use
[fileManager getRelationship:&result ofDirectory:NSTrashDirectory
inDomain:0 toItemAtURL:url error:&error]
&& result == NSURLRelationshipContains.
*/
There also seems to be an issue with trashItemAtURL:
on iCloud-synched folders.
So, how do I solve this? If the Finder can detect the iCloud trash, I should be, too.
(Note: The app I use for testing this is not even sandboxed)
The officially suggested method of using getRelationship:
also fails with an error if the url points to a symlink whose target doesn't exist.
So, basically, this function is quite broken (verified in 10.13.6, 10.15.7 and 11.0.1).
Here's code to demonstrate the bug, which I've filed with Apple under FB8890518:
#import <Foundation/Foundation.h>
static void testSymlink (NSString* symlinkName, NSString* symlinkTarget)
{
NSString *path = [[NSString stringWithFormat:@"~/.Trash/%@", symlinkName] stringByExpandingTildeInPath];
NSURL *url = [NSURL fileURLWithPath:path];
symlink (symlinkTarget.UTF8String, path.UTF8String);
NSLog(@"created symlink at <%@> pointing to <%@>", url.path, symlinkTarget);
NSURLRelationship relationship = -1;
NSError *error = nil;
[NSFileManager.defaultManager getRelationship:&relationship ofDirectory:NSTrashDirectory inDomain:0 toItemAtURL:url error:&error];
NSString *rel = @"undetermined";
if (relationship == 0) rel = @"NSURLRelationshipContains";
if (relationship == 1) rel = @"NSURLRelationshipSame";
if (relationship == 2) rel = @"NSURLRelationshipOther";
NSLog(@"result:\n relationship: %@\n error: %@", rel, error);
}
int main(int argc, const char * argv[])
{
@autoreleasepool {
testSymlink (@"validSymlink", @"/System");
testSymlink (@"brokenSymlink", @"/nonexisting_file");
}
return 0;
}
To view the location of the original file that a shortcut points to, right-click the shortcut and select "Open file location." Windows will open the folder and highlight the original file. You can see the folder path where the file is located in the top of the Windows Explorer window.
One or more Recovered Files folders may appear in your Trash after you restart your Mac. The recovered files are temporary files used by macOS apps. Usually temporary files are deleted by an app when it no longer needs them. If an app quits unexpectedly, it may not be able to delete the temporary files before it quits.
With the realization that [NSFileManager getRelationship:]
even fails for broken symlinks, I conclude that this is a bug in macOS that's been existing undetected for years.
I came up with the following work-around:
Use the getRelationship:
operation, then check the returned error first:
relationship == NSURLRelationshipContains
, and use that as my result.NSURL *theURL = ...;
NSURLRelationship relationship = NSURLRelationshipOther;
NSError *error = nil;
[NSFileManager.defaultManager
getRelationship: &relationship
ofDirectory: NSTrashDirectory
inDomain: 0
toItemAtURL: theURL
error: &error];
BOOL insideTrash = !error && (relationship == NSURLRelationshipContains)
|| error && (
[theURL.path containsString:@"/.Trash/"]
|| [theURL.path containsString:@"/.Trashes/"]
)
);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With