7
votes

I have an UIDocument which I'd like to be comprised of (1) a txt file and (2) several jpg images. I put the txt and all the jpgs into a NSFileWrapper.

When I load the UIDocument, I need the info in the txt file really quickly, so I load it first and ignore all the images until I actually need them.

While I know how to lazily load the images, I'm not sure how to "lazily" save the images (especially when using iCloud, I don't want files to be unnecessarily uploaded/download). Let's suppose I've loaded all the images and did not change them. Then I want to save the UIDocument, ignoring all the images (as they haven't changed) but want to save the text as it did change.

How would I achieve this? Is it even possible? Or is it automatically done? Or should I rather not put the images in my UIDocument and let each image be handled by a different UIDocument? It's all a bit confusing to me, I'm afraid.

Here's my code so far which will save all the images and text (no matter whether they were changed or not):


UIDocument

-(id)contentsForType:(NSString *)typeName error:(NSError *__autoreleasing *)outError {

        NSMutableDictionary *wrappers = [NSMutableDictionary dictionary];
// the following puts a wrapper into a dictionary of wrappers:
        [self encodeObject:self.text toWrappers:wrappers toFileName:@"text.data"];
        [self encodeObject:self.photos toWrappers:wrappers toFileName:@"photos.data"];
        NSFileWrapper *fileWrapper = [[NSFileWrapper alloc] initDirectoryWithFileWrappers:wrappers];

        return fileWrapper;

    }

When I want to save the UIDocument:

[self.doc saveToURL:self.doc.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:^(BOOL success) {
    [self.doc closeWithCompletionHandler:^(BOOL success) {}];
}];
1
My honest opinion (and while not with iCloud have done this from the file system on the device) have created a file where the text file includes the display text, plus text based meta-tags that tell me where to get the images. This way I can load lazily each file, or save lazily each file. NOW, what this doesnt do on the other hand is preserve the atomic write that you may wish to maintain (keeping your images aligned with the text file as saved on whatever device you are saving to). - trumpetlicks
A question might be, when the user is telling you to save their data, why would you want to "lazily" save any part of it? Why not at this point either store it to server (iCloud) or locally store it when iCloud is unavailable and push to server for syncronization when iCloud becomes available? Make the user's data saving the highest priority at that point! - trumpetlicks
Im essentially suggesting different UIDocuments, one is your text file, and the others are the image files. Within the text file, it tells you where to go and get the image files. - trumpetlicks
As always, the answer is "it depends". Really it comes down to would you rather lazily save or have atomic writes? - trumpetlicks
after even MORE reading on the subject, it seems as if the UIDocument actually DOES take into consideration atomic file operations (or as they put it "safe"). Also taken straight from Apple' website on NSFileWrapper - "File wrappers support incremental saving. In contrast with a single binary data object, if a file wrapper contains your document data—for example, text and an image—and the text changes, only the file containing the text has to be written out to disk. This capability results in better performance." It should support the reading and writing you are wanting. - trumpetlicks

1 Answers

3
votes

You should keep a reference to your NSFileWrapper in your UIDocument instance. This way, only the changed contents will be rewritten and not the whole wrapper.

So, keep a reference when you load a file (or create a new one for new documents) :

- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError {
    // save wrapper:
    self.fileWrapper = (NSFileWrapper*)contents;

now you only have to update the wrapper if your file actually changed:

- (id)contentsForType:(NSString *)typeName error:(NSError **)outError {
    NSFileWrapper *subwrapper = [self.fileWrapper.wrappers objectForKey:@"subwrapper"];
    if(self.somethingChanged) {
        [self.fileWrapper.wrappers removeFileWrapper:subwrapper];
        subwrapper = [[NSFileWrapper alloc] initRegularFileWithContents:…

I know the code is very brief, but I hope that helps to point you in the right direction.