CocoaDev

Edit AllPages

To move files to the trash and have the dock update the trash icon accordingly use the following NSWorkspace method:

NSArray *files; NSString *sourceDir; NSString *trashDir = [NSHomeDirectory() stringByAppendingPathComponent:@”.Trash”]; int tag;

[[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:sourceDir destination:trashDir files:files tag:&tag];

Hopefully you can fill in the blanks. –zootbobbalu

note: this method has major overhead associated with it if you are moving a bunch of files to the trash. I profiled an action that moved 400+ files and a large percentage of the spinning was caused by a multitude of mach messages being passed across system resources. My solution was to use NSFileManager to move all of the files I wished to delete into a temporary directory first and then only deleting the temporary directory. –zootbobbalu


Does this do the right thing if the file is on a different disk than the home directory? It doesn’t look like it, but I don’t know how smart this RecycleOperation thing is.


You are correct that it will not do the right thing if the files exists across volumes. If files are spread across volumes you will have to create a temporary directory for each volume. The extra note: works in cases where many files are being deleted from a single directory. If you are searching for files to delete across many different volumes, than you probably will have to take extra steps to insure that the finder can safely return deleted files to their original directory. –zootbobbalu


Could someone please give an example of how to query the Finder to see if a file is going to be deleted immediately or moved to the trash? I can’t find anything on Google. –drblue

Finder only deletes immediately if the file is on a remote volume, doesn’t it? So just check for that. NSWorkspace has a getFileSystemInfoForPath:isRemovable:isWritable:isUnmountable:description:type: that might work, or you could use Carbon.

How would you check for that with that function? On my local AFS mount, from which Finder deletes immediately, I get NOs from isRemovable and isUnmountable. I’m guessing that the Finder’s trash behaviour is quite a bit more complex–I remember something about a .Trashes// folder structure at the volume root, but I can't find any details on it.


Well since no one else posted some code I decided to take this task on, this category on NSFileManager does the appropriate behaviour.

// //  NSFileManagerAdditions.h //  TRKit //

#import <Cocoa/Cocoa.h> #import <Carbon/Carbon.h>

@interface NSFileManager (TRAdditions)

#import “NSFileManagerAdditions.h” #import “NSStringAdditions.h” #import “macbin3.h” #import #import #import #import #import #import #import #import <sys/param.h> #import <sys/mount.h> // is where the statfs struct is defined #import <sys/attr.h> #import <sys/vnode.h> #import <sys/stat.h>

static AuthorizationRef authorizationRef = NULL;

@implementation NSFileManager (TRAdditions)

@end // // NSStringAdditions.h // TRFoundation //

#import <Cocoa/Cocoa.h>

@interface NSString (TRAdditions)

#import “NSStringAdditions.h”

@implementation NSString (TRAdditions)

@end

Enjoy – DanSaul.


Dan, thanks for your code. It seems to work fine so far, except in one situation: if the home directory is not on the startup partition, I had to make the following change:

Replace the line:

useDiskTrash = [standardizedSource containsString:@”/Volumes/”];

with:

// the fix is as follows: we are on a remote volume if the source is on a remote volume AND that remote volume is not the home // this line is a mouthful: first, path standardization misses the aliased volume, then the path does not end with a slash so we have to add one NSString * home = [[[NSHomeDirectory() stringByResolvingSymlinksInPath] stringByStandardizingPath] stringByAppendingString:@”/”]; useDiskTrash = [standardizedSource beginsWith:@”/Volumes/”] && ![standardizedSource beginsWith:home];

and put beginsWith in your NSString additions:

// returns true if the receiver begins with prefix

– Daniel

Um, NSString already has a hasPrefix: method that does exactly the same thing. –JediKnil

I use this NSString category:

It does have a few flaws, like no way to tell if the file will actually go to trash, or be deleted permanently – and I’ve gotten one report that it fails for AFP mounted volumes.

The latter though is clearly a bug Apple should fix, and the former, maybe create a hybrid between this and the function above. I do consider this a bit more �clean�. It will also update the trash can icon in the dock, if the trash can goes from empty to non-empty.

The problem with that method is that it takes a lot of time with a lot of files, which is why I wrote that method.l

03/26/07: The NSWorkSpace performFileOperation methods are completely broken for what appears to be any remote volume. This problem is especially bad when you use OS X Server and your users have non-mobile networked Home Folders. These methods as well as NSFileManager methods fail to work. In general, any file/directory whose path starts with “/private” in the get info dialog will fail.

This is especially bad for Sparkle which means any app that is using that to update itself will fail because sparkle uses the above NSWorkspace method.

David B.


Any have any name to attribute the “finalized” code to? I may be using this in one of my projects, and wanted to have proper attributions.


Specifying “destination: nil” works for moving to the local trash. Could somebody test to see if it also works on remote volumes? It makes sense that if you are specifying an NSWorkspaceRecycleOperation then the system should be responsible for deciding where the files should go – even if it doesn’t say so in the docs – because otherwise what’s the difference between this and a plain NSWorkspaceMoveOperation? Having to calculate the path to the .Trash file yourself just seems like too much work. –SJC


Yes, SJC, Apple’s NSWorkspace docs do specify that some workspace file operations—like, perhaps, trashing operations—do not require any destination path, and to use The Empty String as the destination argument. I like this solution, (though I’m not sure how well it performs on networked drives,) as it doesn’t require any categories, uses straight-up Cocoa, and seems well-supported. –Taldar


I am the one who created the code that you see above, I thank you for the attribution, though it isn’t really required. :-) . – DanSaul


I’m sorry, perhaps I’m missing something, but I found many, many errors with your code Mr. DanSaul, many of them critical. Perhaps it was vandalized by someone, but anyway, here’s my version. I believe it properly checks to see if the dialogue needs to be shown, properly creates the dialogue, properly creates the destination path, etc. Essentially reproducing the behavior of the Finder. Please note that to get it to compile you must remove the log_* statements. Also, note that this version does not create the Trashes folder (and the UID subfolder). I’m not sure if this is the case on all versions of OS X but on 10.5 these folders are created automatically by the system (upon mounting the drives) with root permissions (something that you can’t do without going through Auth Services). Besides having the wrong permissions (it’s not 777) it’s not even therefore possible to properly create these folders without gaining root-privs, therefore this code does not attempt it. (I had tried to do this in fact [as a normal user] and it ended up messing up the ability for things to be trashed on the volume completely. Only after ejecting and remounting the drive did things work properly again. And in addition to root privs these folders have other special flags on them).

#import “NSFileManager+Trash.h”

#import

@implementation NSFileManager (Trash)

@end

Enjoy, it’s bloody insane that Apple forced me to implement this. – Greg


If you’re on Leopard then you don’t need to. There’s a new API, FSMoveObjectToTrashSync and FSMoveObjectToTrashAsync, just for moving objects to the trash. I presume it does the right thing, although I’m prepared to be surprised. – MikeAsh