Why does saving files into a NSDocument create versions of the same file with strange characters in their name?

StackOverflow https://stackoverflow.com//questions/22046035

Question

I’m working on an NSDocument-based application whose document type is a package bundle that contains a bunch of files. I’m saving it thus:

- (NSFileWrapper *)fileWrapperOfType:(NSString *)typeName error:(NSError **)outError
{
    if (!self.documentFileWrapper) {
        NSFileWrapper *documentFileWrapper = [[NSFileWrapper alloc] initDirectoryWithFileWrappers:nil];
        [self setDocumentFileWrapper:documentFileWrapper];
    }

    NSFileWrapper *defaultWrapper = [self.documentFileWrapper.fileWrappers objectForKey:@"default"];
    if (!defaultWrapper) {
        defaultWrapper = [[NSFileWrapper alloc] initDirectoryWithFileWrappers:nil];
        [defaultWrapper setPreferredFilename:@"someFile.ext"];
        [self.documentFileWrapper addFileWrapper:defaultWrapper];
    }

    [defaultWrapper addRegularFileWithContents: ... some computed content for the file ... preferredFilename:@"someFile.ext"];

    return self.documentFileWrapper;
}

In other words, into the bundle, create a folder “default”, and save “someFile.ext” into it with some contents.

Now, the problem. When I go look at what’s actually saved on disk, I see this:

screenshot

Every time I save the file, the NSDocument wrapper seems to create some versioned copy of the resource. I don’t want the versioned clones, I only want the vanilla file with the latest content.

Where do the versioned resources come from? What’s going on here? Is there a doc I should read?

Was it helpful?

Solution

Ok, I think I figured it out. As it turns out, it wasn't actually a versioning issue. It appears that you were simply adding a new file each time. First, a few notes.

  1. You appear not to be creating a file in a package, but a 'directory containing a file', in a package. Is that what you intended to do?
  2. It seems that if the directory "default" does not exist, you create and use a directory "someFile.ext", which means that next time it creates a new directory with a weird prefix. However, I see from your screenshot that you already have a directory "default", which means this isn't your problem.

Now, your actual problem: according to this documentation, addRegularFileWithContents creates a new file with your preferred filename, unless the directory (defaultWrapper) already contains a file with that name (basically). If there already is, it creates a new file with a prefix. addRegularFileWithContents returns an identifying string referring to the NSFileWrapper created (I think it's the filename).

So, what's happening is that each time you go to save the file, it sees that there's already a file of that name, and creates a new prefixed one. One way to solve this is to keep the NSString returned by addRegularFileWithContents, then when you go to save you check the defaultWrapper and remove the NSFileWrapper associated with the stored NSString, if there is one, and then add the file like normal. I've modified your code as follows:

@interface TestDocument () // Relevant part of my test class.  Note `wrapperFileName`.

@property (nonatomic, strong) NSFileWrapper *documentFileWrapper;
@property (nonatomic, strong) NSString *wrapperFileName;

@end



- (NSFileWrapper *)fileWrapperOfType:(NSString *)typeName error:(NSError **)outError
{
    if (!self.documentFileWrapper) {
        NSFileWrapper *documentFileWrapper = [[NSFileWrapper alloc] initDirectoryWithFileWrappers:nil];
        [self setDocumentFileWrapper:documentFileWrapper];
    }

    NSFileWrapper *defaultWrapper = [self.documentFileWrapper.fileWrappers objectForKey:@"default"];
    if (!defaultWrapper) {
        defaultWrapper = [[NSFileWrapper alloc] initDirectoryWithFileWrappers:nil];
        [defaultWrapper setPreferredFilename:@"default"];
        [self.documentFileWrapper addFileWrapper:defaultWrapper];
    } else if (self.wrapperFileName) {
        [defaultWrapper removeFileWrapper:[[defaultWrapper fileWrappers] objectForKey:self.wrapperFileName]];
    }

    self.wrapperFileName = [defaultWrapper addRegularFileWithContents:[@"blah blah" dataUsingEncoding:NSUTF8StringEncoding] preferredFilename:@"someFile.txt"];
    return self.documentFileWrapper;
}
  1. I've changed the defaultWrapper's preferred filename to be "default", as I suspect you intended.
  2. I've added an if block that removes the previously saved file, if one existed.
  3. I've had it store the new NSFileWrapper's filename, for use next time in #2.

After these changes, the program demonstrated what I think is the behavior you expect: one directory containing one file, even after saving multiple times. Note that this was true even when preservesVersions returned YES. I'm not sure where it stores its versions, but I didn't see them.

OTHER TIPS

You could implement -writeSafely… so that it calls straight through to -writeToURL:… Implement -writeToURL:… so that it calls -fileWrapperOfType:… and then writes that out to the URL atomically

i.e. you'd be taking over responsibility for atomic saves by making NSFileWrapper do it. That way the wrapper should neatly track where the files really end up.

Source

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top