UIDocument, NSFileWrapper and Images
Asked Answered
N

1

7

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) {}];
}];
Nunuance answered 11/6, 2012 at 16:17 Comment(13)
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).Sydney
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!Sydney
@Sydney so you would suggest not to put the images in a fileWrapper? Or is there a way to save a photo to a wrapper once the user has chosen one? I don't really get how I can independently save an image to a fileWrapper that is managed by UIDocument...Nunuance
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.Sydney
@Sydney thanks a lot. Even if I have several UIDocuments, I could still have only one NSFileWrapper which comprises all data, right? I'd just manage the contents of this NSFileWrapper from different UIDocuments.Nunuance
As always, the answer is "it depends". Really it comes down to would you rather lazily save or have atomic writes?Sydney
@Sydney As you said earlier, I think the best is to store it to server/save locally after user has added a picture. I guess lazily save is no good as I need to make saving user's data highest priority. But generally it would be an acceptable design to have one NSFileWrapper which comprises "text.data" and several "photo-UUID1.data", "photo-UUID2.data" ... with the photos being managed by separate UIDocs and the text.data managed by another UIDoc?Nunuance
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.Sydney
@Sydney Thanks a lot, makes sense. Can you perhaps put this as an answer so that I can mark it as the solution to my problem?Nunuance
@Sydney also, as far as I understand the docs, I can use a single UIDocument to manage saving/loading of my NSFileWrapper Directory without any performance issues. So I would not need a different UIDocument for each image, I suppose.Nunuance
That seems to be true based on Apple' documentation.Sydney
@Sydney I've updated this question here: #11079989Nunuance
With APFS and all the smart things UIDocument does under the hood I think this is a premature optimisation. Don't worry too much about this unless you can clearly demonstrate performance problems.Yarkand
L
3

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.

Larisa answered 21/2, 2013 at 2:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.