1
votes

I have an OS X application that uses a subclass of NSPersistentDocument to store data. The Core Data persistent store type is SQLite. I am relying on the standard menu commands that get created when you create a new project for a document-based application with Core Data in Xcode.

This application exists for about two years now and is constantly evolving. Just today I discovered that "Save As" does not work in my newest version. The behavior is as follows:

  • I create a new document and enter some data.
  • I save the document, close it, then re-open it. Everything is fine. Then I select "Save As", choose a new name and location for the file. It gets renamed and I can enter some more data.
  • I save the document, close it, re-open it and it is completely empty.
  • When I open the original document, all the changes that should be in the saved-as document are there.
  • The Application does not crash or produce any errors.

I am pretty sure that it once worked correctly. I tried the very first "official" version of my program, and the behavior is the same (wrong).

Edit: I created a new Xcode project of the same type, with just one Core Data entity. The behavior is the same. The only difference to my application is that the new project automatically used the new "Duplicate" menu command that was introduced with 10.7 Lion instead of "Save As". So I have to press the Option key to select "Save As".

I only tested it on 10.9.3, but on two different Macs. Does anybody have an idea where to look at?

Edit 2: It appears to be related to specific accounts (my account on two machines). It works correctly on another account.

3

3 Answers

1
votes

We are seeing a similar problem but only for some users - we have compared the stack traces between two machines - on one it works and the other it doesn't and we can see there is a difference. On the working one the writeSafely method is using a temporary file, but on the non working version the temporary file is not used.

This difference seems to be happening in a private method of NSDocument that for some reason decides not to use a temporary folder.

We have found that if we override this private method and set the value of forceTemporaryFile to YES then the problem goes away.

We do not want to override this private method as we need to submit to the app store - but perhaps this works for you?

1
votes

For us the false behavior even comes up for binary-store too (we use sqlite in our application). Therefor I don't think the change mentioned in the DTS-answer is the same issue. (We currently build against the 10.8-SDK). Furthermore this happens only on some machines, luckily we now have access to a machine where it breaks.

For the undocumented method, overwriting

- (BOOL)_writeSafelyToURL:(NSURL*)url ofType:(NSString*)type forSaveOperation:(NSSaveOperationType)operation forceTemporaryDirectory:(BOOL)forceTemporary error:(NSError **)error
{
    return [super _writeSafelyToURL:url ofType:type forSaveOperation:operation forceTemporaryDirectory:YES error:error];
}

to always use YES for the forceTemporaryDirectory:-parameter solved the problem on the machine where the error happens. Not recommended for real use, this was only while trying to find the cause of the problem.

I posted a question to the cocoa-dev-mailing list too: http://lists.apple.com/archives/cocoa-dev/2014/Jun/msg00358.html maybe someone has some more information.

0
votes

Felix Franz got me on the right track with his posting on cocoa-dev mailing list: The problem is related to specific ACL settings. I added the following code to my NSPersistentDocument class to override writeToURL:ofType:forSaveOperation:originalContentsURL:error::

- (BOOL)writeToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName forSaveOperation:(NSSaveOperationType)saveOperation originalContentsURL:(NSURL *)absoluteOriginalContentsURL error:(NSError *__autoreleasing *)error {
    if ((saveOperation == NSSaveAsOperation)  && (absoluteOriginalContentsURL == nil)) {
        NSLog(@"---------- absoluteOriginalContentsURL == nil for NSSaveAsOperation ------------");
        return [super writeToURL:absoluteURL ofType:typeName forSaveOperation:saveOperation originalContentsURL:self.fileURL error:error];
    }
    return [super writeToURL:absoluteURL ofType:typeName forSaveOperation:saveOperation originalContentsURL:absoluteOriginalContentsURL error:error];
}

It is obvious that it provides self.fileURL to the super method when absoluteOriginalContentsURL is nil for some reason. Now "Save As" works for all accounts I tested.