Replacing a file on Mac is no longer reliable

Save a file. If it exists, delete the existing file and replace it with a new file.

This used to be easy, and it always worked.

  dim f as FolderItem = theFileRef
  if f <> Nil and f.exists then
      f.Delete
      f = theFileRef
  end if
  
  if f <> Nil then
      // write the file
  end if

Now, depending on the version of OSX, it either works (Lion, Mavericks), fails half the time (ML), or fails all the time (Yosemite).

Starting with ML I had to change the code to loop in order to delete the file.

  dim f as FolderItem = theFileRef
  if f <> Nil and f.exists then
      while f.exists
         f.Delete
      wend
      f = theFileRef
  end if

Now this doesn’t even work at all on Yosemite. The file never gets deleted. Funny thing is, the code does not get stuck in the while loop. It thinks that the file has been deleted and moves on. But, the file has not been deleted.

I can only guess this is because of Apple’s changes to the way the file system works (as in, it actually doesn’t work. It is now unbelievably slow and unreliable). Any ideas how to make it work again?

Thanks.

Swapping files this way has never been guaranteed to be atomic or reliable.
There are OS X API’s for doing this in a way that it either works or fails but doesn’t leave you in a state where the original has been deleted & you cant put the new one in place.
See FSExchangeObjects
Not sure if there are existing declares for it or not

ExchangeFilesMBS(first as folderitem, second as folderitem) as integer

in MBS Plugin does it.

      Declare Function FSExchangeObjects lib "InterfaceLib" (ref as Ptr, destRef as Ptr) as Integer
      
      dim srcFSRef as MemoryBlock = temporaryFile.MacFSRef
      dim destFSRef as MemoryBlock = originalFile.MacFSRef
      
      if srcFSRef is nil or destFSRef is nil then
        // return whatever indicate failure
      end if
      
      // Attempt to do the exchange
     dim err as Integer
    err = FSExchangeObjects( srcFSRef, destFSRef )
    if err = -50 then
          System.DebugLog "Error calling FSExchangeObjects(): Parameter Error."
          // return whatever indicates it failed
    elseif err <> 0 then
          // return whatever indicates it failed
    end if

    // return whatever indicates the exchange worked

Norman and Christian, thank you both so much!

I’m amazed - honestly, up until ML it used to work perfectly. I never had any issues with it until ML. Guess I just got lucky.

I’ll be replacing my code for sure …

Thanks again!

Excellent. I would also mark your response as the “answer” but the forum doesn’t let me mark more than one. MBS is an indispensable tool for me, recommend it highly.

FSExchangeObjects was depreciated in OS X 10.8, the replacement is NSFileManager’s replaceItemAtURL, which I suspect Christian has in his plugin - or can add it very quickly.

NSFileManager may already exist in MacOSLIb or MBS - that I didn’t look for
I had the FSExchangeObjects code handy (literally staring at it as this question came up)

[quote=180504:@Aaron Hunt]<…>
Now, depending on the version of OSX, it either works (Lion, Mavericks), fails half the time (ML), or fails all the time (Yosemite).
<…>[/quote]

I’m using such code to copy and overwrite an existing file, and I did not notice any problems. As far as I can see it reliably replaces an existing file.

With this function I am copying a file from the app’s resources folder to its application data folder. This is for a sandboxed app where it wouldn’t allow any file operation outside of the sandbox anyway, without user interaction.

[code]Function BinaryCopyFile(fSource As FolderItem, fTarget As FolderItem) As Boolean
Dim bs1 As BinaryStream
Dim bs2 As binaryStream

//make sure it exists before we try to read it
If fSource.exists Then

//open the folderitem as a binary file without write privelages
//     To open with write privelages, use true instead of false
bs1 = BinaryStream.Open(fSource, False)

//create a binary file 
bs2 = BinaryStream.Create(fTarget, True)  // True = overwrites existing file

//make sure we have a binary stream to read from
If bs1 <> Nil And bs2 <> Nil Then
  Try
    //read and write the whole binaryStream
    bs2.write(bs1.read(bs1.length))
    
    //close the binaryStream
    bs1.close
    bs2.close
    Return True
  Catch exc as IOException
    MsgBox "ERROR - failed to create the output file."
    Return False
  End Try
Else
  Return False
End If

Else
Return False
End If

End Function
[/code]

[quote=180611:@Norman Palardy]NSFileManager may already exist in MacOSLIb or MBS - that I didn’t look for
I had the FSExchangeObjects code handy (literally staring at it as this question came up)[/quote]
I realize that my response was a little rude, I’m sorry it wasn’t intended to be Norman.

With Yosemite, Apple collected on a lot of depreciations and also removed things that weren’t depreciated. This is the new Apple, constantly giveth and taketh (Apple’s Photos is no exception, where they’ve removed the ability to drag photos from their app into 3rd party applications).

So we really have to pay attention to any potential warning that Apple may give. Bearing in mind, that users don’t blame Apple, they blame us, the 3rd party devs who’s software no longer works.

I spend so much time chasing Apple, that I’m really getting tired of it and am starting to consider looking at t’other platform for my apps.