Im trying to figure out what the easiest way to delete files on a Mac is. Currently I am sending the file to specialFolder.Trash. However, if the folder has a file with the same name in it then it errors out, so I check the trash folder for the name and if it is there then I append a number to it and then send it to the trash. I can’t seem to get it to run reliably. I would like to be able to just remove the file out and not deal with the crazy way that Apple has the trash set up.
Anybody out there have a method for deleting files on a Mac that runs without problems? Just want to pass in the path and the file name and have it delete the file.
I am using it in an updater program. It downloads the new version of the program and then deletes the old program out. Thanks for any help you can offer.
Dim f As FolderItem
f = GetFolderItem("Project Templates")
f.Delete
If f.LastErrorCode > 0 Then
MsgBox(Str(f.LastErrorCode))
Else
MsgBox("File deleted!")
End If
Sub MoveFileToTrash(f as FolderItem)
dim r as FolderItem
call MacFileOperationMBS.MoveObjectToTrashSync(f, r, MacFileOperationMBS.kFSFileOperationDefaultOptions)
End Sub
[quote=387021:@Markus Winter]It would help if you post your code, otherwise it is pure guesswork where the problem might be.
My suspicion is that you dont check in a loop and increment the number as required, but as I said, pure guesswork.[/quote]
Sorry Markus, I don’t post on here too much. I didn’t think to post the code. Here it is. Thanks for taking a look.
[code]Public Function MoveFolderToTrash(fPathToFileToDelete as FolderItem, sFileToDelete as String) as String
Dim fPathForTheFileToDelete As FolderItem
Dim fPathForTheFileToTheTrash As FolderItem
Dim iModifier As Integer = 0
Dim sModifier As String = “”
Dim fTrashFolder As FolderItem = SpecialFolder.Trash
Dim sTrashFolder As String = fTrashFolder.AbsolutePath
Dim sError As String = “”
'--------------------------------------------------------------------------------
'Set fTheFolderOrFileToDelete to the passed in folder fPathToFoldeOrFileToDelete and the
'child sFolderOrFileToDelete file. Then check if it is nil or does not exist. If it is nil or does
'not exist then exit the method.
fPathForTheFileToDelete = GetFolderItem(fPathToFileToDelete.AbsolutePath).Child(sFileToDelete)
If fPathForTheFileToDelete = Nil Or Not fPathForTheFileToDelete.Exists Then Return “”
'----------------------------------------------------------------------------------
'For mac, because it will not send the file to the trash if it is a duplicate, it needs to
'have a modifier inserserted to the file name before it can be moved. #If TargetMacOS Then
Do
'set the path for the file to the trash
fPathForTheFileToTheTrash = GetFolderItem(sTrashFolder).Child(sModifier+sFileToDelete)
'check if it already exists in the trash.
If fPathForTheFileToTheTrash.Exists Then
'if it does exist then increment the modifier as get its string value
If iModifier < 100 Then
iModifier = iModifier + 1
sModifier = Str(iModifier)
'rename the file to the name with the new modifier
fPathForTheFileToDelete.Name = sModifier+sFileToDelete
Else
'msgbox fPathForTheFileToTheTrash.AbsolutePath
sError = "Error = 100 limit"
Exit Do
End If
Else ' the file does not exist in the trash.
Exit Do
End If
Loop
#EndIf
move the file To the trash
fPathForTheFileToDelete.MoveFileTo(fTrashFolder)
If fPathForTheFileToDelete.LastErrorCode <> 0 Then ’ error 5000
sError = GetFolderItemErrorCode(fPathForTheFileToDelete.LastErrorCode)
MsgBox sError
End If
[quote=387041:@Christian Schmitz]With MacFileOperation, you can move to trash:
Sub MoveFileToTrash(f as FolderItem)
dim r as FolderItem
call MacFileOperationMBS.MoveObjectToTrashSync(f, r, MacFileOperationMBS.kFSFileOperationDefaultOptions)
End Sub[/quote]
Thanks Christian for the suggestion. I’m trying to keep my software limited to what I have without using the plugins. If I absolutely need to, I will use MBS. Currently have a license because I use their curl plugin. I wasn’t aware of this method from mbs so If I can’t figure it out, Ill have some good code to fall back on.
How about letting Finder move it to the Trash (the way it always does, along with all the renaming-if-neccessary)?
Dim sh As New Shell
sh.execute "osascript -e 'tell application ""Finder"" to delete (POSIX file """ + TheFolderItemToBeMovedToTrash.NativePath + """ as alias)'"
Just be aware that doing it like this may cause the TheFolderItemToBeMovedToTrash instance to pick up the new location (in the Trash). It would then report “TheFolderItemToBeMovedToTrash.Exists = true” (in the new Trash location).
And AppleScript is a no-go if you think about bringing your app to the Mac App Store.
I may just revisit the folderItem.delete. The only reason for sending to the recycle bin is so there is a level of redundancy in the update process. If the update process fails on any one of the files its replacing, the user will be able to manually recover the only file. It’s just a safeguard that I can probably bypass. One problem that I had with FolderItem.delete was that I was getting random errors back saying something to the effect that I didn’t have permissions to delete a folder.
Here is my function to delete a folder (swiped from the Forum I think):
[code]Private Function DeleteEntireFolder(theFolder as FolderItem, continueIfErrors as Boolean = false) as Integer
// Returns an error code if it fails, or zero if the folder was deleted successfully
dim returnCode, lastErr, itemCount as integer
dim files(), dirs() as FolderItem
if theFolder = nil or not theFolder.Exists() then
return 0
end if
// Collect the folders contents first.
// This is faster than collecting them in reverse order and deleting them right away!
itemCount = theFolder.Count
for i as integer = 1 to itemCount
dim f as FolderItem
f = theFolder.TrueItem( i )
if f <> nil then
if f.Directory then
dirs.Append f
else
files.Append f
end if
end if
next
// Now delete the files
for each f as FolderItem in files
f.Delete
lastErr = f.LastErrorCode // Check if an error occurred
if lastErr <> 0 then
if continueIfErrors then
if returnCode = 0 then returnCode = lastErr
else
// Return the error code if any. This will cancel the deletion.
return lastErr
end if
end if
next
redim files(-1) // free the memory used by the files array before we enter recursion
// Now delete the directories
for each f as FolderItem in dirs
lastErr = DeleteEntireFolder( f, continueIfErrors )
if lastErr <> 0 then
if continueIfErrors then
if returnCode = 0 then returnCode = lastErr
else
// Return the error code if any. This will cancel the deletion.
return lastErr
end if
end if
next
if returnCode = 0 then
// Were done without error, so the folder should be empty and we can delete it.
theFolder.Delete
returnCode = theFolder.LastErrorCode
end if
return returnCode
exception exc
theException = new ErrorException(exc, currentMethodName)
End Function
[/code]
It works well, but it doesn’t move files to the trash. It is convenient if you need to recursively delete a populated directory and aren’t worried about it being irreversible.
[quote=387118:@Jürg Otter]How about letting Finder move it to the Trash (the way it always does, along with all the renaming-if-neccessary)?
Dim sh As New Shell
sh.execute "osascript -e 'tell application ""Finder"" to delete (POSIX file """ + TheFolderItemToBeMovedToTrash.NativePath + """ as alias)'"
Just be aware that doing it like this may cause the TheFolderItemToBeMovedToTrash instance to pick up the new location (in the Trash). It would then report “TheFolderItemToBeMovedToTrash.Exists = true” (in the new Trash location).
And AppleScript is a no-go if you think about bringing your app to the Mac App Store.[/quote]
The user may not have the Finder running at the time you invoke this script (like if (s)he has installed Path Finder or has quit the Finder to, say, free some RAM or any other reason). In such a case, the script will launch the Finder to do the task. The user will see the Finder launching (which (s)he probably doesn’t want to), wait for it to restore windows and then do the move to the trash; the user will then have to quit the Finder again.
Not a good practice in my opinion (and I recall seeing Apple saying the same thing for these reasons in a web page (perhaps in the interface guidelines?)).
That AppleScript way worked just fine at it’s time.
It still works. But requires quite some overhead (AppleEvents usage description, codesign entitlements), and is no longer a good user experience (“the .app wants to automate your system. allow? yes/no”).
Up until macOS 10.13 (or even 10.14.4/5) that’s been fine - for our needs.
Right - an edge case. As a user I might even be happy if Finder gets re-started
[code]//These Declares require macOS 10.8+ (Xojo 2017.03 builds for OS X 10.9+)
Declare Function trashItemAtURL Lib “Cocoa” selector “trashItemAtURL:resultingItemURL:error:” ( NSFileManagerInstance As Integer, inUrl As Integer, outUrl As Integer, outError As Integer ) As Boolean
Declare Function defaultManager Lib “Cocoa” selector “defaultManager” ( NSFileManagerClass As Integer ) As Integer
Declare Function NSClassFromString Lib “Cocoa” (className As CFStringRef) As Integer
Declare Function NSerrorlocalizedDescription Lib “Cocoa” selector “localizedDescription” (NSErrorInstance As Integer) As CFStringRef
Declare Function NSURLfileURLWithPathIsDirectory Lib “Cocoa” selector “fileURLWithPath:isDirectory:” (NSURLClass As Integer, path As CFStringRef, directory As Boolean) As Integer
Dim newLocation, NSError As Integer
Dim bSuccess As Boolean = trashItemAtURL( defaultManager( NSClassFromString( “NSFileManager” ) ), NSURLFileURLWithPathIsDirectory( NSClassFromString( “NSURL” ), poFolderitem.NativePath, poFolderitem.Directory ), newLocation, NSError )
If (bSuccess = False) Then
Dim sErrorMessage As String = NSErrorlocalizedDescription( NSError )
//cope with it
end if[/code]